2 * Sylpheed -- 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 * - s-c folderclass callback handler functions
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.
25 #include "claws-features.h"
29 #include <glib/gi18n.h>
36 #include <curl/curl.h>
39 #include "localfolder.h"
41 #include "procheader.h"
42 #include "common/utils.h"
44 #include "prefs_toolbar.h"
49 #include "feedprops.h"
52 #include "rssyl_gtk.h"
53 #include "rssyl_prefs.h"
54 #include "strreplace.h"
56 static gint rssyl_create_tree(Folder *folder);
58 static gboolean existing_tree_found = FALSE;
60 static void rssyl_init_read_func(FolderItem *item, gpointer data)
62 if( !IS_RSSYL_FOLDER_ITEM(item) )
65 existing_tree_found = TRUE;
67 if( folder_item_parent(item) == NULL )
70 rssyl_get_feed_props((RSSylFolderItem *)item);
73 static void rssyl_make_rc_dir(void)
75 gchar *rssyl_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
78 if( !is_dir_exist(rssyl_dir) ) {
79 if( make_dir(rssyl_dir) < 0 ) {
80 g_warning("couldn't create directory %s\n", rssyl_dir);
83 debug_print("created directorty %s\n", rssyl_dir);
89 static void rssyl_create_default_mailbox(void)
96 root = folder_new(rssyl_folder_get_class(), RSSYL_DEFAULT_MAILBOX, NULL);
98 g_return_if_fail(root != NULL);
101 item = FOLDER_ITEM(root->node->data);
103 rssyl_subscribe_new_feed(item, RSSYL_DEFAULT_FEED, TRUE);
106 static gboolean rssyl_refresh_all_feeds_deferred(gpointer data)
108 rssyl_refresh_all_feeds();
112 static void rssyl_toolbar_cb_refresh_all(gpointer parent, const gchar *item_name, gpointer data)
114 rssyl_refresh_all_feeds();
117 void rssyl_init(void)
119 folder_register_class(rssyl_folder_get_class());
127 folder_func_to_all_folders((FolderItemFunc)rssyl_init_read_func, NULL);
129 if( existing_tree_found == FALSE )
130 rssyl_create_default_mailbox();
132 prefs_toolbar_register_plugin_item(TOOLBAR_MAIN, "RSSyl",
133 _("Refresh all feeds"), rssyl_toolbar_cb_refresh_all, NULL);
137 if( rssyl_prefs_get()->refresh_on_startup &&
138 claws_is_starting() )
139 g_timeout_add(2000, rssyl_refresh_all_feeds_deferred, NULL);
142 void rssyl_done(void)
144 prefs_toolbar_unregister_plugin_item(TOOLBAR_MAIN, "RSSyl",
145 _("Refresh all feeds"));
148 if (!claws_is_exiting())
149 folder_unregister_class(rssyl_folder_get_class());
152 static gchar *rssyl_get_new_msg_filename(FolderItem *dest)
157 destpath = folder_item_get_path(dest);
158 g_return_val_if_fail(destpath != NULL, NULL);
160 if( !is_dir_exist(destpath) )
161 make_dir_hier(destpath);
164 destfile = g_strdup_printf("%s%c%d", destpath, G_DIR_SEPARATOR,
166 if( is_file_entry_exist(destfile) ) {
178 static void rssyl_get_last_num(Folder *folder, FolderItem *item)
186 g_return_if_fail(item != NULL);
188 debug_print("rssyl_get_last_num(): Scanning %s ...\n", item->path);
189 path = folder_item_get_path(item);
190 g_return_if_fail(path != NULL);
191 if( change_dir(path) < 0 ) {
197 if( (dp = opendir(".")) == NULL ) {
198 FILE_OP_ERROR(item->path, "opendir");
202 while( (d = readdir(dp)) != NULL ) {
203 if( (num = to_number(d->d_name)) > 0 && dirent_is_regular_file(d) ) {
210 debug_print("Last number in dir %s = %d\n", item->path, max);
211 item->last_num = max;
214 struct _RSSylFolder {
218 typedef struct _RSSylFolder RSSylFolder;
220 FolderClass rssyl_class;
222 static Folder *rssyl_new_folder(const gchar *name, const gchar *path)
226 debug_print("RSSyl: new_folder\n");
230 folder = g_new0(RSSylFolder, 1);
231 FOLDER(folder)->klass = &rssyl_class;
232 folder_init(FOLDER(folder), name);
234 return FOLDER(folder);
237 static void rssyl_destroy_folder(Folder *_folder)
239 RSSylFolder *folder = (RSSylFolder *)_folder;
241 folder_local_folder_destroy(LOCAL_FOLDER(folder));
244 static gint rssyl_scan_tree(Folder *folder)
246 g_return_val_if_fail(folder != NULL, -1);
248 folder->outbox = NULL;
249 folder->draft = NULL;
250 folder->queue = NULL;
251 folder->trash = NULL;
253 debug_print("RSSyl: scanning tree\n");
254 rssyl_create_tree(folder);
259 static gint rssyl_create_tree(Folder *folder)
261 FolderItem *rootitem;
266 if( !folder->node ) {
267 rootitem = folder_item_new(folder, folder->name, NULL);
268 rootitem->folder = folder;
269 rootnode = g_node_new(rootitem);
270 folder->node = rootnode;
271 rootitem->node = rootnode;
273 rootitem = FOLDER_ITEM(folder->node->data);
274 rootnode = folder->node;
277 debug_print("RSSyl: created new rssyl tree\n");
281 static FolderItem *rssyl_item_new(Folder *folder)
283 RSSylFolderItem *ritem;
285 debug_print("RSSyl: item_new\n");
287 ritem = g_new0(RSSylFolderItem, 1);
290 ritem->default_refresh_interval = TRUE;
291 ritem->default_expired_num = TRUE;
292 ritem->fetch_comments = FALSE;
293 ritem->fetch_comments_for = -1;
294 ritem->silent_update = 0;
295 ritem->refresh_interval = rssyl_prefs_get()->refresh;
296 ritem->refresh_id = 0;
297 ritem->expired_num = rssyl_prefs_get()->expired;
298 ritem->last_count = 0;
300 ritem->contents = NULL;
301 ritem->feedprop = NULL;
303 return (FolderItem *)ritem;
306 static void rssyl_item_destroy(Folder *folder, FolderItem *item)
308 RSSylFolderItem *ritem = (RSSylFolderItem *)item;
310 g_return_if_fail(ritem != NULL);
312 /* Silently remove feed refresh timeouts */
313 if( ritem->refresh_id != 0 )
314 g_source_remove(ritem->refresh_id);
317 g_free(ritem->official_name);
318 g_slist_free(ritem->contents);
323 static FolderItem *rssyl_create_folder(Folder *folder,
324 FolderItem *parent, const gchar *name)
326 gchar *path = NULL, *tmp;
327 FolderItem *newitem = NULL;
329 g_return_val_if_fail(folder != NULL, NULL);
330 g_return_val_if_fail(parent != NULL, NULL);
331 g_return_val_if_fail(name != NULL, NULL);
332 tmp = rssyl_feed_title_to_dir((gchar *)name);
333 path = g_strconcat((parent->path != NULL) ? parent->path : "", ".",
336 newitem = folder_item_new(folder, name, path);
337 folder_item_append(parent, newitem);
343 static gchar *rssyl_item_get_path(Folder *folder, FolderItem *item)
346 tmp = rssyl_feed_title_to_dir(item->name);
347 result = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
348 G_DIR_SEPARATOR_S, tmp, NULL);
353 static gint rssyl_rename_folder(Folder *folder, FolderItem *item,
356 gchar *oldname = NULL, *oldpath = NULL, *newpath = NULL;
357 RSSylFolderItem *ritem = NULL;
358 g_return_val_if_fail(folder != NULL, -1);
359 g_return_val_if_fail(item != NULL, -1);
360 g_return_val_if_fail(item->path != NULL, -1);
361 g_return_val_if_fail(name != NULL, -1);
363 debug_print("RSSyl: renaming folder '%s' to '%s'\n", item->path, name);
365 oldpath = rssyl_item_get_path(folder, item);
367 /* now get the new path using the new name */
368 oldname = item->name;
369 item->name = g_strdup(name);
370 newpath = rssyl_item_get_path(folder, item);
372 /* put back the old name in case the rename fails */
374 item->name = oldname;
376 if (g_rename(oldpath, newpath) < 0) {
377 FILE_OP_ERROR(oldpath, "rename");
384 item->path = g_strdup_printf(".%s", name);
386 ritem = (RSSylFolderItem *)item;
389 rssyl_props_update_name(ritem, (gchar *)name);
392 item->name = g_strdup(name);
399 static gint rssyl_remove_folder(Folder *folder, FolderItem *item)
401 g_return_val_if_fail(folder != NULL, -1);
402 g_return_val_if_fail(item != NULL, -1);
403 g_return_val_if_fail(item->path != NULL, -1);
404 g_return_val_if_fail(item->stype == F_NORMAL, -1);
406 debug_print("RSSyl: removing folder item %s\n", item->path);
408 folder_item_remove(item);
413 static gint rssyl_get_num_list(Folder *folder, FolderItem *item,
414 MsgNumberList **list, gboolean *old_uids_valid)
419 gint num, nummsgs = 0;
420 RSSylFolderItem *ritem = (RSSylFolderItem *)item;
422 g_return_val_if_fail(item != NULL, -1);
424 debug_print("RSSyl: scanning '%s'...\n", item->path);
426 rssyl_get_feed_props(ritem);
428 if (ritem->url == NULL)
431 *old_uids_valid = TRUE;
433 path = folder_item_get_path(item);
434 g_return_val_if_fail(path != NULL, -1);
435 if( change_dir(path) < 0 ) {
441 if( (dp = opendir(".")) == NULL ) {
442 FILE_OP_ERROR(item->path, "opendir");
446 while( (d = readdir(dp)) != NULL ) {
447 if( (num = to_number(d->d_name)) > 0 ) {
448 *list = g_slist_prepend(*list, GINT_TO_POINTER(num));
457 static gboolean rssyl_scan_required(Folder *folder, FolderItem *item)
462 static gchar *rssyl_fetch_msg(Folder *folder, FolderItem *item, gint num)
464 gchar *snum = g_strdup_printf("%d", num);
465 gchar *tmp = rssyl_feed_title_to_dir(item->name);
466 gchar *file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
467 G_DIR_SEPARATOR_S, tmp,
468 G_DIR_SEPARATOR_S, snum, NULL);
470 debug_print("RSSyl: fetch_msg: '%s'\n", file);
477 static MsgInfo *rssyl_get_msginfo(Folder *folder, FolderItem *item, gint num)
479 MsgInfo *msginfo = NULL;
483 debug_print("RSSyl: get_msginfo: %d\n", num);
485 g_return_val_if_fail(folder != NULL, NULL);
486 g_return_val_if_fail(item != NULL, NULL);
487 g_return_val_if_fail(num > 0, NULL);
489 file = rssyl_fetch_msg(folder, item, num);
490 g_return_val_if_fail(file != NULL, NULL);
492 flags.perm_flags = MSG_NEW | MSG_UNREAD;
495 msginfo = rssyl_parse_feed_item_to_msginfo(file, flags, TRUE, TRUE, item);
498 msginfo->msgnum = num;
505 static gint rssyl_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
506 GHashTable *relation)
510 MsgFileInfo *fileinfo;
512 g_return_val_if_fail(dest != NULL, -1);
513 g_return_val_if_fail(file_list != NULL, -1);
515 if( dest->last_num < 0 ) {
516 rssyl_get_last_num(folder, dest);
517 if( dest->last_num < 0 ) return -1;
520 for( cur = file_list; cur != NULL; cur = cur->next ) {
521 fileinfo = (MsgFileInfo *)cur->data;
523 destfile = rssyl_get_new_msg_filename(dest);
524 g_return_val_if_fail(destfile != NULL, -1);
527 if( link(fileinfo->file, destfile) < 0 )
529 if( copy_file(fileinfo->file, destfile, TRUE) < 0 ) {
530 g_warning("can't copy message %s to %s\n", fileinfo->file, destfile);
535 if( relation != NULL )
536 g_hash_table_insert(relation, fileinfo,
537 GINT_TO_POINTER(dest->last_num + 1) );
542 return dest->last_num;
545 static gint rssyl_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
550 MsgFileInfo fileinfo;
552 g_return_val_if_fail(file != NULL, -1);
554 fileinfo.msginfo = NULL;
555 fileinfo.file = (gchar *)file;
556 fileinfo.flags = flags;
557 file_list.data = &fileinfo;
558 file_list.next = NULL;
560 ret = rssyl_add_msgs(folder, dest, &file_list, NULL);
564 static gint rssyl_dummy_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *info)
566 if (info->folder == NULL || info->folder->folder != dest->folder) {
569 if (info->folder && info->folder->name && dest->name
570 && !strcmp(info->folder->name, dest->name)) {
571 /* this is a folder move */
572 gchar *file = procmsg_get_message_file(info);
573 gchar *tmp = g_strdup_printf("%s.tmp", file);
574 copy_file(file, tmp, TRUE);
582 static gint rssyl_remove_msg(Folder *folder, FolderItem *item, gint num)
584 gboolean need_scan = FALSE;
587 g_return_val_if_fail(item != NULL, -1);
589 file = rssyl_fetch_msg(folder, item, num);
590 g_return_val_if_fail(file != NULL, -1);
592 need_scan = rssyl_scan_required(folder, item);
594 /* are we doing a folder move ? */
595 tmp = g_strdup_printf("%s.tmp", file);
596 if (is_file_exist(tmp)) {
603 if( claws_unlink(file) < 0 ) {
604 FILE_OP_ERROR(file, "unlink");
610 item->mtime = time(NULL);
616 static gboolean rssyl_subscribe_uri(Folder *folder, const gchar *uri)
618 if (folder->klass != rssyl_folder_get_class())
621 if( rssyl_subscribe_new_feed(
622 FOLDER_ITEM(folder->node->data), uri, FALSE) != NULL )
627 /************************************************************************/
629 FolderClass *rssyl_folder_get_class()
631 if( rssyl_class.idstr == NULL ) {
632 rssyl_class.type = F_UNKNOWN;
633 rssyl_class.idstr = "rssyl";
634 rssyl_class.uistr = "RSSyl";
636 /* Folder functions */
637 rssyl_class.new_folder = rssyl_new_folder;
638 rssyl_class.destroy_folder = rssyl_destroy_folder;
639 rssyl_class.set_xml = folder_set_xml;
640 rssyl_class.get_xml = folder_get_xml;
641 rssyl_class.scan_tree = rssyl_scan_tree;
642 rssyl_class.create_tree = rssyl_create_tree;
644 /* FolderItem functions */
645 rssyl_class.item_new = rssyl_item_new;
646 rssyl_class.item_destroy = rssyl_item_destroy;
647 rssyl_class.item_get_path = rssyl_item_get_path;
648 rssyl_class.create_folder = rssyl_create_folder;
649 rssyl_class.rename_folder = rssyl_rename_folder;
650 rssyl_class.remove_folder = rssyl_remove_folder;
651 rssyl_class.get_num_list = rssyl_get_num_list;
652 rssyl_class.scan_required = rssyl_scan_required;
654 /* Message functions */
655 rssyl_class.get_msginfo = rssyl_get_msginfo;
656 rssyl_class.fetch_msg = rssyl_fetch_msg;
657 rssyl_class.copy_msg = rssyl_dummy_copy_msg;
658 rssyl_class.add_msg = rssyl_add_msg;
659 rssyl_class.add_msgs = rssyl_add_msgs;
660 rssyl_class.remove_msg = rssyl_remove_msg;
661 rssyl_class.remove_msgs = NULL;
662 // rssyl_class.change_flags = rssyl_change_flags;
663 rssyl_class.change_flags = NULL;
664 rssyl_class.subscribe = rssyl_subscribe_uri;
665 debug_print("RSSyl: registered folderclass\n");