4 /* vim:et:ts=4:sw=4:et:sts=4:ai:set list listchars=tab\:»·,trail\:·: */
7 * Virtual folder plugin for claws-mail
9 * Claws Mail is Copyright (C) 1999-2012 by the Claws Mail Team
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
32 #include "procheader.h"
34 #include "alertpanel.h"
36 #include "localfolder.h"
37 #include "statusbar.h"
40 #include "vfolder_gtk.h"
41 #include "vfolder_prop.h"
47 FolderClass vfolder_class
;
48 static gboolean existing_tree_found
= FALSE
;
49 static GList
* vfolders
= NULL
;
51 static void vfolder_get_last_num(Folder
*folder
, FolderItem
*item
)
59 g_return_if_fail(item
!= NULL
);
61 debug_print("vfolder_get_last_num(): Scanning %s ...\n", item
->path
);
62 path
= folder_item_get_path(item
);
63 g_return_if_fail(path
!= NULL
);
64 if( change_dir(path
) < 0 ) {
70 if( (dp
= opendir(".")) == NULL
) {
71 FILE_OP_ERROR(item
->path
, "opendir");
75 while( (d
= readdir(dp
)) != NULL
) {
76 if( (num
= to_number(d
->d_name
)) > 0 && dirent_is_regular_file(d
) ) {
83 debug_print("Last number in dir %s = %d\n", item
->path
, max
);
87 static void vfolder_init_read_func(FolderItem
* item
, gpointer data
) {
88 g_return_if_fail(item
!= NULL
);
90 if (! IS_VFOLDER_FOLDER_ITEM(item
))
93 existing_tree_found
= TRUE
;
95 if (folder_item_parent(item
) == NULL
)
98 vfolder_folder_item_props_read(VFOLDER_ITEM(item
));
99 vfolder_set_msgs_filter(VFOLDER_ITEM(item
));
101 /* if (! IS_VFOLDER_FROZEN(VFOLDER_ITEM(item))) {
102 folder_item_scan(VFOLDER_ITEM(item)->source);
106 static void vfolder_make_rc_dir(void) {
107 gchar
* vfolder_dir
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
, VFOLDER_DIR
, NULL
);
109 if (! is_dir_exist(vfolder_dir
)) {
110 if (make_dir(vfolder_dir
) < 0) {
111 g_warning("couldn't create directory %s\n", vfolder_dir
);
114 debug_print("created directorty %s\n", vfolder_dir
);
120 static void vfolder_create_default_mailbox(void) {
123 vfolder_make_rc_dir();
125 root
= folder_new(vfolder_folder_get_class(), VFOLDER_DEFAULT_MAILBOX
, NULL
);
127 g_return_if_fail(root
!= NULL
);
132 static gboolean
vfolder_scan_required(Folder
* folder
, FolderItem
* item
) {
136 static Folder
* vfolder_new_folder(const gchar
* name
, const gchar
* path
) {
139 debug_print("VFolder: new_folder\n");
141 vfolder_make_rc_dir();
143 folder
= g_new0(VFolder
, 1);
144 FOLDER(folder
)->klass
= &vfolder_class
;
145 folder_init(FOLDER(folder
), name
);
147 return FOLDER(folder
);
150 static void vfolder_destroy_folder(Folder
* _folder
)
152 VFolder
* folder
= VFOLDER(_folder
);
154 folder_local_folder_destroy(LOCAL_FOLDER(folder
));
157 static gint
vfolder_remove_folder(Folder
* folder
, FolderItem
* item
) {
163 g_return_val_if_fail(folder
!= NULL
, -1);
164 g_return_val_if_fail(item
!= NULL
, -1);
165 g_return_val_if_fail(item
->path
!= NULL
, -1);
166 g_return_val_if_fail(item
->stype
== F_NORMAL
, -1);
168 debug_print("VFolder: removing folder item %s\n", item
->path
);
170 gchar
* path
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
,
171 VFOLDER_DIR
, G_DIR_SEPARATOR_S
, item
->name
, NULL
);
172 remove_numbered_files(path
, item
->last_num
- item
->total_msgs
+ 1, item
->last_num
);
174 folder_item_remove(item
);
176 cwd
= g_get_current_dir();
178 if (change_dir(path
) < 0 ) {
183 if( (dp
= g_dir_open(".", 0, NULL
)) == NULL
) {
184 FILE_OP_ERROR(path
, "g_dir_open");
190 while ((name
= g_dir_read_name(dp
)) != NULL
) {
191 debug_print("Removing: %s\n", name
);
196 if (g_rmdir(path
) == 0)
205 static void vfolder_remove_hashtables(VFolderItem
* item
) {
206 if (item
->me_to_claws
) {
207 g_hash_table_destroy(item
->me_to_claws
);
208 item
->me_to_claws
= NULL
;
210 if (item
->claws_to_me
) {
211 g_hash_table_destroy(item
->claws_to_me
);
212 item
->claws_to_me
= NULL
;
216 static gboolean
vfolder_remove_msg_from_bridge(VFolderItem
* item
,
219 gboolean ret
= FALSE
;
221 debug_print("Removing from hashtable: src->%u dest->%u\n", src
, dest
);
222 ret
= g_hash_table_remove(item
->me_to_claws
, GUINT_TO_POINTER(dest
));
223 ret
= g_hash_table_remove(item
->claws_to_me
, GUINT_TO_POINTER(src
));
225 return (ret
== FALSE
);
228 static gboolean vfolder_remove_msg_bridge_claws(VFolderItem* item, guint num) {
229 return vfolder_remove_msg_from_bridge_rel(item, num, TRUE);
232 static gboolean vfolder_remove_msg_bridge_vfolder(VFolderItem* item, guint num) {
233 return vfolder_remove_msg_from_bridge_rel(item, num, FALSE);
236 static void vfolder_free_hashtable_data(gpointer data
) {
240 static MsgBridge
* vfolder_create_bridge(guint src
, guint dest
) {
243 bridge
= g_new0(MsgBridge
, 1);
244 bridge
->my_num
= dest
;
245 bridge
->claws_num
= src
;
250 static GHashTable
* vfolder_hashtable_new() {
251 return g_hash_table_new_full(g_direct_hash
,
252 g_direct_equal
, NULL
, vfolder_free_hashtable_data
);
255 static gboolean
vfolder_add_msg_to_message_bridge(VFolderItem
* item
,
258 gboolean ret
= FALSE
;
261 if (! item
->me_to_claws
)
262 item
->me_to_claws
= vfolder_hashtable_new();
263 if (! item
->claws_to_me
)
264 item
->claws_to_me
= vfolder_hashtable_new();
266 debug_print("Adding to hashtable: src->%u dest->%u\n", src
, dest
);
267 bridge
= vfolder_create_bridge(src
, dest
);
268 g_hash_table_replace(item
->me_to_claws
, GUINT_TO_POINTER(dest
), bridge
);
270 bridge
= vfolder_create_bridge(src
, dest
);
271 g_hash_table_replace(item
->claws_to_me
, GUINT_TO_POINTER(src
), bridge
);
276 static FolderItem
* vfolder_item_new(Folder
* folder
) {
279 debug_print("VFolder: item_new\n");
281 vitem
= g_new0(VFolderItem
, 1);
283 gchar
* id
= folder_get_identifier(folder
);
284 gchar
* cmp
= g_strconcat("#", VFOLDER_DIR
, G_DIR_SEPARATOR_S
, NULL
);
285 if (id
&& strcmp(cmp
, id
) != 0) {
286 debug_print("Creating folder item: %s\n", id
);
288 vfolders
= g_list_prepend(vfolders
, vitem
);
292 return FOLDER_ITEM(vitem
);
295 static void vfolder_item_destroy(Folder
* folder
, FolderItem
* item
) {
296 VFolderItem
* vitem
= VFOLDER_ITEM(item
);
298 g_return_if_fail(vitem
!= NULL
);
300 debug_print("Number of elements in 'vfolders': %d\n", g_list_length(vfolders
));
301 vfolders
= g_list_remove(vfolders
, vitem
);
302 debug_print("Number of elements in 'vfolders': %d\n", g_list_length(vfolders
));
304 g_free(vitem
->filter
);
306 vfolder_remove_hashtables(vitem
);
307 vitem
->source
= NULL
;
313 static FolderItem
* vfolder_create_folder(Folder
* folder
,
317 FolderItem
* newitem
= NULL
;
319 g_return_val_if_fail(folder
!= NULL
, NULL
);
320 g_return_val_if_fail(parent
!= NULL
, NULL
);
321 g_return_val_if_fail(name
!= NULL
, NULL
);
326 path
= g_strconcat((parent
->path
!= NULL
) ? parent
->path
: "", ".",
328 newitem
= folder_item_new(folder
, name
, path
);
329 newitem
->no_sub
= TRUE
;
331 folder_item_append(parent
, newitem
);
337 static gchar
* vfolder_item_get_path(Folder
* folder
, FolderItem
* item
) {
339 result
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
, VFOLDER_DIR
,
340 G_DIR_SEPARATOR_S
, item
->name
, NULL
);
344 static gint
vfolder_get_num_list(Folder
* folder
, FolderItem
* item
,
345 MsgNumberList
** list
, gboolean
* old_uids_valid
) {
349 gint num
, nummsgs
= 0;
351 g_return_val_if_fail(item
!= NULL
, -1);
353 debug_print("VFolder: scanning '%s'...\n", item
->path
);
355 *old_uids_valid
= TRUE
;
357 path
= folder_item_get_path(item
);
358 g_return_val_if_fail(path
!= NULL
, -1);
359 if (change_dir(path
) < 0 ) {
365 if( (dp
= opendir(".")) == NULL
) {
366 FILE_OP_ERROR(item
->path
, "opendir");
370 while ((d
= readdir(dp
)) != NULL
) {
371 if( (num
= to_number(d
->d_name
)) > 0 ) {
372 debug_print("Adding %d to list\n", num
);
373 *list
= g_slist_prepend(*list
, GINT_TO_POINTER(num
));
382 static gint
vfolder_create_tree(Folder
* folder
) {
383 FolderItem
* rootitem
;
386 vfolder_make_rc_dir();
389 rootitem
= folder_item_new(folder
, folder
->name
, NULL
);
390 rootitem
->folder
= folder
;
391 rootnode
= g_node_new(rootitem
);
392 folder
->node
= rootnode
;
393 rootitem
->node
= rootnode
;
395 rootitem
= FOLDER_ITEM(folder
->node
->data
);
396 rootnode
= folder
->node
;
399 debug_print("VFolder: created new vfolder tree\n");
403 static gint
vfolder_scan_tree(Folder
*folder
) {
404 g_return_val_if_fail(folder
!= NULL
, -1);
406 folder
->outbox
= NULL
;
407 folder
->draft
= NULL
;
408 folder
->queue
= NULL
;
409 folder
->trash
= NULL
;
411 debug_print("VFolder: scanning tree\n");
412 vfolder_create_tree(folder
);
417 static gchar
* vfolder_get_new_msg_filename(FolderItem
* dest
) {
421 destpath
= folder_item_get_path(dest
);
422 g_return_val_if_fail(destpath
!= NULL
, NULL
);
424 if( !is_dir_exist(destpath
) )
425 make_dir_hier(destpath
);
428 destfile
= g_strdup_printf("%s%c%d", destpath
, G_DIR_SEPARATOR
,
430 if( is_file_entry_exist(destfile
) ) {
442 static gint
vfolder_add_msgs(Folder
* folder
, FolderItem
* dest
, GSList
* file_list
,
443 GHashTable
* relation
) {
446 MsgFileInfo
* fileinfo
;
450 g_return_val_if_fail(dest
!= NULL
, -1);
451 g_return_val_if_fail(file_list
!= NULL
, -1);
453 vitem
= VFOLDER_ITEM(dest
);
454 if( dest
->last_num
< 0 ) {
455 vfolder_get_last_num(folder
, dest
);
456 if( dest
->last_num
< 0 ) return -1;
459 for( cur
= file_list
; cur
!= NULL
; cur
= cur
->next
) {
460 fileinfo
= (MsgFileInfo
*)cur
->data
;
462 destfile
= vfolder_get_new_msg_filename(dest
);
467 if (! vitem
->deep_copy
)
468 r
= symlink(fileinfo
->file
, destfile
);
471 if (! vitem
->deep_copy
) {
472 g_warning("Requested sparse folder is not possible.\n"
473 "Using plain file copy instead\n");
476 if (copy_file(fileinfo
->file
, destfile
, TRUE
) < 0) {
477 g_warning("can't copy message %s to %s\n", fileinfo
->file
, destfile
);
482 vitem
->deep_copy
= TRUE
;
488 if( relation
!= NULL
)
489 g_hash_table_insert(relation
, fileinfo
,
490 GINT_TO_POINTER(dest
->last_num
+ 1) );
492 vfolder_add_msg_to_message_bridge(vitem
,
493 fileinfo
->msginfo
->msgnum
, dest
->last_num
+ 1);
495 vfolder_folder_item_props_write(VFOLDER_ITEM(dest
));
500 return (destfile
) ? dest
->last_num
: -1;
503 static gint
vfolder_add_msg(Folder
* folder
, FolderItem
* dest
, const gchar
* file
,
507 MsgFileInfo fileinfo
;
509 g_return_val_if_fail(file
!= NULL
, -1);
511 fileinfo
.msginfo
= NULL
;
512 fileinfo
.file
= (gchar
*)file
;
513 fileinfo
.flags
= flags
;
514 file_list
.data
= &fileinfo
;
515 file_list
.next
= NULL
;
517 ret
= vfolder_add_msgs(folder
, dest
, &file_list
, NULL
);
521 static gint
vfolder_copy_msgs(Folder
*folder
, FolderItem
*dest
,
522 MsgInfoList
*msglist
, GHashTable
*relation
) {
528 g_return_val_if_fail(dest
!= NULL
&& IS_VFOLDER_FOLDER_ITEM(dest
), -1);
530 if (! VFOLDER_ITEM(dest
)->deep_copy
)
533 g_return_val_if_fail(folder
!= NULL
, -1);
534 g_return_val_if_fail(msglist
!= NULL
, -1);
536 if( dest
->last_num
< 0 ) {
537 vfolder_get_last_num(folder
, dest
);
538 if( dest
->last_num
< 0 ) return -1;
541 msginfo
= (MsgInfo
*)msglist
->data
;
542 g_return_val_if_fail(msginfo
->folder
!= NULL
, -1);
544 statusbar_print_all(_("Copying messages..."));
546 src
= msginfo
->folder
;
547 if (src
->folder
!= dest
->folder
) {
548 GSList
*infolist
= NULL
, *cur
;
550 for (cur
= msglist
; cur
; cur
= cur
->next
) {
551 msginfo
= (MsgInfo
*)cur
->data
;
552 MsgFileInfo
*fileinfo
= g_new0(MsgFileInfo
, 1);
553 fileinfo
->file
= procmsg_get_message_file(msginfo
);
554 fileinfo
->flags
= &(msginfo
->flags
);
555 infolist
= g_slist_prepend(infolist
, fileinfo
);
557 infolist
= g_slist_reverse(infolist
);
558 res
= folder_item_add_msgs(dest
, infolist
, FALSE
);
559 for (cur
= infolist
; cur
; cur
= cur
->next
) {
560 MsgFileInfo
*info
= (MsgFileInfo
*)cur
->data
;
564 g_slist_free(infolist
);
569 for (cur
= msglist
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
570 MsgInfo
* msginfo
= (MsgInfo
*)cur
->data
;
572 /* put the local file in the folder cache, so that we don't
573 * have to fetch it back later. */
575 gchar
*cache_path
= folder_item_get_path(msginfo
->folder
);
576 debug_print("cache_path: %s\n", cache_path
);
577 gchar
*real_file
= g_strconcat(
578 cache_path
, G_DIR_SEPARATOR_S
,
579 itos(msginfo
->msgnum
), NULL
);
580 debug_print("real_file: %s\n", real_file
);
581 gchar
*cache_file
= NULL
;
583 cache_path
= folder_item_get_path(dest
);
584 debug_print("cache_path: %s\n", cache_path
);
585 cache_file
= vfolder_get_new_msg_filename(dest
);
586 debug_print("cache_file: %s\n", cache_file
);
587 if (!is_dir_exist(cache_path
))
588 make_dir_hier(cache_path
);
589 if (is_file_exist(real_file
) && is_dir_exist(cache_path
)) {
590 g_hash_table_insert(relation
, msginfo
, GINT_TO_POINTER(dest
->last_num
));
591 if (dest
->last_num
> last_num
)
592 last_num
= dest
->last_num
;
593 copy_file(real_file
, cache_file
, TRUE
);
594 debug_print("copied to cache: %s\n", cache_file
);
603 //dest->cache_dirty = 1;
604 //folder_item_update_thaw();
609 static gint
vfolder_copy_msg(Folder
* folder
, FolderItem
* dest
, MsgInfo
* msginfo
) {
612 g_return_val_if_fail(dest
!= NULL
&& IS_VFOLDER_FOLDER_ITEM(dest
), -1);
614 if (! VFOLDER_ITEM(dest
)->deep_copy
)
617 g_return_val_if_fail(folder
!= NULL
, -1);
618 g_return_val_if_fail(msginfo
!= NULL
, -1);
620 g_return_val_if_fail(msginfo
!= NULL
, -1);
622 msglist
.data
= msginfo
;
625 return vfolder_copy_msgs(folder
, dest
, &msglist
, NULL
);
628 static gchar
* vfolder_fetch_msg(Folder
* folder
, FolderItem
* item
, gint num
) {
629 gchar
* snum
= g_strdup_printf("%d", num
);
630 gchar
* file
= g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S
, VFOLDER_DIR
,
631 G_DIR_SEPARATOR_S
, item
->name
, G_DIR_SEPARATOR_S
, snum
, NULL
);
633 debug_print("VFolder: fetch_msg: '%s'\n", file
);
640 static MsgInfo
* vfolder_get_msginfo(Folder
* folder
, FolderItem
* item
, gint num
) {
641 MsgInfo
* msginfo
= NULL
;
645 debug_print("VFolder: get_msginfo: %d\n", num
);
647 g_return_val_if_fail(folder
!= NULL
, NULL
);
648 g_return_val_if_fail(item
!= NULL
, NULL
);
649 g_return_val_if_fail(num
> 0, NULL
);
651 file
= vfolder_fetch_msg(folder
, item
, num
);
652 if (! g_file_test(file
, G_FILE_TEST_EXISTS
)) {
657 flags
.perm_flags
= MSG_NEW
| MSG_UNREAD
;
660 msginfo
= procheader_parse_file(file
, flags
, TRUE
, TRUE
);
663 msginfo
->msgnum
= num
;
665 if (!msginfo
->folder
)
666 msginfo
->folder
= item
;
673 static gint
vfolder_remove_msg(Folder
* folder
, FolderItem
* item
, gint num
)
675 gboolean need_scan
= FALSE
;
678 g_return_val_if_fail(item
!= NULL
, -1);
680 file
= vfolder_fetch_msg(folder
, item
, num
);
681 g_return_val_if_fail(file
!= NULL
, -1);
683 need_scan
= vfolder_scan_required(folder
, item
);
685 /* are we doing a folder move ? */
686 tmp
= g_strdup_printf("%s.tmp", file
);
687 if (is_file_exist(tmp
)) {
695 if( claws_unlink(file
) < 0 ) {
696 FILE_OP_ERROR(file
, "unlink");
701 MsgInfo
* msginfo
= vfolder_find_msg_from_vfolder_num(VFOLDER_ITEM(item
), num
);
703 vfolder_remove_msg_from_bridge(VFOLDER_ITEM(item
), msginfo
->msgnum
, num
);
704 vfolder_folder_item_props_write(VFOLDER_ITEM(item
));
707 MsgBridge
* bridge
= g_hash_table_lookup(VFOLDER_ITEM(item
)->me_to_claws
, GUINT_TO_POINTER(num
));
709 vfolder_remove_msg_from_bridge(VFOLDER_ITEM(item
), bridge
->claws_num
, num
);
710 vfolder_folder_item_props_write(VFOLDER_ITEM(item
));
715 item
->mtime
= time(NULL
);
722 static gint
vfolder_remove_all_msg(Folder
* folder
, FolderItem
* item
) {
723 GSList
*msg_list
, *list
;
725 g_return_val_if_fail(item
!= NULL
, -1);
727 msg_list
= folder_item_get_msg_list(item
);
729 for (list
= msg_list
; list
; list
= g_slist_next(list
)) {
730 MsgInfo
* msginfo
= (MsgInfo
*) list
->data
;
731 folder_item_remove_msg(item
, msginfo
->msgnum
);
734 g_slist_free(msg_list
);
736 vfolder_remove_hashtables(VFOLDER_ITEM(item
));
741 static gboolean
vfolder_subscribe_uri(Folder
* folder
, const gchar
* uri
) {
745 static void vfolder_print_hash_table(GHashTable
* hash
) {
750 g_hash_table_iter_init (&iter
, hash
);
751 while (g_hash_table_iter_next(&iter
, &key
, &value
)) {
752 guint key
= GPOINTER_TO_UINT(key
);
753 MsgBridge
* bridge
= (MsgBridge
*) value
;
754 fprintf(stderr
, "key: %u ----- me: %u -> claws: %u\n",
755 key
, bridge
->my_num
, bridge
->claws_num
);
760 static MsgInfo
* vfolder_find_msg_from_num_rel(VFolderItem
* item
,
762 gboolean claws_to_me
) {
763 MsgInfo
* msginfo
= NULL
;
766 g_return_val_if_fail(item
!= NULL
, NULL
);
770 vfolder_print_hash_table(item
->claws_to_me
);
771 vfolder_print_hash_table(item
->me_to_claws
);
774 hash
= item
->claws_to_me
;
776 hash
= item
->me_to_claws
;
778 MsgBridge
* msgbridge
= g_hash_table_lookup(hash
, GUINT_TO_POINTER(msgnum
));
782 msginfo
= folder_item_get_msginfo(FOLDER_ITEM(item
), msgbridge
->my_num
);
784 msginfo
= folder_item_get_msginfo(item
->source
, msgbridge
->claws_num
);
790 gboolean
vfolder_replace_key_in_bridge(VFolderItem
* item
, guint from
, guint to
) {
791 gboolean ret
= FALSE
;
792 MsgBridge
*bridge
, *old
;
794 if (! item
->me_to_claws
|| ! item
->claws_to_me
)
797 old
= g_hash_table_lookup(item
->claws_to_me
, GUINT_TO_POINTER(from
));
801 debug_print("Replace key in hashtable: from->%u to->%u\n", from
, to
);
802 bridge
= vfolder_create_bridge(to
, old
->my_num
);
804 ret
= g_hash_table_remove(item
->me_to_claws
, GUINT_TO_POINTER(old
->my_num
));
809 ret
= g_hash_table_remove(item
->claws_to_me
, GUINT_TO_POINTER(old
->claws_num
));
811 bridge
->claws_num
= from
;
812 g_hash_table_replace(item
->me_to_claws
, GUINT_TO_POINTER(bridge
->my_num
), bridge
);
816 g_hash_table_replace(item
->claws_to_me
, GUINT_TO_POINTER(bridge
->claws_num
), bridge
);
817 g_hash_table_replace(item
->me_to_claws
, GUINT_TO_POINTER(bridge
->my_num
), bridge
);
822 gboolean
vfolder_add_message_to_bridge(VFolderItem
* item
, MsgBridge
* bridge
) {
823 if (!item
|| !bridge
)
826 return vfolder_add_msg_to_message_bridge(item
, bridge
->claws_num
, bridge
->my_num
);
829 MsgInfo
* vfolder_find_msg_from_claws_num(VFolderItem
* item
, guint msgnum
) {
830 return vfolder_find_msg_from_num_rel(item
, msgnum
, TRUE
);
833 MsgInfo
* vfolder_find_msg_from_vfolder_num(VFolderItem
* item
, guint msgnum
) {
834 return vfolder_find_msg_from_num_rel(item
, msgnum
, FALSE
);
837 GList
* vfolder_get_vfolder_items(void) {
838 return g_list_copy(vfolders
);
841 VFolderItem
* vfolder_get_vfolder_item(const gchar
* name
) {
842 VFolderItem
* item
= NULL
;
843 gchar
* folder_name
= NULL
;
844 gchar
* this_name
= NULL
;
845 GList
* list
= vfolders
;
848 folder_name
= g_strdup(VFOLDER_DEFAULT_MAILBOX
);
850 folder_name
= g_strdup(name
);
852 while (list
&& item
== NULL
) {
853 this_name
= FOLDER_ITEM(list
->data
)->name
;
854 if (this_name
&& strcmp(folder_name
, this_name
) == 0)
855 item
= VFOLDER_ITEM(list
->data
);
865 GList
* vfolder_get_vfolder_from_source(FolderItem
* source
) {
867 FolderItem
* src
= NULL
;
868 GList
* list
= vfolders
;
869 gchar
* src_id
= NULL
;
874 src_id
= folder_item_get_identifier(source
);
879 src
= VFOLDER_ITEM(list
->data
)->source
;
880 if (strcmp(folder_item_get_identifier(src
), src_id
) == 0) {
881 items
= g_list_prepend(items
, list
->data
);
889 typedef struct { GSList
* msgnumlist
; guint remove
; } RemoveInfo
;
890 static void find_msg_to_remove(gpointer key
, gpointer value
, gpointer user_data
) {
891 RemoveInfo
* remove_info
= (RemoveInfo
*) user_data
;
893 if (remove_info
->remove
== 0) {
894 if (g_slist_index(remove_info
->msgnumlist
, key
) < 0) {
895 MsgBridge
* bridge
= (MsgBridge
*) value
;
896 remove_info
->remove
= bridge
->my_num
;
901 static void vfolder_add_msg_to_folder(VFolderItem
* item
) {
902 GSList
*msgnumlist
, *cur
;
904 MsgFileInfo fileinfo
;
907 if (! item
->msg_filter_func
)
910 msgnumlist
= folder_item_get_number_list(item
->source
);
911 for (cur
= msgnumlist
; cur
; cur
= g_slist_next(cur
)) {
912 MsgBridge
* bridge
= g_hash_table_lookup(item
->claws_to_me
, cur
->data
);
914 msgs
.data
= folder_item_get_msginfo(item
->source
, GPOINTER_TO_UINT(cur
->data
));
916 MsgInfoList
* list
= item
->msg_filter_func(&msgs
, item
);
918 fileinfo
.msginfo
= list
->data
;
919 fileinfo
.file
= procmsg_get_message_file_path(fileinfo
.msginfo
);
920 fileinfo
.flags
= &fileinfo
.msginfo
->flags
;
921 file_list
.data
= &fileinfo
;
922 file_list
.next
= NULL
;
923 vfolder_add_msgs(item
->source
->folder
, FOLDER_ITEM(item
), &file_list
, NULL
);
924 FOLDER_ITEM(item
)->update_flags
= F_ITEM_UPDATE_ADDMSG
;
929 g_slist_free(msgnumlist
);
932 static void vfolder_remove_msg_from_folder(VFolderItem
* item
) {
933 RemoveInfo remove_info
;
934 remove_info
.msgnumlist
= folder_item_get_number_list(item
->source
);
935 remove_info
.remove
= 0;
936 g_hash_table_foreach(item
->claws_to_me
, find_msg_to_remove
, &remove_info
);
937 vfolder_remove_msg(FOLDER_ITEM(item
)->folder
, FOLDER_ITEM(item
), remove_info
.remove
);
938 g_slist_free(remove_info
.msgnumlist
);
939 FOLDER_ITEM(item
)->update_flags
= F_ITEM_UPDATE_REMOVEMSG
;
942 void vfolder_folder_item_update_msgs(VFolderItem
* item
, FolderItemUpdateFlags flag
) {
945 if (item
->frozen
|| item
->updating
)
948 if (item
->claws_to_me
) {
949 item
->updating
= TRUE
;
951 case F_ITEM_UPDATE_MSGCNT
:
953 case F_ITEM_UPDATE_CONTENT
:
954 c
= g_hash_table_size(item
->claws_to_me
);
955 if (c
< item
->source
->total_msgs
) {
957 vfolder_add_msg_to_folder(item
);
959 else if (c
> item
->source
->total_msgs
) {
960 /* remove messages */
961 vfolder_remove_msg_from_folder(item
);
964 case F_ITEM_UPDATE_ADDMSG
:
965 vfolder_add_msg_to_folder(item
);
967 case F_ITEM_UPDATE_REMOVEMSG
:
968 vfolder_remove_msg_from_folder(item
);
970 case F_ITEM_UPDATE_NAME
:
973 item
->updating
= FALSE
;
977 FolderClass
* vfolder_folder_get_class() {
978 if (vfolder_class
.idstr
== NULL
) {
979 vfolder_class
.type
= F_UNKNOWN
;
980 vfolder_class
.idstr
= "vfolder";
981 vfolder_class
.uistr
= "VFolder";
983 /* Folder functions */
984 vfolder_class
.new_folder
= vfolder_new_folder
;
985 vfolder_class
.destroy_folder
= vfolder_destroy_folder
;
986 vfolder_class
.set_xml
= folder_set_xml
;
987 vfolder_class
.get_xml
= folder_get_xml
;
988 vfolder_class
.scan_tree
= vfolder_scan_tree
;
989 vfolder_class
.create_tree
= vfolder_create_tree
;
991 /* FolderItem functions */
992 vfolder_class
.item_new
= vfolder_item_new
;
993 vfolder_class
.item_destroy
= vfolder_item_destroy
;
994 vfolder_class
.item_get_path
= vfolder_item_get_path
;
995 vfolder_class
.create_folder
= vfolder_create_folder
;
996 vfolder_class
.rename_folder
= NULL
;
997 vfolder_class
.remove_folder
= vfolder_remove_folder
;
998 vfolder_class
.get_num_list
= vfolder_get_num_list
;
999 vfolder_class
.scan_required
= vfolder_scan_required
;
1001 /* Message functions */
1002 vfolder_class
.get_msginfo
= vfolder_get_msginfo
;
1003 vfolder_class
.fetch_msg
= vfolder_fetch_msg
;
1004 vfolder_class
.copy_msgs
= vfolder_copy_msgs
;
1005 vfolder_class
.copy_msg
= vfolder_copy_msg
;
1006 vfolder_class
.add_msg
= vfolder_add_msg
;
1007 vfolder_class
.add_msgs
= vfolder_add_msgs
;
1008 vfolder_class
.remove_msg
= vfolder_remove_msg
;
1009 vfolder_class
.remove_msgs
= NULL
;
1010 vfolder_class
.remove_all_msg
= vfolder_remove_all_msg
;
1011 vfolder_class
.change_flags
= NULL
;
1012 vfolder_class
.subscribe
= vfolder_subscribe_uri
;
1013 debug_print("VFolder: registered folderclass\n");
1016 return &vfolder_class
;
1019 /* Local functions */
1021 gboolean
vfolder_init(void) {
1022 gchar
* error
= g_new0(gchar
, 1);
1024 folder_register_class(vfolder_folder_get_class());
1026 if (! vfolder_gtk_init(&error
)) {
1027 alertpanel_error("%s", error
);
1032 folder_func_to_all_folders((FolderItemFunc
)vfolder_init_read_func
, NULL
);
1034 if (existing_tree_found
== FALSE
)
1035 vfolder_create_default_mailbox();
1040 void vfolder_done(void) {
1044 if (!claws_is_exiting())
1045 folder_unregister_class(vfolder_folder_get_class());