Revert "RSSyl: Just copy all of the custom folderlist xml tags in copy_private_data...
[claws.git] / src / plugins / rssyl / rssyl.c
1 /*
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>
5  *
6  * - s-c folderclass callback handler functions
7  *
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.
12  *
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.
17  *
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.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 /* Global includes */
28 #include <glib.h>
29 #include <glib/gi18n.h>
30 #include <curl/curl.h>
31
32 /* Claws Mail includes */
33 #include <folder.h>
34 #include <procmsg.h>
35 #include <localfolder.h>
36 #include <common/utils.h>
37 #include <main.h>
38 #include <mh.h>
39 #include <xml.h>
40 #include <toolbar.h>
41 #include <prefs_toolbar.h>
42
43 /* Local includes */
44 #include "libfeed/feeditem.h"
45 #include "rssyl.h"
46 #include "rssyl_deleted.h"
47 #include "rssyl_gtk.h"
48 #include "rssyl_feed.h"
49 #include "rssyl_prefs.h"
50 #include "rssyl_update_feed.h"
51 #include "rssyl_update_format.h"
52 #include "opml_import.h"
53 #include "opml_export.h"
54 #include "strutils.h"
55
56 FolderClass rssyl_class;
57
58 static gint rssyl_create_tree(Folder *folder);
59 static gint rssyl_scan_tree(Folder *folder);
60
61 static gboolean existing_tree_found = FALSE;
62
63 static void rssyl_init_read_func(FolderItem *item, gpointer data)
64 {
65         RFolderItem *ritem = (RFolderItem *)item;
66         RPrefs *rsprefs = NULL;
67
68         if( !IS_RSSYL_FOLDER_ITEM(item) )
69                 return;
70
71         existing_tree_found = TRUE;
72
73         /* Don't do anything if we're on root of our folder tree or on
74          * a regular folder (no feed) */
75         if( folder_item_parent(item) == NULL || ritem->url == NULL )
76                 return;
77
78         ritem->refresh_id = 0;
79
80         /* Start automatic refresh timer, if necessary */
81         if( ritem->default_refresh_interval ) {
82                 rsprefs = rssyl_prefs_get();
83                 if( !rsprefs->refresh_enabled )
84                         return;
85
86                 ritem->refresh_interval = rsprefs->refresh;
87         }
88
89         /* Start the timer, if determined interval is >0 */
90         if( ritem->refresh_interval > 0 )
91                 rssyl_feed_start_refresh_timeout(ritem);
92 }
93
94 static void rssyl_make_rc_dir(void)
95 {
96         gchar *rssyl_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
97                         NULL);
98
99         if( !is_dir_exist(rssyl_dir) ) {
100                 if( make_dir(rssyl_dir) < 0 ) {
101                         g_warning("couldn't create directory %s\n", rssyl_dir);
102                 }
103
104                 debug_print("RSSyl: created directory %s\n", rssyl_dir);
105         }
106
107         g_free(rssyl_dir);
108 }
109
110 static void rssyl_create_default_mailbox(void)
111 {
112         Folder *root = NULL;
113
114         rssyl_make_rc_dir();
115
116         root = folder_new(rssyl_folder_get_class(), RSSYL_DEFAULT_MAILBOX, NULL);
117
118         g_return_if_fail(root != NULL);
119         folder_add(root);
120
121         rssyl_scan_tree(root);
122
123         /* FIXME: subscribe default feed */
124 //      rssyl_subscribe_new_feed(item, RSSYL_DEFAULT_FEED, TRUE);
125 }
126
127 static gboolean rssyl_update_all_feeds_deferred(gpointer data)
128 {
129         rssyl_update_all_feeds();
130         return FALSE;
131 }
132
133 static void rssyl_toolbar_cb_refresh_all_feeds(gpointer parent, const gchar *item_name, gpointer data)
134 {
135         rssyl_update_all_feeds();
136 }
137
138 void rssyl_init(void)
139 {
140         folder_register_class(rssyl_folder_get_class());
141
142         rssyl_gtk_init();
143         rssyl_make_rc_dir();
144
145         rssyl_prefs_init();
146
147         folder_func_to_all_folders((FolderItemFunc)rssyl_init_read_func, NULL);
148
149         if( !existing_tree_found )
150                 rssyl_create_default_mailbox();
151         else
152                 rssyl_update_format();
153
154         prefs_toolbar_register_plugin_item(TOOLBAR_MAIN, "RSSyl", _("Refresh all feeds"), rssyl_toolbar_cb_refresh_all_feeds, NULL);
155
156         if( rssyl_prefs_get()->refresh_on_startup &&
157                         claws_is_starting() )
158                 g_timeout_add(2000, rssyl_update_all_feeds_deferred, NULL);
159 }
160
161 void rssyl_done(void)
162 {
163         rssyl_opml_export();
164
165         prefs_toolbar_unregister_plugin_item(TOOLBAR_MAIN, "RSSyl", _("Refresh all feeds"));
166
167         rssyl_prefs_done();
168         rssyl_gtk_done();
169
170         if( !claws_is_exiting() )
171                 folder_unregister_class(rssyl_folder_get_class());
172
173         debug_print("RSSyl is done\n");
174 }
175
176 static gchar *rssyl_get_new_msg_filename(FolderItem *dest)
177 {
178         gchar *destfile;
179         gchar *destpath;
180
181         destpath = folder_item_get_path(dest);
182         g_return_val_if_fail(destpath != NULL, NULL);
183
184         if( !is_dir_exist(destpath) )
185                 make_dir_hier(destpath);
186
187         for( ; ; ) {
188                 destfile = g_strdup_printf("%s%c%d", destpath, G_DIR_SEPARATOR,
189                                 dest->last_num + 1);
190                 if( is_file_entry_exist(destfile) ) {
191                         dest->last_num++;
192                         g_free(destfile);
193                 } else
194                         break;
195         }
196
197         g_free(destpath);
198
199         return destfile;
200 }
201
202 static void rssyl_get_last_num(Folder *folder, FolderItem *item)
203 {
204         gchar *path;
205         DIR *dp;
206         struct dirent *d;
207         gint max = 0;
208         gint num;
209
210         g_return_if_fail(item != NULL);
211
212         debug_print("rssyl_get_last_num(): Scanning %s ...\n", item->path);
213         path = folder_item_get_path(item);
214         g_return_if_fail(path != NULL);
215
216         if( (dp = opendir(path)) == NULL ) {
217                 FILE_OP_ERROR(item->path, "opendir");
218                 g_free(path);
219                 return;
220         }
221
222         g_free(path);
223
224         while( (d = readdir(dp)) != NULL ) {
225                 if( (num = to_number(d->d_name)) > 0 && dirent_is_regular_file(d) ) {
226                         if( max < num )
227                                 max = num;
228                 }
229         }
230         closedir(dp);
231
232         debug_print("Last number in dir %s = %d\n", item->path, max);
233         item->last_num = max;
234 }
235
236 static Folder *rssyl_new_folder(const gchar *name, const gchar *path)
237 {
238         Folder *folder;
239
240         debug_print("RSSyl: new_folder: %s (%s)\n", name, path);
241
242         rssyl_make_rc_dir();
243
244         folder = g_new0(Folder, 1);
245         FOLDER(folder)->klass = &rssyl_class;
246         folder_init(FOLDER(folder), name);
247
248         return FOLDER(folder);
249 }
250
251 static void rssyl_destroy_folder(Folder *folder)
252 {
253         folder_local_folder_destroy(LOCAL_FOLDER(folder));
254 }
255
256 static void rssyl_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
257 {
258         GList *cur;
259         RFolderItem *ritem = (RFolderItem *)item;
260
261         folder_item_set_xml(folder, item, tag);
262
263         for( cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
264                 XMLAttr *attr = (XMLAttr *) cur->data;
265
266                 if( !attr || !attr->name || !attr->value)
267                         continue;
268
269                 /* (str) URL */
270                 if( !strcmp(attr->name, "uri")) {
271                         g_free(ritem->url);
272                         ritem->url = g_strdup(attr->value);
273                 }
274                 /* (str) Official title */
275                 if( !strcmp(attr->name, "official_title")) {
276                         g_free(ritem->official_title);
277                         ritem->official_title = g_strdup(attr->value);
278                 }
279                 /* (bool) Keep old items */
280                 if( !strcmp(attr->name, "keep_old"))
281                         ritem->keep_old = (atoi(attr->value) == 0 ? FALSE : TRUE );
282                 /* (bool) Use default refresh_interval */
283                 if( !strcmp(attr->name, "default_refresh_interval"))
284                         ritem->default_refresh_interval = (atoi(attr->value) == 0 ? FALSE : TRUE );
285                 /* (int) Refresh interval */
286                 if( !strcmp(attr->name, "refresh_interval"))
287                         ritem->refresh_interval = atoi(attr->value);
288                 /* (bool) Fetch comments */
289                 if( !strcmp(attr->name, "fetch_comments"))
290                         ritem->fetch_comments = (atoi(attr->value) == 0 ? FALSE : TRUE );
291                 /* (int) Max age of posts to fetch comments for */
292                 if( !strcmp(attr->name, "fetch_comments_max_age"))
293                         ritem->fetch_comments_max_age = atoi(attr->value);
294                 /* (bool) Write heading */
295                 if( !strcmp(attr->name, "write_heading"))
296                         ritem->write_heading = (atoi(attr->value) == 0 ? FALSE : TRUE );
297                 /* (int) Silent update */
298                 if( !strcmp(attr->name, "silent_update"))
299                         ritem->silent_update = atoi(attr->value);
300                 /* (bool) Ignore title rename */
301                 if( !strcmp(attr->name, "ignore_title_rename"))
302                         ritem->ignore_title_rename = (atoi(attr->value) == 0 ? FALSE : TRUE );
303                 /* (bool) Verify SSL peer  */
304                 if( !strcmp(attr->name, "ssl_verify_peer"))
305                         ritem->ssl_verify_peer = (atoi(attr->value) == 0 ? FALSE : TRUE );
306         }
307 }
308
309 static XMLTag *rssyl_item_get_xml(Folder *folder, FolderItem *item)
310 {
311         XMLTag *tag;
312         RFolderItem *ri = (RFolderItem *)item;
313         gchar *tmp = NULL;
314
315         tag = folder_item_get_xml(folder, item);
316
317         /* (str) URL */
318         if( ri->url != NULL )
319                 xml_tag_add_attr(tag, xml_attr_new("uri", ri->url));
320         /* (str) Official title */
321         if( ri->official_title != NULL )
322                 xml_tag_add_attr(tag, xml_attr_new("official_title", ri->official_title));
323         /* (bool) Keep old items */
324         xml_tag_add_attr(tag, xml_attr_new("keep_old",
325                                 (ri->keep_old ? "1" : "0")) );
326         /* (bool) Use default refresh interval */
327         xml_tag_add_attr(tag, xml_attr_new("default_refresh_interval",
328                                 (ri->default_refresh_interval ? "1" : "0")) );
329         /* (int) Refresh interval */
330         tmp = g_strdup_printf("%d", ri->refresh_interval);
331         xml_tag_add_attr(tag, xml_attr_new("refresh_interval", tmp));
332         g_free(tmp);
333         /* (bool) Fetch comments */
334         xml_tag_add_attr(tag, xml_attr_new("fetch_comments",
335                                 (ri->fetch_comments ? "1" : "0")) );
336         /* (int) Max age of posts to fetch comments for */
337         tmp = g_strdup_printf("%d", ri->fetch_comments_max_age);
338         xml_tag_add_attr(tag, xml_attr_new("fetch_comments_max_age", tmp));
339         g_free(tmp);
340         /* (bool) Write heading */
341         xml_tag_add_attr(tag, xml_attr_new("write_heading",
342                                 (ri->write_heading ? "1" : "0")) );
343         /* (int) Silent update */
344         tmp = g_strdup_printf("%d", ri->silent_update);
345         xml_tag_add_attr(tag, xml_attr_new("silent_update", tmp));
346         g_free(tmp);
347         /* (bool) Ignore title rename */
348         xml_tag_add_attr(tag, xml_attr_new("ignore_title_rename",
349                                 (ri->ignore_title_rename ? "1" : "0")) );
350         /* (bool) Verify SSL peer */
351         xml_tag_add_attr(tag, xml_attr_new("ssl_verify_peer",
352                                 (ri->ssl_verify_peer ? "1" : "0")) );
353
354         return tag;
355 }
356
357 static gint rssyl_scan_tree(Folder *folder)
358 {
359         g_return_val_if_fail(folder != NULL, -1);
360
361         folder->outbox = NULL;
362         folder->draft = NULL;
363         folder->queue = NULL;
364         folder->trash = NULL;
365
366         debug_print("RSSyl: scanning tree\n");
367         rssyl_create_tree(folder);
368
369         return 0;
370 }
371
372 static gint rssyl_create_tree(Folder *folder)
373 {
374         FolderItem *rootitem;
375         GNode *rootnode;
376
377         g_return_val_if_fail(folder != NULL, -1);
378
379         rssyl_make_rc_dir();
380
381         if( !folder->node ) {
382                 rootitem = folder_item_new(folder, folder->name, NULL);
383                 rootitem->folder = folder;
384                 rootnode = g_node_new(rootitem);
385                 folder->node = rootnode;
386                 rootitem->node = rootnode;
387         }
388
389         debug_print("RSSyl: created new rssyl tree\n");
390         return 0;
391 }
392
393 static FolderItem *rssyl_item_new(Folder *folder)
394 {
395         RFolderItem *ritem = g_new0(RFolderItem, 1);
396
397         ritem->url = NULL;
398         ritem->official_title = NULL;
399         ritem->source_id = NULL;
400         ritem->items = NULL;
401         ritem->keep_old = FALSE;
402         ritem->default_refresh_interval = TRUE;
403         ritem->refresh_interval = atoi(PREF_DEFAULT_REFRESH);
404         ritem->fetch_comments = FALSE;
405         ritem->fetch_comments_max_age = -1;
406         ritem->write_heading = TRUE;
407         ritem->fetching_comments = FALSE;
408         ritem->silent_update = 0;
409         ritem->last_update = 0;
410         ritem->ignore_title_rename = FALSE;
411
412         return (FolderItem *)ritem;
413 }
414
415 static void rssyl_item_destroy(Folder *folder, FolderItem *item)
416 {
417         RFolderItem *ritem = (RFolderItem *)item;
418
419         g_return_if_fail(ritem != NULL);
420
421         g_free(ritem->url);
422         g_free(ritem->official_title);
423         g_slist_free(ritem->items);
424
425         /* Remove a scheduled refresh, if any */
426         if( ritem->refresh_id != 0)
427                 g_source_remove(ritem->refresh_id);
428
429         g_free(ritem);
430 }
431
432 static FolderItem *rssyl_create_folder(Folder *folder,
433                                                                 FolderItem *parent, const gchar *name)
434 {
435         gchar *path = NULL, *basepath = NULL, *itempath = NULL;
436         FolderItem *newitem = NULL;
437
438         g_return_val_if_fail(folder != NULL, NULL);
439         g_return_val_if_fail(parent != NULL, NULL);
440         g_return_val_if_fail(name != NULL, NULL);
441
442         path = folder_item_get_path(parent);
443         if( !is_dir_exist(path) ) {
444                 if( (make_dir_hier(path) != 0) ) {
445                         debug_print("RSSyl: Couldn't create directory (rec) '%s'\n", path);
446                         return NULL;
447                 }
448         }
449
450         basepath = g_strdelimit(g_strdup(name), G_DIR_SEPARATOR_S, '_');
451         path = g_strconcat(path, G_DIR_SEPARATOR_S, basepath, NULL);
452
453         if( make_dir(path) < 0 ) {
454                 debug_print("RSSyl: Couldn't create directory '%s'\n", path);
455                 g_free(path);
456                 g_free(basepath);
457                 return NULL;
458         }
459         g_free(path);
460
461         itempath = g_strconcat((parent->path ? parent->path : ""),
462                         G_DIR_SEPARATOR_S, basepath, NULL);
463         newitem = folder_item_new(folder, name, itempath);
464         g_free(itempath);
465         g_free(basepath);
466
467         folder_item_append(parent, newitem);
468
469         return newitem;
470 }
471
472 FolderItem *rssyl_get_root_folderitem(FolderItem *item)
473 {
474         FolderItem *i;
475
476         for( i = item; folder_item_parent(i) != NULL; i = folder_item_parent(i) ) { }
477         return i;
478 }
479
480 static gchar *rssyl_item_get_path(Folder *folder, FolderItem *item)
481 {
482         gchar *path, *name;
483
484         g_return_val_if_fail(folder != NULL, NULL);
485         g_return_val_if_fail(item != NULL, NULL);
486
487         debug_print("RSSyl: item_get_path\n");
488
489         name = folder_item_get_name(rssyl_get_root_folderitem(item));
490         path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
491                         G_DIR_SEPARATOR_S, name, G_DIR_SEPARATOR_S, item->path, NULL);
492         g_free(name);
493
494         return path;
495 }
496
497 static gboolean rssyl_rename_folder_func(GNode *node, gpointer data)
498 {
499         FolderItem *item = node->data;
500         gchar **paths = data;
501         const gchar *oldpath = paths[0];
502         const gchar *newpath = paths[1];
503         gchar *base;
504         gchar *new_itempath;
505         gint oldpathlen;
506
507         oldpathlen = strlen(oldpath);
508         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
509                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
510                 return TRUE;
511         }
512
513         base = item->path + oldpathlen;
514         while (*base == G_DIR_SEPARATOR) base++;
515         if (*base == '\0')
516                 new_itempath = g_strdup(newpath);
517         else
518                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
519                                 NULL);
520         g_free(item->path);
521         item->path = new_itempath;
522
523         return FALSE;
524 }
525
526 static gint rssyl_rename_folder(Folder *folder, FolderItem *item,
527                                 const gchar *name)
528 {
529         gchar *oldpath;
530         gchar *dirname;
531         gchar *newpath, *utf8newpath;
532         gchar *basenewpath;
533         gchar *paths[2];
534
535         g_return_val_if_fail(folder != NULL, -1);
536         g_return_val_if_fail(item != NULL, -1);
537         g_return_val_if_fail(item->path != NULL, -1);
538         g_return_val_if_fail(name != NULL, -1);
539
540         debug_print("RSSyl: rssyl_rename_folder '%s' -> '%s'\n",
541                         item->name, name);
542
543         if (!strcmp(item->name, name))
544                         return 0;
545
546         oldpath = folder_item_get_path(item);
547         if( !is_dir_exist(oldpath) )
548                 make_dir_hier(oldpath);
549
550         dirname = g_path_get_dirname(oldpath);
551         basenewpath = g_strdelimit(g_strdup(name), G_DIR_SEPARATOR_S, '_');
552         newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, basenewpath, NULL);
553         g_free(basenewpath);
554
555         if( g_rename(oldpath, newpath) < 0 ) {
556                 FILE_OP_ERROR(oldpath, "rename");
557                 g_free(oldpath);
558                 g_free(newpath);
559                 return -1;
560         }
561
562         g_free(oldpath);
563         g_free(newpath);
564
565         if( strchr(item->path, G_DIR_SEPARATOR) != NULL ) {
566                 dirname = g_path_get_dirname(item->path);
567                 utf8newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, name, NULL);
568                 g_free(dirname);
569         } else
570                 utf8newpath = g_strdup(name);
571
572         g_free(item->name);
573         item->name = g_strdup(name);
574
575         paths[0] = g_strdup(item->path);
576         paths[1] = utf8newpath;
577         g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
578                         rssyl_rename_folder_func, paths);
579
580         g_free(paths[0]);
581         g_free(paths[1]);
582
583         return 0;
584 }
585
586 static gint rssyl_remove_folder(Folder *folder, FolderItem *item)
587 {
588         gchar *path = NULL;
589
590         g_return_val_if_fail(folder != NULL, -1);
591         g_return_val_if_fail(item != NULL, -1);
592         g_return_val_if_fail(item->path != NULL, -1);
593         g_return_val_if_fail(item->stype == F_NORMAL, -1);
594
595         debug_print("RSSyl: removing folder item %s\n", item->path);
596
597         path = folder_item_get_path(item);
598         if( remove_dir_recursive(path) < 0 ) {
599                 g_warning("can't remove directory '%s'\n", path);
600                 g_free(path);
601                 return -1;
602         }
603
604         g_free(path);
605         folder_item_remove(item);
606
607         return 0;
608 }
609
610 static gint rssyl_get_num_list(Folder *folder, FolderItem *item,
611                 MsgNumberList **list, gboolean *old_uids_valid)
612 {
613         gchar *path;
614         DIR *dp;
615         struct dirent *d;
616         gint num, nummsgs = 0;
617
618         g_return_val_if_fail(item != NULL, -1);
619
620         debug_print("RSSyl: get_num_list: scanning '%s'\n", item->path);
621
622         *old_uids_valid = TRUE;
623         
624         path = folder_item_get_path(item);
625         g_return_val_if_fail(path != NULL, -1);
626
627         if( (dp = opendir(path)) == NULL ) {
628                 FILE_OP_ERROR(item->path, "opendir");
629                 g_free(path);
630                 return -1;
631         }
632
633         g_free(path);
634
635         while( (d = readdir(dp)) != NULL ) {
636                 if( (num = to_number(d->d_name)) > 0 ) {
637                         *list = g_slist_prepend(*list, GINT_TO_POINTER(num));
638                         nummsgs++;
639                 }
640         }
641
642         closedir(dp);
643
644         debug_print("RSSyl: get_num_list: returning %d\n", nummsgs);
645
646         return nummsgs;
647 }
648
649 static gboolean rssyl_scan_required(Folder *folder, FolderItem *item)
650 {
651         return TRUE;
652 }
653
654 static gchar *rssyl_fetch_msg(Folder *folder, FolderItem *item, gint num)
655 {
656         gchar *path;
657         gchar *file;
658
659         g_return_val_if_fail(item != NULL, NULL);
660         g_return_val_if_fail(num > 0, NULL);
661
662         path = folder_item_get_path(item);
663         file = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
664         g_free(path);
665
666         debug_print("RSSyl: fetch_msg '%s'\n", file);
667
668         if( !is_file_exist(file)) {
669                 g_free(file);
670                 return NULL;
671         }
672
673         return file;
674 }
675
676 static MsgInfo *rssyl_get_msginfo(Folder *folder, FolderItem *item, gint num)
677 {
678         MsgInfo *msginfo = NULL;
679         gchar *file;
680         MsgFlags flags;
681
682         g_return_val_if_fail(folder != NULL, NULL);
683         g_return_val_if_fail(item != NULL, NULL);
684         g_return_val_if_fail(num > 0, NULL);
685
686         debug_print("RSSyl: get_msginfo: %d\n", num);
687
688         file = rssyl_fetch_msg(folder, item, num);
689         g_return_val_if_fail(file != NULL, NULL);
690
691         flags.perm_flags = 0;
692         flags.tmp_flags = 0;
693
694         msginfo = rssyl_feed_parse_item_to_msginfo(file, flags, TRUE, TRUE, item);
695         g_free(file);
696
697         if( msginfo )
698                 msginfo->msgnum = num;
699
700         return msginfo;
701 }
702
703 static gint rssyl_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
704                 GHashTable *relation)
705 {
706         gchar *destfile;
707         GSList *cur;
708         MsgFileInfo *fileinfo;
709
710         g_return_val_if_fail(dest != NULL, -1);
711         g_return_val_if_fail(file_list != NULL, -1);
712
713         if( dest->last_num < 0 ) {
714                 rssyl_get_last_num(folder, dest);
715                 if( dest->last_num < 0 ) return -1;
716         }
717
718         for( cur = file_list; cur != NULL; cur = cur->next ) {
719                 fileinfo = (MsgFileInfo *)cur->data;
720
721                 destfile = rssyl_get_new_msg_filename(dest);
722                 g_return_val_if_fail(destfile != NULL, -1);
723                 debug_print("RSSyl: add_msgs: new filename is '%s'\n", destfile);
724
725                 if( link(fileinfo->file, destfile) < 0 ) {
726                         if( copy_file(fileinfo->file, destfile, TRUE) < 0 ) {
727                                 g_warning("can't copy message %s to %s\n", fileinfo->file, destfile);
728                                 g_free(destfile);
729                                 return -1;
730                         }
731                 }
732
733                 if( relation != NULL )
734                         g_hash_table_insert(relation, fileinfo->msginfo != NULL ?
735                                         (gpointer) fileinfo->msginfo : (gpointer) fileinfo,
736                                         GINT_TO_POINTER(dest->last_num + 1));
737                 g_free(destfile);
738                 dest->last_num++;
739         }
740
741
742         return dest->last_num;
743 }
744
745 static gint rssyl_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
746                 MsgFlags *flags)
747 {
748         GSList file_list;
749         MsgFileInfo fileinfo;
750
751         g_return_val_if_fail(file != NULL, -1);
752
753         fileinfo.msginfo = NULL;
754         fileinfo.file = (gchar *)file;
755         fileinfo.flags = flags;
756         file_list.data = &fileinfo;
757         file_list.next = NULL;
758
759         return rssyl_add_msgs(folder, dest, &file_list, NULL);
760 }
761
762 static gint rssyl_remove_msg(Folder *folder, FolderItem *item, gint num)
763 {
764         gboolean need_scan = FALSE;
765         gchar *file, *tmp;
766
767         g_return_val_if_fail(item != NULL, -1);
768
769         file = rssyl_fetch_msg(folder, item, num);
770         g_return_val_if_fail(file != NULL, -1);
771
772         need_scan = rssyl_scan_required(folder, item);
773
774         /* are we doing a folder move ? */
775         tmp = g_strdup_printf("%s.tmp", file);
776         if (is_file_exist(tmp)) {
777                 g_unlink(tmp);
778                 g_free(tmp);
779                 g_free(file);
780                 return 0;
781         }
782         g_free(tmp);
783
784         rssyl_deleted_add((RFolderItem *)item, file);
785
786         if( g_unlink(file) < 0 ) {
787                 FILE_OP_ERROR(file, "unlink");
788                 g_free(file);
789                 return -1;
790         }
791
792         if( !need_scan )
793                 item->mtime = time(NULL);
794
795         g_free(file);
796         return 0;
797 }
798
799 static gboolean rssyl_subscribe_uri(Folder *folder, const gchar *uri)
800 {
801         if (folder->klass != rssyl_folder_get_class())
802                 return FALSE;
803         return (rssyl_feed_subscribe_new(FOLDER_ITEM(folder->node->data), uri, FALSE) ?
804                         TRUE : FALSE);
805 }
806
807 static void rssyl_copy_private_data(Folder *folder, FolderItem *oldi,
808                 FolderItem *newi)
809 {
810         RFolderItem *olditem = (RFolderItem *)oldi,
811                                                                         *newitem = (RFolderItem *)newi;
812
813         g_return_if_fail(folder != NULL);
814         g_return_if_fail(olditem != NULL);
815         g_return_if_fail(newitem != NULL);
816
817         if( olditem->url != NULL ) {
818                 g_free(newitem->url);
819                 newitem->url = g_strdup(olditem->url);
820         }
821
822         if( olditem->official_title != NULL ) {
823                 g_free(newitem->official_title);
824                 newitem->official_title = g_strdup(olditem->official_title);
825         }
826 }
827
828 /************************************************************************/
829
830 FolderClass *rssyl_folder_get_class()
831 {
832         if( rssyl_class.idstr == NULL ) {
833                 rssyl_class.type = F_UNKNOWN;
834                 rssyl_class.idstr = "rssyl";
835                 rssyl_class.uistr = "RSSyl";
836
837                 /* Folder functions */
838                 rssyl_class.new_folder = rssyl_new_folder;
839                 rssyl_class.destroy_folder = rssyl_destroy_folder;
840                 rssyl_class.set_xml = folder_set_xml;
841                 rssyl_class.get_xml = folder_get_xml;
842                 rssyl_class.scan_tree = rssyl_scan_tree;
843                 rssyl_class.create_tree = rssyl_create_tree;
844
845                 /* FolderItem functions */
846                 rssyl_class.item_new = rssyl_item_new;
847                 rssyl_class.item_destroy = rssyl_item_destroy;
848                 rssyl_class.item_get_path = rssyl_item_get_path;
849                 rssyl_class.create_folder = rssyl_create_folder;
850                 rssyl_class.rename_folder = rssyl_rename_folder;
851                 rssyl_class.remove_folder = rssyl_remove_folder;
852                 rssyl_class.get_num_list = rssyl_get_num_list;
853                 rssyl_class.scan_required = rssyl_scan_required;
854                 rssyl_class.item_set_xml = rssyl_item_set_xml;
855                 rssyl_class.item_get_xml = rssyl_item_get_xml;
856
857                 /* Message functions */
858                 rssyl_class.get_msginfo = rssyl_get_msginfo;
859                 rssyl_class.fetch_msg = rssyl_fetch_msg;
860                 rssyl_class.copy_msg = mh_get_class()->copy_msg;
861                 rssyl_class.copy_msgs = mh_get_class()->copy_msgs;
862                 rssyl_class.add_msg = rssyl_add_msg;
863                 rssyl_class.add_msgs = rssyl_add_msgs;
864                 rssyl_class.remove_msg = rssyl_remove_msg;
865                 rssyl_class.remove_msgs = NULL;
866 //              rssyl_class.change_flags = rssyl_change_flags;
867                 rssyl_class.change_flags = NULL;
868                 rssyl_class.subscribe = rssyl_subscribe_uri;
869                 rssyl_class.copy_private_data = rssyl_copy_private_data;
870                 rssyl_class.search_msgs = folder_item_search_msgs_local;
871         }
872
873         return &rssyl_class;
874 }