2 * Copyright (C) 2006 Andrej Kacian <andrej@kacian.sk>
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.
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.
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.
26 #include <glib/gi18n.h>
29 /* Claws Mail includes */
30 #include <common/claws.h>
31 #include <mainwindow.h>
32 #include <statusbar.h>
33 #include <alertpanel.h>
35 #include <prefs_common.h>
40 #include "libfeed/feed.h"
42 #include "rssyl_deleted.h"
43 #include "rssyl_feed.h"
44 #include "rssyl_parse_feed.h"
45 #include "rssyl_prefs.h"
46 #include "rssyl_update_comments.h"
48 /* rssyl_fetch_feed_thr() */
50 static void *rssyl_fetch_feed_thr(void *arg)
52 RFetchCtx *ctx = (RFetchCtx *)arg;
54 /* Fetch and parse the feed. */
55 ctx->response_code = feed_update(ctx->feed, -1);
57 /* Signal main thread that we're done here. */
63 /* rssyl_fetch_feed() */
64 void rssyl_fetch_feed(RFetchCtx *ctx, gboolean verbose)
70 g_return_if_fail(ctx != NULL);
73 if( pthread_create(&pt, PTHREAD_CREATE_JOINABLE, rssyl_fetch_feed_thr,
75 /* Bummer, couldn't create thread. Continue non-threaded. */
76 rssyl_fetch_feed_thr(ctx);
78 /* Thread created, let's wait until it finishes. */
79 debug_print("RSSyl: waiting for thread to finish (timeout: %ds)\n",
80 feed_get_timeout(ctx->feed));
81 while( !ctx->ready ) {
85 debug_print("RSSyl: thread finished\n");
86 pthread_join(pt, NULL);
89 debug_print("RSSyl: no pthreads available, running non-threaded fetch\n");
90 rssyl_fetch_feed_thr(ctx);
93 if( ctx->response_code == FEED_ERR_INIT ) {
94 debug_print("RSSyl: libfeed reports init error from libcurl\n");
95 ctx->error = g_strdup("Internal error");
96 } else if( ctx->response_code == FEED_ERR_FETCH ) {
97 debug_print("RSSyl: libfeed reports some other error from libcurl\n");
98 ctx->error = g_strdup(ctx->feed->fetcherr);
99 } else if( ctx->response_code == FEED_ERR_UNAUTH ) {
100 debug_print("RSSyl: URL authorization type is unknown\n");
101 ctx->error = g_strdup("Unknown value for URL authorization type");
102 } else if( ctx->response_code >= 400 && ctx->response_code < 500 ) {
103 switch( ctx->response_code ) {
105 ctx->error = g_strdup(_("401 (Authorisation required)"));
108 ctx->error = g_strdup(_("403 (Unauthorised)"));
111 ctx->error = g_strdup(_("404 (Not found)"));
114 ctx->error = g_strdup_printf(_("Error %d"), ctx->response_code);
119 /* Here we handle "imperfect" conditions. If verbose is TRUE, we also
120 * display error dialogs for user. We always log the error. */
121 if( ctx->error != NULL ) {
122 /* libcurl wasn't happy */
123 debug_print("RSSyl: Error: %s\n", ctx->error);
125 gchar *msg = g_markup_printf_escaped(
126 (const char *) C_("First parameter is URL, second is error text",
127 "Error fetching feed at\n<b>%s</b>:\n\n%s"),
128 feed_get_url(ctx->feed), ctx->error);
129 alertpanel_error("%s", msg);
133 log_error(LOG_PROTOCOL, RSSYL_LOG_ERROR_FETCH, ctx->feed->url, ctx->error);
135 ctx->success = FALSE;
137 if( ctx->feed == NULL ) {
139 gchar *msg = g_markup_printf_escaped(
140 (const char *) _("No valid feed found at\n<b>%s</b>"),
141 feed_get_url(ctx->feed));
142 alertpanel_error("%s", msg);
146 log_error(LOG_PROTOCOL, RSSYL_LOG_ERROR_NOFEED,
147 feed_get_url(ctx->feed));
149 ctx->success = FALSE;
150 } else if (feed_get_title(ctx->feed) == NULL) {
151 /* We shouldn't do this, since a title is mandatory. */
152 feed_set_title(ctx->feed, _("Untitled feed"));
153 log_print(LOG_PROTOCOL, _("Possibly invalid feed without title at %s.\n"),
154 feed_get_url(ctx->feed));
159 RFetchCtx *rssyl_prep_fetchctx_from_item(RFolderItem *ritem)
161 RFetchCtx *ctx = NULL;
163 g_return_val_if_fail(ritem != NULL, NULL);
165 ctx = g_new0(RFetchCtx, 1);
166 ctx->feed = feed_new(ritem->url);
171 feed_set_timeout(ctx->feed, prefs_common_get_prefs()->io_timeout_secs);
172 feed_set_cookies_path(ctx->feed, rssyl_prefs_get()->cookies_path);
173 feed_set_ssl_verify_peer(ctx->feed, ritem->ssl_verify_peer);
174 feed_set_auth(ctx->feed, ritem->auth);
176 if (!g_ascii_strncasecmp(ritem->url, "https", 5)) {
177 feed_set_cacert_file(ctx->feed, claws_ssl_get_cert_file());
178 debug_print("RSSyl: using cert file '%s'\n", feed_get_cacert_file(ctx->feed));
185 RFetchCtx *rssyl_prep_fetchctx_from_url(gchar *url)
187 RFetchCtx *ctx = NULL;
189 g_return_val_if_fail(url != NULL, NULL);
191 ctx = g_new0(RFetchCtx, 1);
192 ctx->feed = feed_new(url);
197 feed_set_timeout(ctx->feed, prefs_common_get_prefs()->io_timeout_secs);
198 feed_set_cookies_path(ctx->feed, rssyl_prefs_get()->cookies_path);
199 feed_set_ssl_verify_peer(ctx->feed, rssyl_prefs_get()->ssl_verify_peer);
201 if (!g_ascii_strncasecmp(url, "https", 5)) {
202 feed_set_cacert_file(ctx->feed, claws_ssl_get_cert_file());
203 debug_print("RSSyl: using cert file '%s'\n", feed_get_cacert_file(ctx->feed));
210 /* rssyl_update_feed() */
212 gboolean rssyl_update_feed(RFolderItem *ritem, gboolean verbose)
214 RFetchCtx *ctx = NULL;
215 MainWindow *mainwin = mainwindow_get_mainwindow();
217 gboolean success = FALSE;
219 g_return_val_if_fail(ritem != NULL, FALSE);
220 g_return_val_if_fail(ritem->url != NULL, FALSE);
222 debug_print("RSSyl: starting to update '%s' (%s)\n",
223 ritem->item.name, ritem->url);
225 log_print(LOG_PROTOCOL, RSSYL_LOG_UPDATING, ritem->url);
227 msg = g_strdup_printf(_("Updating feed '%s'..."), ritem->item.name);
228 STATUSBAR_PUSH(mainwin, msg);
233 /* Prepare context for fetching the feed file */
234 ctx = rssyl_prep_fetchctx_from_item(ritem);
235 g_return_val_if_fail(ctx != NULL, FALSE);
237 /* Fetch the feed file */
238 rssyl_fetch_feed(ctx, verbose);
240 debug_print("RSSyl: fetch done; success == %s\n",
241 ctx->success ? "TRUE" : "FALSE");
243 debug_print("RSSyl: STARTING TO PARSE FEED\n");
244 if( ctx->success && !(ctx->success = rssyl_parse_feed(ritem, ctx->feed)) ) {
245 /* both libcurl and libfeed were happy, but we weren't */
246 debug_print("RSSyl: Error processing feed\n");
248 gchar *msg = g_markup_printf_escaped(
249 (const char *) _("Couldn't process feed at\n<b>%s</b>\n\n"
250 "Please contact developers, this should not happen."),
251 feed_get_url(ctx->feed));
252 alertpanel_error("%s", msg);
256 log_error(LOG_PROTOCOL, RSSYL_LOG_ERROR_PROC, ctx->feed->url);
259 debug_print("RSSyl: FEED PARSED\n");
261 STATUSBAR_POP(mainwin);
263 if( claws_is_exiting() ) {
264 feed_free(ctx->feed);
270 if( ritem->fetch_comments )
271 rssyl_update_comments(ritem);
273 /* Prune our deleted items list of items which are no longer in
275 rssyl_deleted_expire(ritem, ctx->feed);
278 success = ctx->success;
279 feed_free(ctx->feed);
286 static gboolean rssyl_update_recursively_func(GNode *node, gpointer data)
291 g_return_val_if_fail(node->data != NULL, FALSE);
293 item = FOLDER_ITEM(node->data);
294 ritem = (RFolderItem *)item;
296 if( ritem->url != NULL ) {
297 debug_print("RSSyl: Updating feed '%s'\n", item->name);
298 rssyl_update_feed(ritem, FALSE);
300 debug_print("RSSyl: Updating in folder '%s'\n", item->name);
305 void rssyl_update_recursively(FolderItem *item)
307 g_return_if_fail(item != NULL);
308 g_return_if_fail(item->folder != NULL);
310 if( item->folder->klass != rssyl_folder_get_class() )
313 debug_print("Recursively updating '%s'\n", item->name);
315 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
316 rssyl_update_recursively_func, NULL);
319 void rssyl_update_all_func(FolderItem *item, gpointer data)
321 /* Only try to refresh our feed folders */
322 if( !IS_RSSYL_FOLDER_ITEM(item) )
325 if( folder_item_parent(item) == NULL )
326 rssyl_update_recursively(item);
329 void rssyl_update_all_feeds(void)
331 if (prefs_common_get_prefs()->work_offline &&
332 !inc_offline_should_override(TRUE,
333 _("Claws Mail needs network access in order to update your feeds.")) ) {
337 folder_func_to_all_folders((FolderItemFunc)rssyl_update_all_func, NULL);