New RSSyl replacing old one.
[claws.git] / src / plugins / rssyl / libfeed / feed.c
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. */
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->timeout = FEED_DEFAULT_TIMEOUT;
42         feed->url = g_strdup(url);
43         feed->title = NULL;
44         feed->description = NULL;
45         feed->language = NULL;
46         feed->author = NULL;
47         feed->generator = NULL;
48         feed->items = NULL;
49
50         feed->fetcherr = NULL;
51         feed->cookies_path = NULL;
52
53         return feed;
54 }
55
56 static void _free_items(gpointer item, gpointer nada)
57 {
58         feed_item_free(item);
59 }
60
61 void feed_free(Feed *feed)
62 {
63         if( feed == NULL )
64                 return; /* Return silently, without printing a glib error. */
65
66         g_free(feed->url);
67         g_free(feed->title);
68         g_free(feed->description);
69         g_free(feed->language);
70         g_free(feed->author);
71         g_free(feed->generator);
72         g_free(feed->fetcherr);
73         g_free(feed->cookies_path);
74
75         if( feed->items != NULL ) {
76                 g_slist_foreach(feed->items, _free_items, NULL);
77                 g_slist_free(feed->items);
78         }
79
80         g_free(feed);
81         feed = NULL;
82 }
83
84 void feed_free_items(Feed *feed)
85 {
86         if( feed == NULL )
87                 return;
88
89         if( feed->items != NULL ) {
90                 g_slist_foreach(feed->items, _free_items, NULL);
91                 g_slist_free(feed->items);
92                 feed->items = NULL;
93         }
94 }
95
96 /* Timeout */
97 void feed_set_timeout(Feed *feed, guint timeout)
98 {
99         g_return_if_fail(feed != NULL);
100         feed->timeout = timeout;
101 }
102
103 guint feed_get_timeout(Feed *feed)
104 {
105         g_return_val_if_fail(feed != NULL, 0);
106         return feed->timeout;
107 }
108
109 /* URL */
110 void feed_set_url(Feed *feed, gchar *url)
111 {
112         g_return_if_fail(feed != NULL);
113         g_return_if_fail(url != NULL);
114
115         if( feed->url != NULL ) {
116                 g_free(feed->url);
117                 feed->url = NULL;
118         }
119
120         feed->url = g_strdup(url);
121 }
122
123 gchar *feed_get_url(Feed *feed)
124 {
125         g_return_val_if_fail(feed != NULL, NULL);
126         return feed->url;
127 }
128
129 /* Title */
130 gchar *feed_get_title(Feed *feed)
131 {
132         g_return_val_if_fail(feed != NULL, NULL);
133         return feed->title;
134 }
135
136 void feed_set_title(Feed *feed, gchar *new_title)
137 {
138         g_return_if_fail(feed != NULL);
139         g_return_if_fail(new_title != NULL);
140
141         if (feed->title != NULL) {
142                 g_free(feed->title);
143                 feed->title = NULL;
144         }
145
146         feed->title = g_strdup(new_title);
147 }
148
149 /* Description */
150 gchar *feed_get_description(Feed *feed)
151 {
152         g_return_val_if_fail(feed != NULL, NULL);
153         return feed->description;
154 }
155
156 /* Language */
157 gchar *feed_get_language(Feed *feed)
158 {
159         g_return_val_if_fail(feed != NULL, NULL);
160         return feed->language;
161 }
162
163 /* Author */
164 gchar *feed_get_author(Feed *feed)
165 {
166         g_return_val_if_fail(feed != NULL, NULL);
167         return feed->author;
168 }
169
170 /* Generator */
171 gchar *feed_get_generator(Feed *feed)
172 {
173         g_return_val_if_fail(feed != NULL, NULL);
174         return feed->generator;
175 }
176
177 /* Fetch error (if not NULL, supplied by libcurl) */
178 gchar *feed_get_fetcherror(Feed *feed)
179 {
180         g_return_val_if_fail(feed != NULL, NULL);
181         return feed->fetcherr;
182 }
183
184 /* Returns number of items currently in the feed. */
185 gint feed_n_items(Feed *feed)
186 {
187         g_return_val_if_fail(feed != NULL, -1);
188
189         if( feed->items == NULL )       /* No items here. */
190                 return 0;
191
192         return g_slist_length(feed->items);
193 }
194
195 /* Returns nth item from feed. */
196 FeedItem *feed_nth_item(Feed *feed, guint n)
197 {
198         g_return_val_if_fail(feed != NULL, NULL);
199
200         return g_slist_nth_data(feed->items, n);
201 }
202
203 /* feed_update()
204  * Takes initialized feed with url set, fetches the feed from this url,
205  * updates rest of Feed struct members and returns HTTP response code
206  * we got from url's server. */
207 guint feed_update(Feed *feed, time_t last_update)
208 {
209         CURL *eh = NULL;
210         CURLcode res;
211         FeedParserCtx *feed_ctx = NULL;
212         glong response_code = 0;
213
214         g_return_val_if_fail(feed != NULL, FEED_ERR_NOFEED);
215         g_return_val_if_fail(feed->url != NULL, FEED_ERR_NOURL);
216
217         /* Init curl before anything else. */
218         eh = curl_easy_init();
219
220         g_return_val_if_fail(eh != NULL, FEED_ERR_INIT);
221
222         /* Curl initialized, create parser context now. */
223         feed_ctx = malloc( sizeof(FeedParserCtx) );
224
225         feed_ctx->parser = XML_ParserCreate(NULL);
226         feed_ctx->depth = 0;
227         feed_ctx->str = NULL;
228         feed_ctx->feed = feed;
229         feed_ctx->location = 0;
230         feed_ctx->curitem = NULL;
231         feed_ctx->id_is_permalink = TRUE;
232
233         feed_ctx->name = NULL;
234         feed_ctx->mail = NULL;
235
236         /* Set initial expat handlers, which will take care of choosing
237          * correct parser later. */
238         feed_parser_set_expat_handlers(feed_ctx);
239
240         curl_easy_setopt(eh, CURLOPT_URL, feed->url);
241         curl_easy_setopt(eh, CURLOPT_NOPROGRESS, 1);
242 #ifdef CURLOPT_MUTE
243         curl_easy_setopt(eh, CURLOPT_MUTE, 1);
244 #endif
245         curl_easy_setopt(eh, CURLOPT_WRITEFUNCTION, feed_writefunc);
246         curl_easy_setopt(eh, CURLOPT_WRITEDATA, feed_ctx);
247         curl_easy_setopt(eh, CURLOPT_FOLLOWLOCATION, 1);
248         curl_easy_setopt(eh, CURLOPT_MAXREDIRS, 3);
249         curl_easy_setopt(eh, CURLOPT_TIMEOUT, feed->timeout);
250         curl_easy_setopt(eh, CURLOPT_NOSIGNAL, 1);
251         curl_easy_setopt(eh, CURLOPT_ENCODING, "");
252         curl_easy_setopt(eh, CURLOPT_USERAGENT, "libfeed 0.1");
253
254         /* Use HTTP's If-Modified-Since feature, if application provided
255          * the timestamp of last update. */
256         if( last_update != -1 ) {
257                 curl_easy_setopt(eh, CURLOPT_TIMECONDITION,
258                                 CURL_TIMECOND_IFMODSINCE);
259                 curl_easy_setopt(eh, CURLOPT_TIMEVALUE, last_update);
260         }
261
262 #if LIBCURL_VERSION_NUM >= 0x070a00
263         if (feed->ssl_verify_peer == FALSE) {
264                 curl_easy_setopt(eh, CURLOPT_SSL_VERIFYPEER, 0);
265                 curl_easy_setopt(eh, CURLOPT_SSL_VERIFYHOST, 0);
266         }
267 #endif
268
269         if(feed->cookies_path != NULL)
270                 curl_easy_setopt(eh, CURLOPT_COOKIEFILE, feed->cookies_path);
271
272         res = curl_easy_perform(eh);
273         XML_Parse(feed_ctx->parser, "", 0, TRUE);
274
275         if( res != CURLE_OK ) {
276                 feed->fetcherr = g_strdup(curl_easy_strerror(res));
277                 response_code = FEED_ERR_FETCH;
278         } else
279                 curl_easy_getinfo(eh, CURLINFO_RESPONSE_CODE, &response_code);
280
281         curl_easy_cleanup(eh);
282
283         /* Cleanup, we should be done. */
284         XML_ParserFree(feed_ctx->parser);
285         g_free(feed_ctx->name);
286         g_free(feed_ctx->mail);
287         g_free(feed_ctx);
288
289         return response_code;
290 }
291
292 void feed_foreach_item(Feed *feed, GFunc func, gpointer data)
293 {
294         g_return_if_fail(feed != NULL);
295         g_return_if_fail(feed->items != NULL);
296
297         g_slist_foreach(feed->items, func, data);
298 }
299
300 gboolean feed_prepend_item(Feed *feed, FeedItem *item)
301 {
302         g_return_val_if_fail(feed != NULL, FALSE);
303         g_return_val_if_fail(item != NULL, FALSE);
304
305         feed->items = g_slist_prepend(feed->items, item);
306         return TRUE;
307 }
308
309 gboolean feed_append_item(Feed *feed, FeedItem *item)
310 {
311         g_return_val_if_fail(feed != NULL, FALSE);
312         g_return_val_if_fail(item != NULL, FALSE);
313
314         feed->items = g_slist_append(feed->items, item);
315         return TRUE;
316 }
317
318 gboolean feed_insert_item(Feed *feed, FeedItem *item, gint pos)
319 {
320         g_return_val_if_fail(feed != NULL, FALSE);
321         g_return_val_if_fail(item != NULL, FALSE);
322         g_return_val_if_fail(pos < 0, FALSE);
323
324         feed->items = g_slist_insert(feed->items, item, pos);
325         return TRUE;
326 }
327
328 gchar *feed_get_cookies_path(Feed *feed)
329 {
330         g_return_val_if_fail(feed != NULL, NULL);
331         return feed->cookies_path;
332 }
333
334 void feed_set_cookies_path(Feed *feed, gchar *path)
335 {
336         g_return_if_fail(feed != NULL);
337
338         if( feed->cookies_path != NULL ) {
339                 g_free(feed->cookies_path);
340                 feed->cookies_path = NULL;
341         }
342
343         feed->cookies_path = (path != NULL ? g_strdup(path) : NULL);
344 }
345
346 gboolean feed_get_ssl_verify_peer(Feed *feed)
347 {
348         g_return_val_if_fail(feed != NULL, FALSE);
349         return feed->ssl_verify_peer;
350 }
351
352 void feed_set_ssl_verify_peer(Feed *feed, gboolean ssl_verify_peer)
353 {
354         g_return_if_fail(feed != NULL);
355         feed->ssl_verify_peer = ssl_verify_peer;
356 }