1 /*
2 * Copyright (C) 2006 Andrej Kacian <andrej@kacian.sk>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20 #define __USE_GNU
21
22 #include <stdlib.h>
23 #include <glib.h>
24 #include <curl/curl.h>
25 #include <expat.h>
26
27 #include "feed.h"
28 #include "parser.h"
29
30 /* feed_new()
31 * Initializes new Feed struct, setting its url and a default timeout. */
feed_new(gchar * url)32 Feed *feed_new(gchar *url)
33 {
34 Feed *feed = NULL;
35
36 g_return_val_if_fail(url != NULL, NULL);
37
38 feed = malloc( sizeof(Feed) );
39 g_return_val_if_fail(feed != NULL, NULL);
40
41 feed->is_valid = TRUE;
42 feed->timeout = FEED_DEFAULT_TIMEOUT;
43 feed->url = g_strdup(url);
44 feed->auth = NULL;
45 feed->title = NULL;
46 feed->description = NULL;
47 feed->language = NULL;
48 feed->author = NULL;
49 feed->generator = NULL;
50 feed->link = NULL;
51 feed->items = NULL;
52
53 feed->fetcherr = NULL;
54 feed->cookies_path = NULL;
55
56 feed->ssl_verify_peer = TRUE;
57 feed->cacert_file = NULL;
58
59 return feed;
60 }
61
_free_items(gpointer item,gpointer nada)62 static void _free_items(gpointer item, gpointer nada)
63 {
64 feed_item_free(item);
65 }
66
_free_auth(Feed * feed)67 static void _free_auth(Feed *feed)
68 {
69 if (feed == NULL)
70 return;
71
72 if (feed->auth != NULL) {
73 if (feed->auth->username != NULL)
74 g_free(feed->auth->username);
75 if (feed->auth->password != NULL)
76 g_free(feed->auth->password);
77 g_free(feed->auth);
78 feed->auth = NULL;
79 }
80 }
81
feed_free(Feed * feed)82 void feed_free(Feed *feed)
83 {
84 if( feed == NULL )
85 return; /* Return silently, without printing a glib error. */
86
87 g_free(feed->url);
88 _free_auth(feed);
89 g_free(feed->title);
90 g_free(feed->description);
91 g_free(feed->language);
92 g_free(feed->author);
93 g_free(feed->generator);
94 g_free(feed->link);
95 g_free(feed->fetcherr);
96 g_free(feed->cookies_path);
97 g_free(feed->cacert_file);
98
99 if( feed->items != NULL ) {
100 g_slist_foreach(feed->items, _free_items, NULL);
101 g_slist_free(feed->items);
102 }
103
104 g_free(feed);
105 feed = NULL;
106 }
107
feed_free_items(Feed * feed)108 void feed_free_items(Feed *feed)
109 {
110 if( feed == NULL )
111 return;
112
113 if( feed->items != NULL ) {
114 g_slist_foreach(feed->items, _free_items, NULL);
115 g_slist_free(feed->items);
116 feed->items = NULL;
117 }
118 }
119
120 /* Timeout */
feed_set_timeout(Feed * feed,guint timeout)121 void feed_set_timeout(Feed *feed, guint timeout)
122 {
123 g_return_if_fail(feed != NULL);
124 feed->timeout = timeout;
125 }
126
feed_get_timeout(Feed * feed)127 guint feed_get_timeout(Feed *feed)
128 {
129 g_return_val_if_fail(feed != NULL, 0);
130 return feed->timeout;
131 }
132
133 /* URL */
feed_set_url(Feed * feed,gchar * url)134 void feed_set_url(Feed *feed, gchar *url)
135 {
136 g_return_if_fail(feed != NULL);
137 g_return_if_fail(url != NULL);
138
139 if( feed->url != NULL ) {
140 g_free(feed->url);
141 feed->url = NULL;
142 }
143
144 feed->url = g_strdup(url);
145 }
146
feed_get_url(Feed * feed)147 gchar *feed_get_url(Feed *feed)
148 {
149 g_return_val_if_fail(feed != NULL, NULL);
150 return feed->url;
151 }
152
153 /* Auth */
feed_set_auth(Feed * feed,FeedAuth * auth)154 void feed_set_auth(Feed *feed, FeedAuth *auth)
155 {
156 g_return_if_fail(feed != NULL);
157 g_return_if_fail(auth != NULL);
158
159 _free_auth(feed);
160 feed->auth = g_new0(FeedAuth, 1);
161 feed->auth->type = auth->type;
162 feed->auth->username = g_strdup(auth->username);
163 feed->auth->password = g_strdup(auth->password);
164 }
165
feed_get_auth(Feed * feed)166 FeedAuth *feed_get_auth(Feed *feed)
167 {
168 g_return_val_if_fail(feed != NULL, NULL);
169 return feed->auth;
170 }
171
172 /* Title */
feed_get_title(Feed * feed)173 gchar *feed_get_title(Feed *feed)
174 {
175 g_return_val_if_fail(feed != NULL, NULL);
176 return feed->title;
177 }
178
feed_set_title(Feed * feed,gchar * new_title)179 void feed_set_title(Feed *feed, gchar *new_title)
180 {
181 g_return_if_fail(feed != NULL);
182 g_return_if_fail(new_title != NULL);
183
184 if (feed->title != NULL) {
185 g_free(feed->title);
186 feed->title = NULL;
187 }
188
189 feed->title = g_strdup(new_title);
190 }
191
192 /* Description */
feed_get_description(Feed * feed)193 gchar *feed_get_description(Feed *feed)
194 {
195 g_return_val_if_fail(feed != NULL, NULL);
196 return feed->description;
197 }
198
199 /* Language */
feed_get_language(Feed * feed)200 gchar *feed_get_language(Feed *feed)
201 {
202 g_return_val_if_fail(feed != NULL, NULL);
203 return feed->language;
204 }
205
206 /* Author */
feed_get_author(Feed * feed)207 gchar *feed_get_author(Feed *feed)
208 {
209 g_return_val_if_fail(feed != NULL, NULL);
210 return feed->author;
211 }
212
213 /* Generator */
feed_get_generator(Feed * feed)214 gchar *feed_get_generator(Feed *feed)
215 {
216 g_return_val_if_fail(feed != NULL, NULL);
217 return feed->generator;
218 }
219
220 /* Fetch error (if not NULL, supplied by libcurl) */
feed_get_fetcherror(Feed * feed)221 gchar *feed_get_fetcherror(Feed *feed)
222 {
223 g_return_val_if_fail(feed != NULL, NULL);
224 return feed->fetcherr;
225 }
226
227 /* Returns number of items currently in the feed. */
feed_n_items(Feed * feed)228 gint feed_n_items(Feed *feed)
229 {
230 g_return_val_if_fail(feed != NULL, -1);
231
232 if( feed->items == NULL ) /* No items here. */
233 return 0;
234
235 return g_slist_length(feed->items);
236 }
237
238 /* Returns nth item from feed. */
feed_nth_item(Feed * feed,guint n)239 FeedItem *feed_nth_item(Feed *feed, guint n)
240 {
241 g_return_val_if_fail(feed != NULL, NULL);
242
243 return g_slist_nth_data(feed->items, n);
244 }
245
246 /* feed_update()
247 * Takes initialized feed with url set, fetches the feed from this url,
248 * updates rest of Feed struct members and returns HTTP response code
249 * we got from url's server. */
feed_update(Feed * feed,time_t last_update)250 guint feed_update(Feed *feed, time_t last_update)
251 {
252 CURL *eh = NULL;
253 CURLcode res;
254 FeedParserCtx *feed_ctx = NULL;
255 glong response_code = 0;
256
257 g_return_val_if_fail(feed != NULL, FEED_ERR_NOFEED);
258 g_return_val_if_fail(feed->url != NULL, FEED_ERR_NOURL);
259
260 /* Init curl before anything else. */
261 eh = curl_easy_init();
262
263 g_return_val_if_fail(eh != NULL, FEED_ERR_INIT);
264
265 /* Curl initialized, create parser context now. */
266 feed_ctx = malloc( sizeof(FeedParserCtx) );
267
268 feed_ctx->parser = XML_ParserCreate(NULL);
269 feed_ctx->depth = 0;
270 feed_ctx->str = NULL;
271 feed_ctx->xhtml_str = NULL;
272 feed_ctx->feed = feed;
273 feed_ctx->location = 0;
274 feed_ctx->curitem = NULL;
275 feed_ctx->id_is_permalink = TRUE;
276
277 feed_ctx->name = NULL;
278 feed_ctx->mail = NULL;
279
280 /* Set initial expat handlers, which will take care of choosing
281 * correct parser later. */
282 feed_parser_set_expat_handlers(feed_ctx);
283
284 curl_easy_setopt(eh, CURLOPT_URL, feed->url);
285 curl_easy_setopt(eh, CURLOPT_NOPROGRESS, 1);
286 #ifdef CURLOPT_MUTE
287 curl_easy_setopt(eh, CURLOPT_MUTE, 1);
288 #endif
289 curl_easy_setopt(eh, CURLOPT_WRITEFUNCTION, feed_writefunc);
290 curl_easy_setopt(eh, CURLOPT_WRITEDATA, feed_ctx);
291 curl_easy_setopt(eh, CURLOPT_FOLLOWLOCATION, 1);
292 curl_easy_setopt(eh, CURLOPT_MAXREDIRS, 3);
293 curl_easy_setopt(eh, CURLOPT_TIMEOUT, feed->timeout);
294 curl_easy_setopt(eh, CURLOPT_NOSIGNAL, 1);
295 curl_easy_setopt(eh, CURLOPT_ENCODING, "");
296 curl_easy_setopt(eh, CURLOPT_USERAGENT, "libfeed 0.1");
297 curl_easy_setopt(eh, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
298
299 /* Use HTTP's If-Modified-Since feature, if application provided
300 * the timestamp of last update. */
301 if( last_update != -1 ) {
302 curl_easy_setopt(eh, CURLOPT_TIMECONDITION,
303 CURL_TIMECOND_IFMODSINCE);
304 curl_easy_setopt(eh, CURLOPT_TIMEVALUE, (long)last_update);
305 }
306
307 #if LIBCURL_VERSION_NUM >= 0x070a00
308 if (feed->ssl_verify_peer == FALSE) {
309 curl_easy_setopt(eh, CURLOPT_SSL_VERIFYPEER, 0);
310 curl_easy_setopt(eh, CURLOPT_SSL_VERIFYHOST, 0);
311 }
312 #endif
313
314 if (feed->cacert_file != NULL)
315 curl_easy_setopt(eh, CURLOPT_CAINFO, feed->cacert_file);
316
317 if(feed->cookies_path != NULL)
318 curl_easy_setopt(eh, CURLOPT_COOKIEFILE, feed->cookies_path);
319
320 if (feed->auth != NULL) {
321 switch (feed->auth->type) {
322 case FEED_AUTH_NONE:
323 break;
324 case FEED_AUTH_BASIC:
325 curl_easy_setopt(eh, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
326 curl_easy_setopt(eh, CURLOPT_USERNAME,
327 feed->auth->username);
328 curl_easy_setopt(eh, CURLOPT_PASSWORD,
329 feed->auth->password);
330 break;
331 default:
332 response_code = FEED_ERR_UNAUTH; /* unknown auth */
333 goto cleanup;
334 }
335 }
336
337 res = curl_easy_perform(eh);
338 XML_Parse(feed_ctx->parser, "", 0, TRUE);
339
340 if( res != CURLE_OK ) {
341 feed->fetcherr = g_strdup(curl_easy_strerror(res));
342 response_code = FEED_ERR_FETCH;
343 } else {
344 curl_easy_getinfo(eh, CURLINFO_RESPONSE_CODE, &response_code);
345 }
346
347 cleanup:
348 curl_easy_cleanup(eh);
349
350 /* Cleanup, we should be done. */
351 XML_ParserFree(feed_ctx->parser);
352 g_free(feed_ctx->name);
353 g_free(feed_ctx->mail);
354 if (feed_ctx->str != NULL)
355 g_string_free(feed_ctx->str, TRUE);
356 if (feed_ctx->xhtml_str != NULL)
357 g_string_free(feed_ctx->xhtml_str, TRUE);
358 g_free(feed_ctx);
359
360 return response_code;
361 }
362
feed_foreach_item(Feed * feed,GFunc func,gpointer data)363 void feed_foreach_item(Feed *feed, GFunc func, gpointer data)
364 {
365 g_return_if_fail(feed != NULL);
366 g_return_if_fail(feed->items != NULL);
367
368 g_slist_foreach(feed->items, func, data);
369 }
370
feed_prepend_item(Feed * feed,FeedItem * item)371 gboolean feed_prepend_item(Feed *feed, FeedItem *item)
372 {
373 g_return_val_if_fail(feed != NULL, FALSE);
374 g_return_val_if_fail(item != NULL, FALSE);
375
376 feed->items = g_slist_prepend(feed->items, item);
377 return TRUE;
378 }
379
feed_append_item(Feed * feed,FeedItem * item)380 gboolean feed_append_item(Feed *feed, FeedItem *item)
381 {
382 g_return_val_if_fail(feed != NULL, FALSE);
383 g_return_val_if_fail(item != NULL, FALSE);
384
385 feed->items = g_slist_append(feed->items, item);
386 return TRUE;
387 }
388
feed_insert_item(Feed * feed,FeedItem * item,gint pos)389 gboolean feed_insert_item(Feed *feed, FeedItem *item, gint pos)
390 {
391 g_return_val_if_fail(feed != NULL, FALSE);
392 g_return_val_if_fail(item != NULL, FALSE);
393 g_return_val_if_fail(pos < 0, FALSE);
394
395 feed->items = g_slist_insert(feed->items, item, pos);
396 return TRUE;
397 }
398
feed_get_cookies_path(Feed * feed)399 gchar *feed_get_cookies_path(Feed *feed)
400 {
401 g_return_val_if_fail(feed != NULL, NULL);
402 return feed->cookies_path;
403 }
404
feed_set_cookies_path(Feed * feed,gchar * path)405 void feed_set_cookies_path(Feed *feed, gchar *path)
406 {
407 g_return_if_fail(feed != NULL);
408
409 if( feed->cookies_path != NULL ) {
410 g_free(feed->cookies_path);
411 feed->cookies_path = NULL;
412 }
413
414 feed->cookies_path = (path != NULL ? g_strdup(path) : NULL);
415 }
416
feed_get_ssl_verify_peer(Feed * feed)417 gboolean feed_get_ssl_verify_peer(Feed *feed)
418 {
419 g_return_val_if_fail(feed != NULL, FALSE);
420 return feed->ssl_verify_peer;
421 }
422
feed_set_ssl_verify_peer(Feed * feed,gboolean ssl_verify_peer)423 void feed_set_ssl_verify_peer(Feed *feed, gboolean ssl_verify_peer)
424 {
425 g_return_if_fail(feed != NULL);
426 feed->ssl_verify_peer = ssl_verify_peer;
427 }
428
feed_get_cacert_file(Feed * feed)429 gchar *feed_get_cacert_file(Feed *feed)
430 {
431 g_return_val_if_fail(feed != NULL, NULL);
432 return feed->cacert_file;
433 }
434
feed_set_cacert_file(Feed * feed,const gchar * path)435 void feed_set_cacert_file(Feed *feed, const gchar *path)
436 {
437 g_return_if_fail(feed != NULL);
438
439 if( feed->cacert_file != NULL ) {
440 g_free(feed->cacert_file);
441 feed->cacert_file = NULL;
442 }
443
444 feed->cacert_file = (path != NULL ? g_strdup(path) : NULL);
445 }
446