Cleanup.
[jamendo] / src / http_utils.c
1 /*
2  * http_utils.c
3  *
4  *  Created on: 2009-12-18
5  *      Author: marcin
6  */
7
8 #include "http_utils.h"
9
10 #define HTTP_CACHE_PATH "/tmp/jamendo-cache"
11 #define MAX_CACHE_DAYS 2
12
13 static GInputStream* http_cache_get(const gchar* uri);
14
15 xmlDocPtr http_get_xml(const gchar* uri) {
16         GInputStream* stream = NULL;
17         GError *error = NULL;
18         gchar buf[1024];
19         gint ncount = 0;
20         xmlParserCtxtPtr ctxt = NULL;
21         xmlDocPtr doc = NULL;
22
23         g_debug("http_get_xml: uri=%s", uri);
24
25         /**
26          * I found out that, this line will also do parsing xml from HTTP, instead of this complicated function
27          *
28         xmlDocPtr doc = xmlReadFile(uri,NULL,0);
29         */
30
31         stream = http_cache_get(uri);
32
33         if (!stream) {
34                 g_warning("http_get_xml: ERROR");
35                 return NULL;
36         }
37
38         do {
39                 ncount = g_input_stream_read(G_INPUT_STREAM(stream), buf, G_N_ELEMENTS(buf), NULL, &error);
40                 if (error) {
41                         g_error("http_get_xml: %s", error->message);
42                         g_error_free(error);
43                         error = NULL;
44                 }
45                 if (ncount) {
46                         if (!ctxt) {
47                                 ctxt = xmlCreatePushParserCtxt(NULL, NULL, buf, ncount, uri);
48                         } else {
49                                 xmlParseChunk(ctxt, buf, ncount, 0);
50                         }
51                 }
52         } while (ncount != 0);
53
54         if (ctxt) {
55                 xmlParseChunk(ctxt, buf, 0, 1);
56                 doc = ctxt->myDoc;
57                 if (!ctxt->wellFormed) {
58                         g_warning("http_get_xml: wellFormed=%d", ctxt->wellFormed);
59                 }
60                 xmlFreeParserCtxt(ctxt);
61         }
62
63         g_input_stream_close(stream, NULL, NULL);
64
65         return doc;
66 }
67
68 GdkPixbuf* http_get_image(const gchar* uri, gint width, gint height) {
69         GError *error = NULL;
70         GdkPixbuf* image = NULL;
71
72         GInputStream* stream = http_cache_get(uri);
73         if (stream) {
74                 image = gdk_pixbuf_new_from_stream_at_scale(stream, width, height, TRUE, NULL, &error);
75                 g_input_stream_close(stream, NULL, NULL);
76         }
77
78         return image;
79 }
80
81 //TODO: add parameter to support cache expiration
82 static GInputStream* http_cache_get(const gchar* uri) {
83         gchar* tmp;
84         gchar* cache_path;
85         GFile* cache_file;
86         GFile* http_file;
87         GError* error = NULL;
88         GInputStream* input_stream;
89         GOutputStream* output_stream;
90
91         cache_file = g_file_new_for_path(HTTP_CACHE_PATH);
92         if (!g_file_query_exists(cache_file, NULL)) {
93                 g_file_make_directory_with_parents(cache_file, NULL, NULL);
94         }
95         g_object_unref(cache_file);
96
97         /** check if uri is in our cache **/
98         tmp = g_uri_escape_string(uri, NULL, FALSE);
99         cache_path = g_strdup_printf("%s/%s", HTTP_CACHE_PATH, tmp);
100         g_free(tmp);
101
102         cache_file = g_file_new_for_path(cache_path);
103         g_free(cache_path);
104
105         if (g_file_query_exists(cache_file, NULL)) {
106                 GFileInfo *info;
107                 GTimeVal mod, cur;
108                 GDate *modDate = g_date_new(), *curDate = g_date_new();
109
110                 info = g_file_query_info(cache_file, G_FILE_ATTRIBUTE_TIME_MODIFIED, 0, NULL, NULL);
111                 mod.tv_sec = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
112                 g_object_unref(info);
113                 g_date_set_time_val(modDate, &mod);
114
115                 g_get_current_time(&cur);
116                 g_date_set_time_val(curDate, &cur);
117
118                 gint days = g_date_days_between(modDate, curDate);
119
120                 if (days <= MAX_CACHE_DAYS) {
121                         g_debug("http_cache_get: cache age %d days - ok", days);
122                         input_stream = G_INPUT_STREAM(g_file_read(cache_file, NULL, &error));
123
124                         if (!error) {
125                                 g_object_unref(cache_file);
126                                 return input_stream;
127                         }
128
129                         g_warning("http_cache_get: cannot read cache file: %s", error->message);
130                         g_error_free(error);
131                         error = NULL;
132                 }
133                 else {
134                         g_debug("http_cache_get: cache age %d days - to old", days);
135                 }
136         }
137
138         /** not in cache, get it from http */
139         http_file = g_file_new_for_uri(uri);
140         input_stream = G_INPUT_STREAM(g_file_read(http_file, NULL, &error));
141         g_object_unref(http_file);
142         if (error) {
143                 g_warning("http_cache_get: cannot read from HTTP: %s", error->message);
144                 g_error_free(error);
145                 g_object_unref(cache_file);
146                 return NULL;
147         }
148
149         output_stream = G_OUTPUT_STREAM(g_file_replace(cache_file,NULL,0,G_FILE_CREATE_REPLACE_DESTINATION,NULL,&error));
150         if (error) {
151                 g_warning("http_cache_get: cannot create cache file: %s", error->message);
152                 g_error_free(error);
153                 g_object_unref(cache_file);
154                 return input_stream;
155         }
156
157         g_output_stream_splice(output_stream, input_stream, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE
158                         | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, NULL, &error);
159         if (error) {
160                 g_warning("http_cache_get: cannot write HTTP file to cache: %s", error->message);
161                 g_error_free(error);
162                 g_object_unref(cache_file);
163                 return NULL;
164         }
165
166         /** input and output streams are closed by g_output_stream_splice **/
167         input_stream = G_INPUT_STREAM(g_file_read(cache_file, NULL, &error));
168         g_object_unref(cache_file);
169         if (error) {
170                 g_warning("http_cache_get: cannot reopen cache file %s", error->message);
171                 g_error_free(error);
172                 return NULL;
173         }
174         return input_stream;
175 }