2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2004 Hiroyuki Yamamoto
4 * This file (C) 2005 Andrej Kacian <andrej@kacian.sk>
6 * - handling of info about user-deleted feed items
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 /* Claws Mail includes */
33 #include <common/utils.h>
34 #include <file-utils.h>
41 static RDeletedItem *_new_deleted_item()
43 RDeletedItem *ditem = g_new0(RDeletedItem, 1);
47 ditem->date_published = -1;
52 static void _free_deleted_item(gpointer d, gpointer user_data)
54 RDeletedItem *ditem = (RDeletedItem *)d;
64 void rssyl_deleted_free(GSList *deleted_items)
66 if (deleted_items != NULL) {
67 debug_print("RSSyl: releasing list of deleted items\n");
68 g_slist_foreach(deleted_items, _free_deleted_item, NULL);
69 g_slist_free(deleted_items);
74 static gchar * _deleted_file_path(RFolderItem *ritem)
76 gchar *itempath, *deleted_file;
78 itempath = folder_item_get_path(&ritem->item);
79 deleted_file = g_strconcat(itempath, G_DIR_SEPARATOR_S, RSSYL_DELETED_FILE, NULL);
85 /***************************************************************/
86 GSList *rssyl_deleted_update(RFolderItem *ritem)
88 gchar *deleted_file, *contents, **lines, **line;
91 RDeletedItem *ditem = NULL;
92 GSList *deleted_items = NULL;
94 g_return_val_if_fail(ritem != NULL, NULL);
96 deleted_file = _deleted_file_path(ritem);
98 debug_print("RSSyl: getting list of deleted items from '%s'\n", deleted_file);
100 if (!g_file_test(deleted_file, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
101 debug_print("RSSyl: '%s' doesn't exist, ignoring\n", deleted_file);
102 g_free(deleted_file);
106 g_file_get_contents(deleted_file, &contents, NULL, &error);
109 g_warning("GError: '%s'", error->message);
113 if (contents != NULL) {
114 lines = strsplit_no_copy(contents, '\n');
116 g_warning("Couldn't read '%s', ignoring", deleted_file);
117 g_free(deleted_file);
121 g_free(deleted_file);
124 line = g_strsplit(lines[i], ": ", 2);
125 if (line[0] && line[1] && strlen(line[0]) && strlen(line[1])) {
126 if (!strcmp(line[0], "ID")) {
127 ditem = _new_deleted_item();
128 ditem->id = g_strdup(line[1]);
129 } else if (ditem != NULL && !strcmp(line[0], "TITLE")) {
130 ditem->title = g_strdup(line[1]);
131 } else if (ditem != NULL && !strcmp(line[0], "DPUB")) {
132 ditem->date_published = atoi(line[1]);
133 deleted_items = g_slist_prepend(deleted_items, ditem);
145 debug_print("RSSyl: got %d deleted items\n", g_slist_length(deleted_items));
146 return deleted_items;
149 static void _store_one_deleted_item(gpointer data, gpointer user_data)
151 RDeletedItem *ditem = (RDeletedItem *)data;
152 FILE *f = (FILE *)user_data;
153 gboolean err = FALSE;
155 if (ditem == NULL || ditem->id == NULL)
162 ditem->id, ditem->title,
163 (long long)ditem->date_published) < 0);
166 debug_print("RSSyl: Error during writing deletion file.\n");
169 static void rssyl_deleted_store_internal(GSList *deleted_items, const gchar *deleted_file)
173 if (g_file_test(deleted_file, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
174 if (g_remove(deleted_file) != 0) {
175 debug_print("RSSyl: Oops, couldn't delete '%s', bailing out\n",
181 if (g_slist_length(deleted_items) == 0)
184 if ((f = claws_fopen(deleted_file, "w")) == NULL) {
185 debug_print("RSSyl: Couldn't open '%s', bailing out.\n", deleted_file);
189 g_slist_foreach(deleted_items, (GFunc)_store_one_deleted_item,
192 claws_safe_fclose(f);
193 debug_print("RSSyl: written and closed deletion file\n");
196 void rssyl_deleted_store(RFolderItem *ritem)
200 g_return_if_fail(ritem != NULL);
202 path = _deleted_file_path(ritem);
203 rssyl_deleted_store_internal(ritem->deleted_items, path);
208 /* Creates a FeedItem from a message file and uses the data to add a item
209 * to the list of deleted stuff. */
210 void rssyl_deleted_add(RFolderItem *ritem, gchar *path)
212 FeedItem *fitem = NULL;
213 RDeletedItem *ditem = NULL;
214 GSList *deleted_items = rssyl_deleted_update(ritem);
215 gchar *deleted_file = NULL;
217 if (!(fitem = rssyl_parse_folder_item_file(path)))
220 ditem = _new_deleted_item();
221 ditem->id = g_strdup(feed_item_get_id(fitem));
222 ditem->title = conv_unmime_header(feed_item_get_title(fitem),
224 ditem->date_published = feed_item_get_date_published(fitem);
226 deleted_items = g_slist_prepend(deleted_items, ditem);
228 deleted_file = _deleted_file_path(ritem);
229 rssyl_deleted_store_internal(deleted_items, deleted_file);
230 g_free(deleted_file);
232 rssyl_deleted_free(deleted_items);
234 RFeedCtx *ctx = (RFeedCtx *)fitem->data;
236 feed_item_free(fitem);
239 static gint _rssyl_deleted_check_func(gconstpointer a, gconstpointer b)
241 RDeletedItem *ditem = (RDeletedItem *)a;
242 FeedItem *fitem = (FeedItem *)b;
244 gboolean id_match = FALSE;
245 gboolean title_match = FALSE;
246 gboolean pubdate_match = FALSE;
248 g_return_val_if_fail(ditem != NULL, -10);
249 g_return_val_if_fail(fitem != NULL, -20);
251 /* Following must match:
252 * ID, or if there is no ID, the URL, since that's
253 * what would have been stored in .deleted instead
255 if ((id = feed_item_get_id(fitem)) == NULL)
256 id = feed_item_get_url(fitem);
258 if (ditem->id && id &&
259 !strcmp(ditem->id, id))
263 if (ditem->title && feed_item_get_title(fitem) &&
264 !strcmp(ditem->title, feed_item_get_title(fitem)))
267 /* ...and time of publishing */
268 if (ditem->date_published == -1 ||
269 ditem->date_published == feed_item_get_date_published(fitem))
270 pubdate_match = TRUE;
272 /* if all three match, it's the same item */
273 if (id_match && title_match && pubdate_match) {
281 /* Returns TRUE if fitem is found among the deleted stuff. */
282 gboolean rssyl_deleted_check(GSList *deleted_items, FeedItem *fitem)
284 if (g_slist_find_custom(deleted_items, (gconstpointer)fitem,
285 _rssyl_deleted_check_func) != NULL)
291 /******** Expiring ********/
292 struct _RDelExpireCtx {
297 typedef struct _RDelExpireCtx RDelExpireCtx;
299 static void _rssyl_deleted_expire_func_f(gpointer data, gpointer user_data)
301 FeedItem *fitem = (FeedItem *)data;
302 RDelExpireCtx *ctx = (RDelExpireCtx *)user_data;
304 gboolean id_match = FALSE;
305 gboolean title_match = FALSE;
306 gboolean pubdate_match = FALSE;
308 /* Following must match:
309 * ID, or if there is no ID, the URL, since that's
310 * what would have been stored in .deleted instead
312 if ((id = feed_item_get_id(fitem)) == NULL)
313 id = feed_item_get_url(fitem);
315 if (ctx->ditem->id && id &&
316 !strcmp(ctx->ditem->id, id))
320 if (ctx->ditem->title && feed_item_get_title(fitem) &&
321 !strcmp(ctx->ditem->title, feed_item_get_title(fitem)))
324 /* time of publishing, if set... */
325 if (ctx->ditem->date_published == -1 ||
326 ctx->ditem->date_published == feed_item_get_date_published(fitem))
327 pubdate_match = TRUE;
329 /* if it's our item, set to NOT delete, since it's obviously
330 * still in the feed */
331 if (id_match && title_match && pubdate_match)
335 /* Checks each item in deleted items list against feed and removes it if
336 * it is not found there anymore. */
337 void rssyl_deleted_expire(RFolderItem *ritem, Feed *feed)
339 GSList *d = NULL, *d2;
340 RDelExpireCtx *ctx = NULL;
343 g_return_if_fail(ritem != NULL);
344 g_return_if_fail(feed != NULL);
346 ritem->deleted_items = rssyl_deleted_update(ritem);
348 /* Iterate over all items in the list */
349 d = ritem->deleted_items;
351 ditem = (RDeletedItem *)d->data;
352 ctx = g_new0(RDelExpireCtx, 1);
356 /* Adjust ctx->delete accordingly */
357 feed_foreach_item(feed, _rssyl_deleted_expire_func_f, (gpointer)ctx);
359 /* Remove the item if necessary */
361 debug_print("RSSyl: (DELETED) removing '%s' from list\n", ditem->title);
363 ritem->deleted_items = g_slist_remove_link(ritem->deleted_items, d);
373 /* Write the new list to disk */
374 rssyl_deleted_store(ritem);
376 /* And clean up after myself */
377 rssyl_deleted_free(ritem->deleted_items);