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;
299 ritem->ssl_verify_peer = rssyl_prefs_get()->ssl_verify_peer;
301 ritem->contents = NULL;
302 ritem->feedprop = NULL;
304 return (FolderItem *)ritem;
307 static void rssyl_item_destroy(Folder *folder, FolderItem *item)
309 RSSylFolderItem *ritem = (RSSylFolderItem *)item;
311 g_return_if_fail(ritem != NULL);
313 /* Silently remove feed refresh timeouts */
314 if( ritem->refresh_id != 0 )
315 g_source_remove(ritem->refresh_id);
318 g_free(ritem->official_name);
319 g_slist_free(ritem->contents);
324 static FolderItem *rssyl_create_folder(Folder *folder,
325 FolderItem *parent, const gchar *name)
327 gchar *path = NULL, *tmp;
328 FolderItem *newitem = NULL;
330 g_return_val_if_fail(folder != NULL, NULL);
331 g_return_val_if_fail(parent != NULL, NULL);
332 g_return_val_if_fail(name != NULL, NULL);
333 tmp = rssyl_feed_title_to_dir((gchar *)name);
334 path = g_strconcat((parent->path != NULL) ? parent->path : "", ".",
337 newitem = folder_item_new(folder, name, path);
338 folder_item_append(parent, newitem);
344 static gchar *rssyl_item_get_path(Folder *folder, FolderItem *item)
347 tmp = rssyl_feed_title_to_dir(item->name);
348 result = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
349 G_DIR_SEPARATOR_S, tmp, NULL);
354 static gint rssyl_rename_folder(Folder *folder, FolderItem *item,
357 gchar *oldname = NULL, *oldpath = NULL, *newpath = NULL;
358 RSSylFolderItem *ritem = NULL;
359 g_return_val_if_fail(folder != NULL, -1);
360 g_return_val_if_fail(item != NULL, -1);
361 g_return_val_if_fail(item->path != NULL, -1);
362 g_return_val_if_fail(name != NULL, -1);
364 debug_print("RSSyl: renaming folder '%s' to '%s'\n", item->path, name);
366 oldpath = rssyl_item_get_path(folder, item);
368 /* now get the new path using the new name */
369 oldname = item->name;
370 item->name = g_strdup(name);
371 newpath = rssyl_item_get_path(folder, item);
373 /* put back the old name in case the rename fails */
375 item->name = oldname;
377 if (g_rename(oldpath, newpath) < 0) {
378 FILE_OP_ERROR(oldpath, "rename");
385 item->path = g_strdup_printf(".%s", name);
387 ritem = (RSSylFolderItem *)item;
390 rssyl_props_update_name(ritem, (gchar *)name);
393 item->name = g_strdup(name);
400 static gint rssyl_remove_folder(Folder *folder, FolderItem *item)
402 g_return_val_if_fail(folder != NULL, -1);
403 g_return_val_if_fail(item != NULL, -1);
404 g_return_val_if_fail(item->path != NULL, -1);
405 g_return_val_if_fail(item->stype == F_NORMAL, -1);
407 debug_print("RSSyl: removing folder item %s\n", item->path);
409 folder_item_remove(item);
414 static gint rssyl_get_num_list(Folder *folder, FolderItem *item,
415 MsgNumberList **list, gboolean *old_uids_valid)
420 gint num, nummsgs = 0;
421 RSSylFolderItem *ritem = (RSSylFolderItem *)item;
423 g_return_val_if_fail(item != NULL, -1);
425 debug_print("RSSyl: scanning '%s'...\n", item->path);
427 rssyl_get_feed_props(ritem);
429 if (ritem->url == NULL)
432 *old_uids_valid = TRUE;
434 path = folder_item_get_path(item);
435 g_return_val_if_fail(path != NULL, -1);
436 if( change_dir(path) < 0 ) {
442 if( (dp = opendir(".")) == NULL ) {
443 FILE_OP_ERROR(item->path, "opendir");
447 while( (d = readdir(dp)) != NULL ) {
448 if( (num = to_number(d->d_name)) > 0 ) {
449 *list = g_slist_prepend(*list, GINT_TO_POINTER(num));
458 static gboolean rssyl_scan_required(Folder *folder, FolderItem *item)
463 static gchar *rssyl_fetch_msg(Folder *folder, FolderItem *item, gint num)
465 gchar *snum = g_strdup_printf("%d", num);
466 gchar *tmp = rssyl_feed_title_to_dir(item->name);
467 gchar *file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, RSSYL_DIR,
468 G_DIR_SEPARATOR_S, tmp,
469 G_DIR_SEPARATOR_S, snum, NULL);
471 debug_print("RSSyl: fetch_msg: '%s'\n", file);
478 static MsgInfo *rssyl_get_msginfo(Folder *folder, FolderItem *item, gint num)
480 MsgInfo *msginfo = NULL;
484 debug_print("RSSyl: get_msginfo: %d\n", num);
486 g_return_val_if_fail(folder != NULL, NULL);
487 g_return_val_if_fail(item != NULL, NULL);
488 g_return_val_if_fail(num > 0, NULL);
490 file = rssyl_fetch_msg(folder, item, num);
491 g_return_val_if_fail(file != NULL, NULL);
493 flags.perm_flags = MSG_NEW | MSG_UNREAD;
496 msginfo = rssyl_parse_feed_item_to_msginfo(file, flags, TRUE, TRUE, item);
499 msginfo->msgnum = num;
506 static gint rssyl_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
507 GHashTable *relation)
511 MsgFileInfo *fileinfo;
513 g_return_val_if_fail(dest != NULL, -1);
514 g_return_val_if_fail(file_list != NULL, -1);
516 if( dest->last_num < 0 ) {
517 rssyl_get_last_num(folder, dest);
518 if( dest->last_num < 0 ) return -1;
521 for( cur = file_list; cur != NULL; cur = cur->next ) {
522 fileinfo = (MsgFileInfo *)cur->data;
524 destfile = rssyl_get_new_msg_filename(dest);
525 g_return_val_if_fail(destfile != NULL, -1);
528 if( link(fileinfo->file, destfile) < 0 )
530 if( copy_file(fileinfo->file, destfile, TRUE) < 0 ) {
531 g_warning("can't copy message %s to %s\n", fileinfo->file, destfile);
536 if( relation != NULL )
537 g_hash_table_insert(relation, fileinfo,
538 GINT_TO_POINTER(dest->last_num + 1) );
543 return dest->last_num;
546 static gint rssyl_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
551 MsgFileInfo fileinfo;
553 g_return_val_if_fail(file != NULL, -1);
555 fileinfo.msginfo = NULL;
556 fileinfo.file = (gchar *)file;
557 fileinfo.flags = flags;
558 file_list.data = &fileinfo;
559 file_list.next = NULL;
561 ret = rssyl_add_msgs(folder, dest, &file_list, NULL);
565 static gint rssyl_dummy_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *info)
567 if (info->folder == NULL || info->folder->folder != dest->folder) {
570 if (info->folder && info->folder->name && dest->name
571 && !strcmp(info->folder->name, dest->name)) {
572 /* this is a folder move */
573 gchar *file = procmsg_get_message_file(info);
574 gchar *tmp = g_strdup_printf("%s.tmp", file);
575 copy_file(file, tmp, TRUE);
583 static gint rssyl_remove_msg(Folder *folder, FolderItem *item, gint num)
585 gboolean need_scan = FALSE;
588 g_return_val_if_fail(item != NULL, -1);
590 file = rssyl_fetch_msg(folder, item, num);
591 g_return_val_if_fail(file != NULL, -1);
593 need_scan = rssyl_scan_required(folder, item);
595 /* are we doing a folder move ? */
596 tmp = g_strdup_printf("%s.tmp", file);
597 if (is_file_exist(tmp)) {
604 if( claws_unlink(file) < 0 ) {
605 FILE_OP_ERROR(file, "unlink");
611 item->mtime = time(NULL);
617 static gboolean rssyl_subscribe_uri(Folder *folder, const gchar *uri)
619 if (folder->klass != rssyl_folder_get_class())
622 if( rssyl_subscribe_new_feed(
623 FOLDER_ITEM(folder->node->data), uri, FALSE) != NULL )
628 /************************************************************************/
630 FolderClass *rssyl_folder_get_class()
632 if( rssyl_class.idstr == NULL ) {
633 rssyl_class.type = F_UNKNOWN;
634 rssyl_class.idstr = "rssyl";
635 rssyl_class.uistr = "RSSyl";
637 /* Folder functions */
638 rssyl_class.new_folder = rssyl_new_folder;
639 rssyl_class.destroy_folder = rssyl_destroy_folder;
640 rssyl_class.set_xml = folder_set_xml;
641 rssyl_class.get_xml = folder_get_xml;
642 rssyl_class.scan_tree = rssyl_scan_tree;
643 rssyl_class.create_tree = rssyl_create_tree;
645 /* FolderItem functions */
646 rssyl_class.item_new = rssyl_item_new;
647 rssyl_class.item_destroy = rssyl_item_destroy;
648 rssyl_class.item_get_path = rssyl_item_get_path;
649 rssyl_class.create_folder = rssyl_create_folder;
650 rssyl_class.rename_folder = rssyl_rename_folder;
651 rssyl_class.remove_folder = rssyl_remove_folder;
652 rssyl_class.get_num_list = rssyl_get_num_list;
653 rssyl_class.scan_required = rssyl_scan_required;
655 /* Message functions */
656 rssyl_class.get_msginfo = rssyl_get_msginfo;
657 rssyl_class.fetch_msg = rssyl_fetch_msg;
658 rssyl_class.copy_msg = rssyl_dummy_copy_msg;
659 rssyl_class.add_msg = rssyl_add_msg;
660 rssyl_class.add_msgs = rssyl_add_msgs;
661 rssyl_class.remove_msg = rssyl_remove_msg;
662 rssyl_class.remove_msgs = NULL;
663 // rssyl_class.change_flags = rssyl_change_flags;
664 rssyl_class.change_flags = NULL;
665 rssyl_class.subscribe = rssyl_subscribe_uri;
666 debug_print("RSSyl: registered folderclass\n");