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>
28 /* Claws Mail includes */
35 #include "libfeed/feed.h"
36 #include "libfeed/feeditem.h"
37 #include "libfeed/date.h"
40 #include "rssyl_add_item.h"
41 #include "rssyl_deleted.h"
42 #include "rssyl_feed.h"
43 #include "rssyl_parse_feed.h"
44 #include "rssyl_prefs.h"
47 static void rssyl_foreach_parse_func(gpointer data, gpointer user_data)
49 FeedItem *feed_item = (FeedItem *)data;
50 RFolderItem *ritem = (RFolderItem *)user_data;
52 rssyl_add_item(ritem, feed_item);
55 struct _RSSylExpireItemsCtx {
61 typedef struct _RSSylExpireItemsCtx RSSylExpireItemsCtx;
63 static void expire_items_func(gpointer data, gpointer user_data)
65 RSSylExpireItemsCtx *ctx = (RSSylExpireItemsCtx *)user_data;
66 FeedItem *item = (FeedItem *)data;
67 gchar *id = NULL, *id2 = NULL;
69 if( (id = feed_item_get_id(item)) == NULL )
70 id = feed_item_get_url(item);
75 if( (id2 = feed_item_get_id(ctx->item)) == NULL )
76 id2 = feed_item_get_url(ctx->item);
81 /* Simply check ID, as we should have up-to-date items right now. */
82 if( !strcmp(id, id2) )
86 static void rssyl_expire_items(RFolderItem *ritem, Feed *feed)
88 FeedItem *item = NULL;
90 RSSylExpireItemsCtx *ctx = NULL;
93 debug_print("RSSyl: rssyl_expire_items()\n");
95 g_return_if_fail(ritem != NULL);
96 g_return_if_fail(ritem->items != NULL);
97 g_return_if_fail(feed != NULL);
99 ctx = malloc( sizeof(RSSylExpireItemsCtx) );
100 ctx->expired_ids = NULL;
102 /* Check each locally stored item, if it is still in the upstream
103 * feed - xnay it if not. */
104 for( i = ritem->items; i != NULL; i = i->next ) {
105 item = (FeedItem *)i->data;
107 /* Comments will be expired later, once we know which parent items
108 * have been expired. */
109 if (feed_item_get_parent_id(item) != NULL)
112 /* Find matching item in the fresh feed. */
115 feed_foreach_item(feed, expire_items_func, ctx);
118 /* No match, add item ids to the list and get rid of it. */
119 debug_print("RSSyl: expiring '%s'\n", feed_item_get_id(item));
120 ctx->expired_ids = g_slist_prepend(ctx->expired_ids,
121 g_strdup(feed_item_get_id(item)));
122 fctx = (RFeedCtx *)item->data;
123 g_remove(fctx->path);
127 /* Now do one more pass over folder contents, and expire comments
128 * whose parents are gone. */
129 for( i = ritem->items; i != NULL; i = i->next ) {
130 item = (FeedItem *)i->data;
132 /* Handle comments expiration. */
133 if (feed_item_get_parent_id(item) != NULL) {
134 /* If its parent's id is on list of expired ids, this comment
136 if (g_slist_find_custom(ctx->expired_ids,
137 feed_item_get_parent_id(item), (GCompareFunc)g_strcmp0)) {
138 debug_print("RSSyl: expiring comment '%s'\n", feed_item_get_id(item));
139 fctx = (RFeedCtx *)item->data;
140 g_remove(fctx->path);
145 debug_print("RSSyl: expired %d items\n", g_slist_length(ctx->expired_ids));
147 g_slist_free_full(ctx->expired_ids, g_free);
151 /* -------------------------------------------------------------------------
152 * rssyl_parse_feed() */
154 gboolean rssyl_parse_feed(RFolderItem *ritem, Feed *feed)
156 gchar *tmp = NULL, *tmp2 = NULL;
159 g_return_val_if_fail(ritem != NULL, FALSE);
160 g_return_val_if_fail(feed != NULL, FALSE);
161 g_return_val_if_fail(feed->title != NULL, FALSE);
163 debug_print("RSSyl: parse_feed\n");
165 /* Set the last_update timestamp here, so it is the same for all items */
166 ritem->last_update = time(NULL);
168 /* If the upstream feed changed its title, change name of our folder
169 * accordingly even if user has renamed it before. This makes sure that
170 * user will be aware of the upstream title change. */
171 if( !ritem->ignore_title_rename &&
172 (ritem->official_title == NULL ||
173 strcmp(feed->title, ritem->official_title)) ) {
174 g_free(ritem->official_title);
175 ritem->official_title = g_strdup(feed->title);
177 tmp = rssyl_format_string(feed->title, TRUE, TRUE);
179 tmp2 = g_strdup(tmp);
180 while (folder_item_rename(&ritem->item, tmp2) != 0 && i < 20) {
182 tmp2 = g_strdup_printf("%s__%d", tmp, ++i);
183 debug_print("RSSyl: couldn't rename, trying '%s'\n", tmp2);
185 /* TODO: handle case when i reaches 20 */
190 /* FIXME: update name in properties */
191 /* FIXME: store feed properties */
194 folder_item_update_freeze();
196 /* Read contents of folder, so we can check for duplicates/updates */
197 rssyl_folder_read_existing(ritem);
199 if( claws_is_exiting() ) {
200 debug_print("RSSyl: Claws-Mail is exiting, bailing out\n");
201 log_print(LOG_PROTOCOL, RSSYL_LOG_ABORTED_EXITING, ritem->url);
202 folder_item_update_thaw();
206 /* Populate the ->deleted_items list so that we can check it when
207 * adding each item. */
208 ritem->deleted_items = rssyl_deleted_update(ritem);
210 /* Parse each item in the feed, adding or updating existing items if
212 if( feed_n_items(feed) > 0 )
213 feed_foreach_item(feed, rssyl_foreach_parse_func, (gpointer)ritem);
215 if( !ritem->keep_old && !ritem->fetching_comments ) {
216 rssyl_folder_read_existing(ritem);
217 rssyl_expire_items(ritem, feed);
220 rssyl_deleted_free(ritem->deleted_items);
222 folder_item_scan(&ritem->item);
223 folder_item_update_thaw();
225 if( !ritem->fetching_comments )
226 log_print(LOG_PROTOCOL, RSSYL_LOG_UPDATED, ritem->url);