1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2020 the Claws Mail team and Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #ifdef HAVE_CONFIG_H
20 #  include "config.h"
21 #include "claws-features.h"
22 #endif
23 
24 #include "defs.h"
25 
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <stdlib.h>
34 #ifdef WIN32
35 #include <w32lib.h>
36 #endif
37 
38 #include "alertpanel.h"
39 #include "folder.h"
40 #include "session.h"
41 #include "inc.h"
42 #include "imap.h"
43 #include "news.h"
44 #include "mh.h"
45 #include "utils.h"
46 #include "xml.h"
47 #include "codeconv.h"
48 #include "prefs_gtk.h"
49 #include "account.h"
50 #include "filtering.h"
51 #include "procheader.h"
52 #include "hooks.h"
53 #include "log.h"
54 #include "folder_item_prefs.h"
55 #include "remotefolder.h"
56 #include "partial_download.h"
57 #include "statusbar.h"
58 #include "gtkutils.h"
59 #include "timing.h"
60 #include "compose.h"
61 #include "main.h"
62 #include "msgcache.h"
63 #include "privacy.h"
64 #include "prefs_common.h"
65 #include "prefs_migration.h"
66 #include "file-utils.h"
67 
68 /* Dependecies to be removed ?! */
69 #include "prefs_account.h"
70 
71 /* Define possible missing constants for Windows. */
72 #ifdef G_OS_WIN32
73 # ifndef S_IRGRP
74 # define S_IRGRP 0
75 # define S_IWGRP 0
76 # endif
77 # ifndef S_IROTH
78 # define S_IROTH 0
79 # define S_IWOTH 0
80 # endif
81 #endif
82 
83 static GList *folder_list = NULL;
84 static GSList *class_list = NULL;
85 static GSList *folder_unloaded_list = NULL;
86 
87 void folder_init		(Folder		*folder,
88 				 const gchar	*name);
89 
90 static gchar *folder_item_get_cache_file	(FolderItem	*item);
91 static gchar *folder_item_get_mark_file	(FolderItem	*item);
92 static gchar *folder_item_get_tags_file	(FolderItem	*item);
93 static GNode *folder_get_xml_node	(Folder 	*folder);
94 static Folder *folder_get_from_xml	(GNode 		*node);
95 static void folder_update_op_count_rec	(GNode		*node);
96 
97 
98 static void folder_get_persist_prefs_recursive
99 					(GNode *node, GHashTable *pptable);
100 static gboolean persist_prefs_free	(gpointer key, gpointer val, gpointer data);
101 static void folder_item_read_cache		(FolderItem *item);
102 gint folder_item_scan_full		(FolderItem *item, gboolean filtering);
103 static void folder_item_update_with_msg (FolderItem *item, FolderItemUpdateFlags update_flags,
104                                          MsgInfo *msg);
105 static GHashTable *folder_persist_prefs_new	(Folder *folder);
106 static void folder_persist_prefs_free		(GHashTable *pptable);
107 static void folder_item_restore_persist_prefs	(FolderItem *item, GHashTable *pptable);
108 
folder_system_init(void)109 void folder_system_init(void)
110 {
111 	folder_register_class(mh_get_class());
112 	folder_register_class(imap_get_class());
113 	folder_register_class(news_get_class());
114 }
115 
folder_get_class_list(void)116 static GSList *folder_get_class_list(void)
117 {
118 	return class_list;
119 }
120 
folder_register_class(FolderClass * klass)121 void folder_register_class(FolderClass *klass)
122 {
123 	GSList *xmllist, *cur;
124 
125 	debug_print("registering folder class %s\n", klass->idstr);
126 
127 	class_list = g_slist_append(class_list, klass);
128 
129 	xmllist = g_slist_copy(folder_unloaded_list);
130 	for (cur = xmllist; cur != NULL; cur = g_slist_next(cur)) {
131 		GNode *node = (GNode *) cur->data;
132 		XMLNode *xmlnode = (XMLNode *) node->data;
133 		GList *cur = xmlnode->tag->attr;
134 
135 		for (; cur != NULL; cur = g_list_next(cur)) {
136 			XMLAttr *attr = (XMLAttr *) cur->data;
137 
138 			if (!attr || !attr->name || !attr->value) continue;
139 			if (!strcmp(attr->name, "type") && !strcmp(attr->value, klass->idstr)) {
140 				Folder *folder;
141 
142 				folder = folder_get_from_xml(node);
143 				if (folder) {
144 					folder_add(folder);
145 					folder_unloaded_list = g_slist_remove(folder_unloaded_list, node);
146 					xml_free_tree(node);
147 				}
148 				cur = NULL;
149 				continue;
150 			}
151 		}
152 	}
153 	g_slist_free(xmllist);
154 }
155 
folder_unregister_class(FolderClass * klass)156 void folder_unregister_class(FolderClass *klass)
157 {
158 	GList *folderlist, *cur;
159 
160 	debug_print("unregistering folder class %s\n", klass->idstr);
161 
162 	class_list = g_slist_remove(class_list, klass);
163 
164 	folderlist = g_list_copy(folder_get_list());
165 	for (cur = folderlist; cur != NULL; cur = g_list_next(cur)) {
166 		Folder *folder = (Folder *) cur->data;
167 
168 		if (folder->klass == klass) {
169 			GNode *xmlnode = folder_get_xml_node(folder);
170 			folder_unloaded_list = g_slist_append(folder_unloaded_list, xmlnode);
171 			folder_destroy(folder);
172 		}
173 	}
174 	g_list_free(folderlist);
175 
176 	if (klass->prefs_pages)
177 		g_slist_free(klass->prefs_pages);
178 }
179 
folder_new(FolderClass * klass,const gchar * name,const gchar * path)180 Folder *folder_new(FolderClass *klass, const gchar *name, const gchar *path)
181 {
182 	Folder *folder = NULL;
183 	FolderItem *item;
184 
185 	cm_return_val_if_fail(klass != NULL, NULL);
186 
187 	name = name ? name : path;
188 	folder = klass->new_folder(name, path);
189 
190 	/* Create root folder item */
191 	item = folder_item_new(folder, name, NULL);
192 	if (item == NULL) {
193 		return NULL;
194 	}
195 	item->folder = folder;
196 	folder->node = item->node;
197 	folder->data = NULL;
198 
199 	return folder;
200 }
201 
folder_init(Folder * folder,const gchar * name)202 void folder_init(Folder *folder, const gchar *name)
203 {
204 	cm_return_if_fail(folder != NULL);
205 
206 	folder_set_name(folder, name);
207 
208 	/* Init folder data */
209 	folder->account = NULL;
210 	folder->sort = 0;
211 	folder->inbox = NULL;
212 	folder->outbox = NULL;
213 	folder->draft = NULL;
214 	folder->queue = NULL;
215 	folder->trash = NULL;
216 }
217 
reset_parent_type(FolderItem * item,gpointer data)218 static void reset_parent_type(FolderItem *item, gpointer data) {
219 	item->parent_stype = -1;
220 }
221 
folder_item_change_type(FolderItem * item,SpecialFolderItemType newtype)222 void folder_item_change_type(FolderItem *item, SpecialFolderItemType newtype)
223 {
224 	Folder *folder = NULL;
225 	FolderUpdateData hookdata;
226 
227 	if (item == NULL)
228 		return;
229 
230 	folder = item->folder;
231 	/* unset previous root of newtype */
232 	switch(newtype) {
233 	case F_INBOX:
234 		folder_item_change_type(folder->inbox, F_NORMAL);
235 		folder->inbox = item;
236 		break;
237 	case F_OUTBOX:
238 		folder_item_change_type(folder->outbox, F_NORMAL);
239 		folder->outbox = item;
240 		break;
241 	case F_QUEUE:
242 		folder_item_change_type(folder->queue, F_NORMAL);
243 		folder->queue = item;
244 		break;
245 	case F_DRAFT:
246 		folder_item_change_type(folder->draft, F_NORMAL);
247 		folder->draft = item;
248 		break;
249 	case F_TRASH:
250 		folder_item_change_type(folder->trash, F_NORMAL);
251 		folder->trash = item;
252 		break;
253 	case F_NORMAL:
254 	default:
255 		break;
256 	}
257 	/* set new type for current folder and sons */
258 	item->stype = newtype;
259 	folder_func_to_all_folders(reset_parent_type, NULL);
260 
261 	hookdata.folder = folder;
262 	hookdata.update_flags = FOLDER_TREE_CHANGED;
263 	hookdata.item = NULL;
264 	hookdata.item2 = NULL;
265 	hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
266 }
267 
folder_destroy(Folder * folder)268 void folder_destroy(Folder *folder)
269 {
270 	cm_return_if_fail(folder != NULL);
271 	cm_return_if_fail(folder->klass->destroy_folder != NULL);
272 
273 	folder_remove(folder);
274 
275 	folder_tree_destroy(folder);
276 
277 	folder->klass->destroy_folder(folder);
278 
279 	g_free(folder->name);
280 	g_free(folder);
281 }
282 
folder_set_xml(Folder * folder,XMLTag * tag)283 void folder_set_xml(Folder *folder, XMLTag *tag)
284 {
285 	GList *cur;
286 	FolderItem *rootitem = NULL;
287 
288 	if ((folder->node != NULL) && (folder->node->data != NULL))
289 		rootitem = (FolderItem *) folder->node->data;
290 
291 	for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
292 		XMLAttr *attr = (XMLAttr *) cur->data;
293 
294 		if (!attr || !attr->name || !attr->value) continue;
295 		if (!strcmp(attr->name, "name")) {
296 			g_free(folder->name);
297 			folder->name = g_strdup(attr->value);
298 			if (rootitem != NULL) {
299 				g_free(rootitem->name);
300 				rootitem->name = g_strdup(attr->value);
301 			}
302 		} else if (!strcmp(attr->name, "account_id")) {
303 			PrefsAccount *account;
304 
305 			account = account_find_from_id(atoi(attr->value));
306 			if (!account)
307 				g_warning("account_id: %s not found", attr->value);
308 			else {
309 				folder->account = account;
310 				account->folder = folder;
311 			}
312 		} else if (!strcmp(attr->name, "collapsed")) {
313 			if (rootitem != NULL)
314 				rootitem->collapsed = *attr->value == '1' ? TRUE : FALSE;
315 		} else if (!strcmp(attr->name, "sort")) {
316 			folder->sort = atoi(attr->value);
317 		}
318 	}
319 }
320 
folder_get_xml(Folder * folder)321 XMLTag *folder_get_xml(Folder *folder)
322 {
323 	XMLTag *tag;
324 
325 	tag = xml_tag_new("folder");
326 
327 	if (folder->name)
328 		xml_tag_add_attr(tag, xml_attr_new("name", folder->name));
329 	if (folder->account)
330 		xml_tag_add_attr(tag, xml_attr_new_int("account_id", folder->account->account_id));
331 	if (folder->node && folder->node->data) {
332 		FolderItem *rootitem = (FolderItem *) folder->node->data;
333 
334 		xml_tag_add_attr(tag, xml_attr_new("collapsed", rootitem->collapsed ? "1" : "0"));
335 	}
336 	xml_tag_add_attr(tag, xml_attr_new_int("sort", folder->sort));
337 
338 	return tag;
339 }
340 
folder_item_new(Folder * folder,const gchar * name,const gchar * path)341 FolderItem *folder_item_new(Folder *folder, const gchar *name, const gchar *path)
342 {
343 	FolderItem *item = NULL;
344 
345 	cm_return_val_if_fail(folder != NULL, NULL);
346 
347 	if (folder->klass->item_new) {
348 		item = folder->klass->item_new(folder);
349 	} else {
350 		item = g_new0(FolderItem, 1);
351 	}
352 
353 	cm_return_val_if_fail(item != NULL, NULL);
354 
355 	item->stype = F_NORMAL;
356 
357 	if(!g_utf8_validate(name, -1, NULL)) {
358 		item->name = g_malloc(strlen(name)*2+1);
359 		conv_localetodisp(item->name, strlen(name)*2+1, name);
360 	} else {
361 		item->name = g_strdup(name);
362 	}
363 
364 	item->path = g_strdup(path);
365 	item->mtime = 0;
366 	item->new_msgs = 0;
367 	item->unread_msgs = 0;
368 	item->unreadmarked_msgs = 0;
369 	item->marked_msgs = 0;
370 	item->total_msgs = 0;
371 	item->replied_msgs = 0;
372 	item->forwarded_msgs = 0;
373 	item->locked_msgs = 0;
374 	item->ignored_msgs = 0;
375 	item->watched_msgs = 0;
376 	item->order = 0;
377 	item->last_num = -1;
378 	item->cache = NULL;
379 	item->no_sub = FALSE;
380 	item->no_select = FALSE;
381 	item->collapsed = FALSE;
382 	item->thread_collapsed = prefs_common.folder_default_thread_collapsed;
383 	item->threaded  = prefs_common.folder_default_thread;
384 	item->hide_read_threads = prefs_common.folder_default_hide_read_threads;
385 	item->hide_read_msgs = prefs_common.folder_default_hide_read_msgs;
386 	item->hide_del_msgs = prefs_common.folder_default_hide_del_msgs;
387 	item->ret_rcpt  = FALSE;
388 	item->opened    = FALSE;
389 	item->node = g_node_new(item);
390 	item->folder = NULL;
391 	item->account = NULL;
392 	item->apply_sub = FALSE;
393 	item->mark_queue = NULL;
394 	item->data = NULL;
395 	item->parent_stype = -1;
396 
397 	item->sort_key = prefs_common.default_sort_key;
398 	item->sort_type = prefs_common.default_sort_type;
399 
400 	item->prefs = folder_item_prefs_new();
401 
402 	return item;
403 }
404 
folder_item_append(FolderItem * parent,FolderItem * item)405 void folder_item_append(FolderItem *parent, FolderItem *item)
406 {
407 	cm_return_if_fail(parent != NULL);
408 	cm_return_if_fail(parent->folder != NULL);
409 	cm_return_if_fail(parent->node != NULL);
410 	cm_return_if_fail(item != NULL);
411 
412 	item->folder = parent->folder;
413 	g_node_append(parent->node, item->node);
414 }
415 
folder_item_remove(FolderItem * item)416 void folder_item_remove(FolderItem *item)
417 {
418 	GNode *node, *start_node;
419 	FolderUpdateData hookdata;
420 	gchar *tags_file = NULL, *tags_dir = NULL;
421 
422 	cm_return_if_fail(item != NULL);
423 	cm_return_if_fail(item->folder != NULL);
424 	cm_return_if_fail(item->folder->node != NULL);
425 
426 	start_node = item->node;
427 
428 	node = item->folder->node;
429 
430 	node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
431 	node = node->children;
432 
433 	/* remove my children */
434 	while (node != NULL) {
435 		if (node && node->data) {
436 			FolderItem *sub_item = (FolderItem*) node->data;
437 			node = node->next;
438 			folder_item_remove(sub_item);
439 		}
440 	}
441 
442 	/* remove myself */
443 	if (item->cache != NULL) {
444 		msgcache_destroy(item->cache);
445 		item->cache = NULL;
446 	}
447 	tags_file = folder_item_get_tags_file(item);
448 	if (tags_file)
449 		claws_unlink(tags_file);
450 	tags_dir = g_path_get_dirname(tags_file);
451 	if (tags_dir)
452 		rmdir(tags_dir);
453 
454 	g_free(tags_file);
455 	g_free(tags_dir);
456 
457 	hookdata.folder = item->folder;
458 	hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_REMOVE_FOLDERITEM;
459 	hookdata.item = item;
460 	hookdata.item2 = NULL;
461 	hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
462 
463 	node = start_node;
464 
465 	if (item->folder->node == node)
466 		item->folder->node = NULL;
467 
468 	folder_item_destroy(item);
469 
470 	g_node_destroy(node);
471 }
472 
folder_item_remove_children(FolderItem * item)473 void folder_item_remove_children(FolderItem *item)
474 {
475 	GNode *node, *next;
476 
477 	cm_return_if_fail(item != NULL);
478 	cm_return_if_fail(item->folder != NULL);
479 	cm_return_if_fail(item->node != NULL);
480 
481 	node = item->node->children;
482 	while (node != NULL) {
483 		next = node->next;
484 		folder_item_remove(FOLDER_ITEM(node->data));
485 		node = next;
486 	}
487 }
488 
folder_item_destroy(FolderItem * item)489 void folder_item_destroy(FolderItem *item)
490 {
491 	Folder *folder;
492 
493 	cm_return_if_fail(item != NULL);
494 
495 	folder = item->folder;
496 	if (folder) {
497 		if (folder->inbox == item)
498 			folder->inbox = NULL;
499 		else if (folder->outbox == item)
500 			folder->outbox = NULL;
501 		else if (folder->draft == item)
502 			folder->draft = NULL;
503 		else if (folder->queue == item)
504 			folder->queue = NULL;
505 		else if (folder->trash == item)
506 			folder->trash = NULL;
507 	}
508 
509 	if (item->cache)
510 		folder_item_free_cache(item, TRUE);
511 	if (item->prefs)
512 		folder_item_prefs_free(item->prefs);
513 	g_free(item->name);
514 	g_free(item->path);
515 
516 	if (item->folder != NULL) {
517 		if(item->folder->klass->item_destroy) {
518 			item->folder->klass->item_destroy(item->folder, item);
519 		} else {
520 			g_free(item);
521 		}
522 	}
523 }
524 
folder_item_parent(FolderItem * item)525 FolderItem *folder_item_parent(FolderItem *item)
526 {
527 	cm_return_val_if_fail(item != NULL, NULL);
528 	cm_return_val_if_fail(item->node != NULL, NULL);
529 
530 	if (item->node->parent == NULL)
531 		return NULL;
532 	return (FolderItem *) item->node->parent->data;
533 }
534 
folder_item_set_xml(Folder * folder,FolderItem * item,XMLTag * tag)535 void folder_item_set_xml(Folder *folder, FolderItem *item, XMLTag *tag)
536 {
537 	GList *cur;
538 
539 	for (cur = tag->attr; cur != NULL; cur = g_list_next(cur)) {
540 		XMLAttr *attr = (XMLAttr *) cur->data;
541 
542 		if (!attr || !attr->name || !attr->value) continue;
543 		if (!strcmp(attr->name, "type")) {
544 			if (!g_ascii_strcasecmp(attr->value, "normal"))
545 				item->stype = F_NORMAL;
546 			else if (!g_ascii_strcasecmp(attr->value, "inbox"))
547 				item->stype = F_INBOX;
548 			else if (!g_ascii_strcasecmp(attr->value, "outbox"))
549 				item->stype = F_OUTBOX;
550 			else if (!g_ascii_strcasecmp(attr->value, "draft"))
551 				item->stype = F_DRAFT;
552 			else if (!g_ascii_strcasecmp(attr->value, "queue"))
553 				item->stype = F_QUEUE;
554 			else if (!g_ascii_strcasecmp(attr->value, "trash"))
555 				item->stype = F_TRASH;
556 		} else if (!strcmp(attr->name, "name")) {
557 			g_free(item->name);
558 			item->name = g_strdup(attr->value);
559 		} else if (!strcmp(attr->name, "path")) {
560 			g_free(item->path);
561 			item->path = g_strdup(attr->value);
562 		} else if (!strcmp(attr->name, "mtime"))
563 			item->mtime = strtoul(attr->value, NULL, 10);
564 		else if (!strcmp(attr->name, "new"))
565 			item->new_msgs = atoi(attr->value);
566 		else if (!strcmp(attr->name, "unread"))
567 			item->unread_msgs = atoi(attr->value);
568 		else if (!strcmp(attr->name, "unreadmarked"))
569 			item->unreadmarked_msgs = atoi(attr->value);
570 		else if (!strcmp(attr->name, "marked"))
571 			item->marked_msgs = atoi(attr->value);
572 		else if (!strcmp(attr->name, "replied"))
573 			item->replied_msgs = atoi(attr->value);
574 		else if (!strcmp(attr->name, "forwarded"))
575 			item->forwarded_msgs = atoi(attr->value);
576 		else if (!strcmp(attr->name, "locked"))
577 			item->locked_msgs = atoi(attr->value);
578 		else if (!strcmp(attr->name, "ignored"))
579 			item->ignored_msgs = atoi(attr->value);
580 		else if (!strcmp(attr->name, "watched"))
581 			item->watched_msgs = atoi(attr->value);
582 		else if (!strcmp(attr->name, "order"))
583 			item->order = atoi(attr->value);
584 		else if (!strcmp(attr->name, "total"))
585 			item->total_msgs = atoi(attr->value);
586 		else if (!strcmp(attr->name, "no_sub"))
587 			item->no_sub = *attr->value == '1' ? TRUE : FALSE;
588 		else if (!strcmp(attr->name, "no_select"))
589 			item->no_select = *attr->value == '1' ? TRUE : FALSE;
590 		else if (!strcmp(attr->name, "collapsed"))
591 			item->collapsed = *attr->value == '1' ? TRUE : FALSE;
592 		else if (!strcmp(attr->name, "thread_collapsed"))
593 			item->thread_collapsed =  *attr->value == '1' ? TRUE : FALSE;
594 		else if (!strcmp(attr->name, "threaded"))
595 			item->threaded =  *attr->value == '1' ? TRUE : FALSE;
596 		else if (!strcmp(attr->name, "hidereadmsgs"))
597 			item->hide_read_msgs =  *attr->value == '1' ? TRUE : FALSE;
598 		else if (!strcmp(attr->name, "hidedelmsgs"))
599 			item->hide_del_msgs =  *attr->value == '1' ? TRUE : FALSE;
600 		else if (!strcmp(attr->name, "hidereadthreads"))
601 			item->hide_read_threads =  *attr->value == '1' ? TRUE : FALSE;
602 		else if (!strcmp(attr->name, "reqretrcpt"))
603 			item->ret_rcpt =  *attr->value == '1' ? TRUE : FALSE;
604 		else if (!strcmp(attr->name, "sort_key")) {
605 			if (!strcmp(attr->value, "none"))
606 				item->sort_key = SORT_BY_NONE;
607 			else if (!strcmp(attr->value, "number"))
608 				item->sort_key = SORT_BY_NUMBER;
609 			else if (!strcmp(attr->value, "size"))
610 				item->sort_key = SORT_BY_SIZE;
611 			else if (!strcmp(attr->value, "date"))
612 				item->sort_key = SORT_BY_DATE;
613 			else if (!strcmp(attr->value, "from"))
614 				item->sort_key = SORT_BY_FROM;
615 			else if (!strcmp(attr->value, "subject"))
616 				item->sort_key = SORT_BY_SUBJECT;
617 			else if (!strcmp(attr->value, "score"))
618 				item->sort_key = SORT_BY_SCORE;
619 			else if (!strcmp(attr->value, "label"))
620 				item->sort_key = SORT_BY_LABEL;
621 			else if (!strcmp(attr->value, "mark"))
622 				item->sort_key = SORT_BY_MARK;
623 			else if (!strcmp(attr->value, "unread"))
624 				item->sort_key = SORT_BY_STATUS;
625 			else if (!strcmp(attr->value, "mime"))
626 				item->sort_key = SORT_BY_MIME;
627 			else if (!strcmp(attr->value, "to"))
628 				item->sort_key = SORT_BY_TO;
629 			else if (!strcmp(attr->value, "locked"))
630 				item->sort_key = SORT_BY_LOCKED;
631 			else if (!strcmp(attr->value, "tags"))
632 				item->sort_key = SORT_BY_TAGS;
633 			else if (!strcmp(attr->value, "thread_date"))
634 				item->sort_key = SORT_BY_THREAD_DATE;
635 		} else if (!strcmp(attr->name, "sort_type")) {
636 			if (!strcmp(attr->value, "ascending"))
637 				item->sort_type = SORT_ASCENDING;
638 			else
639 				item->sort_type = SORT_DESCENDING;
640 		} else if (!strcmp(attr->name, "account_id")) {
641 			PrefsAccount *account;
642 
643 			account = account_find_from_id(atoi(attr->value));
644 			if (!account)
645 				g_warning("account_id: %s not found", attr->value);
646 			else
647 				item->account = account;
648 		} else if (!strcmp(attr->name, "apply_sub")) {
649 			item->apply_sub = *attr->value == '1' ? TRUE : FALSE;
650 		} else if (!strcmp(attr->name, "last_seen")) {
651 			if (!claws_crashed())
652 				item->last_seen = atoi(attr->value);
653 			else
654 				item->last_seen = 0;
655 		}
656 	}
657 	/* options without meaning in drafts */
658 	if (item->stype == F_DRAFT)
659 		item->hide_read_msgs =
660 			item->hide_del_msgs =
661 				item->hide_read_threads = FALSE;
662 }
663 
folder_item_get_xml(Folder * folder,FolderItem * item)664 XMLTag *folder_item_get_xml(Folder *folder, FolderItem *item)
665 {
666 	static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
667 						 "draft", "queue", "trash"};
668 	static gchar *sort_key_str[] = {"none", "number", "size", "date",
669 					"from", "subject", "score", "label",
670 					"mark", "unread", "mime", "to",
671 					"locked", "tags", "thread_date" };
672 	XMLTag *tag;
673 	gchar *value;
674 
675 	tag = xml_tag_new("folderitem");
676 
677 	xml_tag_add_attr(tag, xml_attr_new("type", folder_item_stype_str[item->stype]));
678 	if (item->name)
679 		xml_tag_add_attr(tag, xml_attr_new("name", item->name));
680 	if (item->path)
681 		xml_tag_add_attr(tag, xml_attr_new("path", item->path));
682 	if (item->no_sub)
683 		xml_tag_add_attr(tag, xml_attr_new("no_sub", "1"));
684 	if (item->no_select)
685 		xml_tag_add_attr(tag, xml_attr_new("no_select", "1"));
686 	xml_tag_add_attr(tag, xml_attr_new("collapsed", item->collapsed && item->node->children ? "1" : "0"));
687 	xml_tag_add_attr(tag, xml_attr_new("thread_collapsed", item->thread_collapsed ? "1" : "0"));
688 	xml_tag_add_attr(tag, xml_attr_new("threaded", item->threaded ? "1" : "0"));
689 	xml_tag_add_attr(tag, xml_attr_new("hidereadmsgs", item->hide_read_msgs ? "1" : "0"));
690 	xml_tag_add_attr(tag, xml_attr_new("hidedelmsgs", item->hide_del_msgs ? "1" : "0"));
691 	xml_tag_add_attr(tag, xml_attr_new("hidereadthreads", item->hide_read_threads ? "1" : "0"));
692 	if (item->ret_rcpt)
693 		xml_tag_add_attr(tag, xml_attr_new("reqretrcpt", "1"));
694 
695 	if (item->sort_key != SORT_BY_NONE) {
696 		xml_tag_add_attr(tag, xml_attr_new("sort_key", sort_key_str[item->sort_key]));
697 		xml_tag_add_attr(tag, xml_attr_new("sort_type", item->sort_type == SORT_ASCENDING ? "ascending" : "descending"));
698 	}
699 
700 	value = g_strdup_printf("%ld", (unsigned long int) item->mtime);
701 	xml_tag_add_attr(tag, xml_attr_new("mtime", value));
702 	g_free(value);
703 	xml_tag_add_attr(tag, xml_attr_new_int("new", item->new_msgs));
704 	xml_tag_add_attr(tag, xml_attr_new_int("unread", item->unread_msgs));
705 	xml_tag_add_attr(tag, xml_attr_new_int("unreadmarked", item->unreadmarked_msgs));
706 	xml_tag_add_attr(tag, xml_attr_new_int("marked", item->marked_msgs));
707 	xml_tag_add_attr(tag, xml_attr_new_int("total", item->total_msgs));
708 	xml_tag_add_attr(tag, xml_attr_new_int("replied", item->replied_msgs));
709 	xml_tag_add_attr(tag, xml_attr_new_int("forwarded", item->forwarded_msgs));
710 	xml_tag_add_attr(tag, xml_attr_new_int("locked", item->locked_msgs));
711 	xml_tag_add_attr(tag, xml_attr_new_int("ignore", item->ignored_msgs));
712 	xml_tag_add_attr(tag, xml_attr_new_int("watched", item->watched_msgs));
713 	xml_tag_add_attr(tag, xml_attr_new_int("order", item->order));
714 
715 	if (item->account)
716 		xml_tag_add_attr(tag, xml_attr_new_int("account_id", item->account->account_id));
717 	if (item->apply_sub)
718 		xml_tag_add_attr(tag, xml_attr_new("apply_sub", "1"));
719 
720 	xml_tag_add_attr(tag, xml_attr_new_int("last_seen", item->last_seen));
721 
722 	return tag;
723 }
724 
folder_set_ui_func(Folder * folder,FolderUIFunc func,gpointer data)725 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
726 {
727 	cm_return_if_fail(folder != NULL);
728 
729 	folder->ui_func = func;
730 	folder->ui_func_data = data;
731 }
732 
folder_set_name(Folder * folder,const gchar * name)733 void folder_set_name(Folder *folder, const gchar *name)
734 {
735 	cm_return_if_fail(folder != NULL);
736 
737 	g_free(folder->name);
738 	folder->name = name ? g_strdup(name) : NULL;
739 	if (folder->node && folder->node->data) {
740 		FolderItem *item = (FolderItem *)folder->node->data;
741 
742 		g_free(item->name);
743 		item->name = name ? g_strdup(name) : NULL;
744 	}
745 }
746 
folder_set_sort(Folder * folder,guint sort)747 void folder_set_sort(Folder *folder, guint sort)
748 {
749 	cm_return_if_fail(folder != NULL);
750 
751 	if (folder->sort != sort) {
752 		folder_remove(folder);
753 		folder->sort = sort;
754 		folder_add(folder);
755 	}
756 }
757 
folder_tree_destroy_func(GNode * node,gpointer data)758 static gboolean folder_tree_destroy_func(GNode *node, gpointer data) {
759 	FolderItem *item = (FolderItem *) node->data;
760 
761 	folder_item_destroy(item);
762 	return FALSE;
763 }
764 
folder_tree_destroy(Folder * folder)765 void folder_tree_destroy(Folder *folder)
766 {
767 	GNode *node;
768 
769 	cm_return_if_fail(folder != NULL);
770 
771 	node = folder->node;
772 
773 	prefs_filtering_clear_folder(folder);
774 
775 	if (node != NULL) {
776 		g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
777 				folder_tree_destroy_func, NULL);
778 		g_node_destroy(node);
779 		folder->node = NULL;
780 	}
781 }
782 
folder_add(Folder * folder)783 void folder_add(Folder *folder)
784 {
785 	Folder *cur_folder;
786 	GList *cur;
787 	gint i;
788 	FolderUpdateData hookdata;
789 
790 	cm_return_if_fail(folder != NULL);
791 
792 	if ((FOLDER_TYPE(folder) == F_IMAP ||
793 	     FOLDER_TYPE(folder) == F_NEWS) &&
794 	    folder->account == NULL) {
795 		return;
796 	}
797 
798 	for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
799 		cur_folder = FOLDER(cur->data);
800 		if (cur_folder->sort < folder->sort)
801 			break;
802 	}
803 
804 	folder_list = g_list_insert(folder_list, folder, i);
805 
806 	hookdata.folder = folder;
807 	hookdata.update_flags = FOLDER_ADD_FOLDER;
808 	hookdata.item = NULL;
809 	hookdata.item2 = NULL;
810 	hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
811 }
812 
folder_remove(Folder * folder)813 void folder_remove(Folder *folder)
814 {
815 	FolderUpdateData hookdata;
816 
817 	cm_return_if_fail(folder != NULL);
818 
819 	folder_list = g_list_remove(folder_list, folder);
820 
821 	hookdata.folder = folder;
822 	hookdata.update_flags = FOLDER_REMOVE_FOLDER;
823 	hookdata.item = NULL;
824 	hookdata.item2 = NULL;
825 	hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
826 }
827 
folder_get_list(void)828 GList *folder_get_list(void)
829 {
830 	return folder_list;
831 }
832 
folder_read_list(void)833 gint folder_read_list(void)
834 {
835 	GNode *node, *cur;
836 	XMLNode *xmlnode;
837 	gchar *path;
838 	GList *list;
839 	gint config_version = -1;
840 
841 	path = folder_get_list_path();
842 	if (!is_file_exist(path)) return -1;
843 	node = xml_parse_file(path);
844 	if (!node) return -1;
845 
846 	xmlnode = node->data;
847 	if (g_strcmp0(xmlnode->tag->tag, "folderlist") != 0) {
848 		g_warning("wrong folder list");
849 		xml_free_tree(node);
850 		return -1;
851 	}
852 
853 	cur = node->children;
854 	while (cur != NULL) {
855 		Folder *folder;
856 
857 		folder = folder_get_from_xml(cur);
858 		if (folder != NULL)
859 			folder_add(folder);
860 		else
861 			folder_unloaded_list = g_slist_append(folder_unloaded_list,
862 				(gpointer) xml_copy_tree(cur));
863 		cur = cur->next;
864 	}
865 
866 	for (list = xmlnode->tag->attr; list != NULL; list = list->next) {
867 		XMLAttr *attr = list->data;
868 
869 		if (!attr || !attr->name || !attr->value) continue;
870 		if (!strcmp(attr->name, "config_version")) {
871 			config_version = atoi(attr->value);
872 			debug_print("Found folderlist config_version %d\n", config_version);
873 		}
874 	}
875 
876 	xml_free_tree(node);
877 
878 	if (prefs_update_config_version_folderlist(config_version) < 0) {
879 		debug_print("Folderlist configuration file version upgrade failed\n");
880 		return -2;
881 	}
882 
883 	if (folder_list || folder_unloaded_list)
884 		return 0;
885 	else
886 		return -1;
887 }
888 
folder_write_list(void)889 void folder_write_list(void)
890 {
891 	GList *list;
892 	GSList *slist;
893 	Folder *folder;
894 	gchar *path;
895 	PrefFile *pfile;
896 	GNode *rootnode;
897 	XMLNode *xmlnode;
898 	XMLTag *tag;
899 
900 	path = folder_get_list_path();
901 	if ((pfile = prefs_write_open(path)) == NULL) return;
902 
903 	if (xml_file_put_xml_decl(pfile->fp) < 0) {
904 		prefs_file_close_revert(pfile);
905 		g_warning("failed to start write folder list.");
906 		return;
907 	}
908 	tag = xml_tag_new("folderlist");
909 	xml_tag_add_attr(tag, xml_attr_new_int("config_version",
910 				CLAWS_CONFIG_VERSION));
911 
912 	xmlnode = xml_node_new(tag, NULL);
913 
914 	rootnode = g_node_new(xmlnode);
915 
916 	for (list = folder_list; list != NULL; list = list->next) {
917 		GNode *node;
918 
919 		folder = list->data;
920 		node = folder_get_xml_node(folder);
921 		if (node != NULL)
922 			g_node_append(rootnode, node);
923 	}
924 
925 	for (slist = folder_unloaded_list; slist != NULL; slist = g_slist_next(slist)) {
926 		GNode *node = (GNode *) slist->data;
927 
928 		g_node_append(rootnode, (gpointer) xml_copy_tree(node));
929 	}
930 
931 	if (xml_write_tree(rootnode, pfile->fp) < 0) {
932 		prefs_file_close_revert(pfile);
933 		g_warning("failed to write folder list.");
934 	} else if (prefs_file_close(pfile) < 0) {
935 		g_warning("failed to write folder list.");
936 	}
937 	xml_free_tree(rootnode);
938 }
939 
folder_scan_tree_func(GNode * node,gpointer data)940 static gboolean folder_scan_tree_func(GNode *node, gpointer data)
941 {
942 	GHashTable *pptable = (GHashTable *)data;
943 	FolderItem *item = (FolderItem *)node->data;
944 
945 	folder_item_restore_persist_prefs(item, pptable);
946 	folder_item_scan_full(item, FALSE);
947 
948 	return FALSE;
949 }
950 
folder_restore_prefs_func(GNode * node,gpointer data)951 static gboolean folder_restore_prefs_func(GNode *node, gpointer data)
952 {
953 	GHashTable *pptable = (GHashTable *)data;
954 	FolderItem *item = (FolderItem *)node->data;
955 
956 	folder_item_restore_persist_prefs(item, pptable);
957 
958 	return FALSE;
959 }
960 
folder_scan_tree(Folder * folder,gboolean rebuild)961 void folder_scan_tree(Folder *folder, gboolean rebuild)
962 {
963 	GHashTable *pptable;
964 	FolderUpdateData hookdata;
965 	Folder *old_folder = folder;
966 
967 	if (!folder->klass->scan_tree)
968 		return;
969 
970 	pptable = folder_persist_prefs_new(folder);
971 
972 	if (rebuild)
973 		folder_remove(folder);
974 
975 	if (folder->klass->scan_tree(folder) < 0) {
976 		if (rebuild)
977 			folder_add(old_folder);
978 		return;
979 	} else if (rebuild)
980 		folder_add(folder);
981 
982 	hookdata.folder = folder;
983 	hookdata.update_flags = FOLDER_TREE_CHANGED;
984 	hookdata.item = NULL;
985 	hookdata.item2 = NULL;
986 	hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
987 
988 	if (rebuild)
989 		g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_scan_tree_func, pptable);
990 	else
991 		g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1, folder_restore_prefs_func, pptable);
992 
993 	folder_persist_prefs_free(pptable);
994 
995 	prefs_matcher_read_config();
996 
997 	folder_write_list();
998 }
999 
folder_create_folder(FolderItem * parent,const gchar * name)1000 FolderItem *folder_create_folder(FolderItem *parent, const gchar *name)
1001 {
1002 	FolderItem *new_item;
1003 
1004 	cm_return_val_if_fail(parent != NULL, NULL);
1005 
1006 	new_item = parent->folder->klass->create_folder(parent->folder, parent, name);
1007 	if (new_item) {
1008 		FolderUpdateData hookdata;
1009 
1010 		new_item->cache = msgcache_new();
1011 		new_item->cache_dirty = TRUE;
1012 		new_item->mark_dirty = TRUE;
1013 		new_item->tags_dirty = TRUE;
1014 
1015 		hookdata.folder = new_item->folder;
1016 		hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_ADD_FOLDERITEM;
1017 		hookdata.item = new_item;
1018 		hookdata.item2 = NULL;
1019 		hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
1020 	}
1021 
1022 	return new_item;
1023 }
1024 
folder_item_rename(FolderItem * item,gchar * newname)1025 gint folder_item_rename(FolderItem *item, gchar *newname)
1026 {
1027 	gint retval;
1028 
1029 	cm_return_val_if_fail(item != NULL, -1);
1030 	cm_return_val_if_fail(newname != NULL, -1);
1031 
1032 	retval = item->folder->klass->rename_folder(item->folder, item, newname);
1033 
1034 	if (retval >= 0) {
1035 		FolderItemUpdateData hookdata;
1036 		FolderUpdateData hookdata2;
1037 
1038 		hookdata.item = item;
1039 		hookdata.update_flags = F_ITEM_UPDATE_NAME;
1040 		hookdata.msg = NULL;
1041 		hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &hookdata);
1042 
1043 		hookdata2.folder = item->folder;
1044 		hookdata2.item = item;
1045 		hookdata2.item2 = NULL;
1046 		hookdata2.update_flags = FOLDER_RENAME_FOLDERITEM;
1047 		hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata2);
1048 	}
1049 
1050 	return retval;
1051 }
1052 
1053 struct TotalMsgCount
1054 {
1055 	guint new_msgs;
1056 	guint unread_msgs;
1057 	guint unreadmarked_msgs;
1058 	guint marked_msgs;
1059 	guint total_msgs;
1060 	guint replied_msgs;
1061 	guint forwarded_msgs;
1062 	guint locked_msgs;
1063 	guint ignored_msgs;
1064 	guint watched_msgs;
1065 };
1066 
1067 struct FuncToAllFoldersData
1068 {
1069 	FolderItemFunc	function;
1070 	gpointer	data;
1071 };
1072 
folder_func_to_all_folders_func(GNode * node,gpointer data)1073 static gboolean folder_func_to_all_folders_func(GNode *node, gpointer data)
1074 {
1075 	FolderItem *item;
1076 	struct FuncToAllFoldersData *function_data = (struct FuncToAllFoldersData *) data;
1077 
1078 	cm_return_val_if_fail(node->data != NULL, FALSE);
1079 
1080 	item = FOLDER_ITEM(node->data);
1081 	cm_return_val_if_fail(item != NULL, FALSE);
1082 
1083 	function_data->function(item, function_data->data);
1084 
1085 	return FALSE;
1086 }
1087 
folder_func_to_all_folders(FolderItemFunc function,gpointer data)1088 void folder_func_to_all_folders(FolderItemFunc function, gpointer data)
1089 {
1090 	GList *list;
1091 	Folder *folder;
1092 	struct FuncToAllFoldersData function_data;
1093 
1094 	function_data.function = function;
1095 	function_data.data = data;
1096 
1097 	for (list = folder_list; list != NULL; list = list->next) {
1098 		folder = FOLDER(list->data);
1099 		if (folder->node)
1100 			g_node_traverse(folder->node, G_PRE_ORDER,
1101 					G_TRAVERSE_ALL, -1,
1102 					folder_func_to_all_folders_func,
1103 					&function_data);
1104 	}
1105 }
1106 
folder_count_total_msgs_func(FolderItem * item,gpointer data)1107 static void folder_count_total_msgs_func(FolderItem *item, gpointer data)
1108 {
1109 	struct TotalMsgCount *count = (struct TotalMsgCount *)data;
1110 
1111 	count->new_msgs += item->new_msgs;
1112 	count->unread_msgs += item->unread_msgs;
1113 	count->unreadmarked_msgs += item->unreadmarked_msgs;
1114 	count->marked_msgs += item->marked_msgs;
1115 	count->total_msgs += item->total_msgs;
1116 	count->replied_msgs += item->replied_msgs;
1117 	count->forwarded_msgs += item->forwarded_msgs;
1118 	count->locked_msgs += item->locked_msgs;
1119 	count->ignored_msgs += item->ignored_msgs;
1120 	count->watched_msgs += item->watched_msgs;
1121 }
1122 
1123 struct TotalMsgStatus
1124 {
1125         guint new;
1126         guint unread;
1127 	guint total;
1128 	GString *str;
1129 };
1130 
folder_get_status_full_all_func(GNode * node,gpointer data)1131 static gboolean folder_get_status_full_all_func(GNode *node, gpointer data)
1132 {
1133 	FolderItem *item;
1134 	struct TotalMsgStatus *status = (struct TotalMsgStatus *)data;
1135 	gchar *id;
1136 
1137  	cm_return_val_if_fail(node->data != NULL, FALSE);
1138 
1139  	item = FOLDER_ITEM(node->data);
1140 
1141 	if (!item->path) return FALSE;
1142 
1143 	status->new += item->new_msgs;
1144 	status->unread += item->unread_msgs;
1145 	status->total += item->total_msgs;
1146 
1147 	if (status->str) {
1148 		id = folder_item_get_identifier(item);
1149 		g_string_append_printf(status->str, "%5d %5d %5d %s\n",
1150 				  item->new_msgs, item->unread_msgs,
1151 				  item->total_msgs, id);
1152 		g_free(id);
1153 	}
1154 
1155  	return FALSE;
1156  }
1157 
folder_get_status_full_all(GString * str,guint * new,guint * unread,guint * total)1158 static void folder_get_status_full_all(GString *str, guint *new, guint *unread,
1159 				       guint *total)
1160 {
1161  	GList *list;
1162  	Folder *folder;
1163 	struct TotalMsgStatus status;
1164 
1165 	status.new = status.unread = status.total = 0;
1166 	status.str = str;
1167 
1168 	debug_print("Counting total number of messages...\n");
1169 
1170  	for (list = folder_list; list != NULL; list = list->next) {
1171  		folder = FOLDER(list->data);
1172  		if (folder->node)
1173  			g_node_traverse(folder->node, G_PRE_ORDER,
1174  					G_TRAVERSE_ALL, -1,
1175 					folder_get_status_full_all_func,
1176 					&status);
1177  	}
1178 
1179 	*new = status.new;
1180 	*unread = status.unread;
1181 	*total = status.total;
1182 }
1183 
folder_get_status(GPtrArray * folders,gboolean full)1184 gchar *folder_get_status(GPtrArray *folders, gboolean full)
1185 {
1186 	guint new, unread, total;
1187 	GString *str;
1188 	gint i;
1189 	gchar *ret;
1190 
1191 	new = unread = total = 0;
1192 
1193 	str = g_string_new(NULL);
1194 
1195 	if (folders) {
1196 		for (i = 0; i < folders->len; i++) {
1197 			FolderItem *item;
1198 
1199 			item = g_ptr_array_index(folders, i);
1200 			new += item->new_msgs;
1201 			unread += item->unread_msgs;
1202 			total += item->total_msgs;
1203 
1204 			if (full) {
1205 				gchar *id;
1206 
1207 				id = folder_item_get_identifier(item);
1208 				g_string_append_printf(str, "%5d %5d %5d %s\n",
1209 						  item->new_msgs, item->unread_msgs,
1210 						  item->total_msgs, id);
1211 				g_free(id);
1212 			}
1213 		}
1214 	} else {
1215 		folder_get_status_full_all(full ? str : NULL,
1216 					   &new, &unread, &total);
1217 	}
1218 
1219 	if (full)
1220 		g_string_append_printf(str, "%5d %5d %5d\n", new, unread, total);
1221 	else
1222 		g_string_append_printf(str, "%d %d %d\n", new, unread, total);
1223 
1224 	ret = str->str;
1225 	g_string_free(str, FALSE);
1226 
1227 	return ret;
1228 }
1229 
folder_count_total_msgs(guint * new_msgs,guint * unread_msgs,guint * unreadmarked_msgs,guint * marked_msgs,guint * total_msgs,guint * replied_msgs,guint * forwarded_msgs,guint * locked_msgs,guint * ignored_msgs,guint * watched_msgs)1230 void folder_count_total_msgs(guint *new_msgs, guint *unread_msgs,
1231 			     guint *unreadmarked_msgs, guint *marked_msgs,
1232 			     guint *total_msgs, guint *replied_msgs,
1233 			     guint *forwarded_msgs, guint *locked_msgs,
1234 			     guint *ignored_msgs, guint *watched_msgs)
1235 {
1236 	struct TotalMsgCount count;
1237 
1238 	count.new_msgs = count.unread_msgs = count.unreadmarked_msgs = 0;
1239 	count.total_msgs = count.replied_msgs = count.forwarded_msgs = 0;
1240 	count.locked_msgs = count.ignored_msgs = count.watched_msgs = 0;
1241 	count.marked_msgs = 0;
1242 
1243 	debug_print("Counting total number of messages...\n");
1244 
1245 	folder_func_to_all_folders(folder_count_total_msgs_func, &count);
1246 
1247 	*new_msgs = count.new_msgs;
1248 	*unread_msgs = count.unread_msgs;
1249 	*unreadmarked_msgs = count.unreadmarked_msgs;
1250 	*marked_msgs = count.marked_msgs;
1251 	*total_msgs = count.total_msgs;
1252 	*replied_msgs = count.replied_msgs;
1253 	*forwarded_msgs = count.forwarded_msgs;
1254 	*locked_msgs = count.locked_msgs;
1255 	*ignored_msgs = count.ignored_msgs;
1256 	*watched_msgs = count.watched_msgs;
1257 }
1258 
folder_find_from_path(const gchar * path)1259 Folder *folder_find_from_path(const gchar *path)
1260 {
1261 	GList *list;
1262 	Folder *folder;
1263 
1264 	for (list = folder_list; list != NULL; list = list->next) {
1265 		folder = list->data;
1266 		if ((FOLDER_TYPE(folder) == F_MH ||
1267 		     FOLDER_TYPE(folder) == F_MBOX) &&
1268 		    !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
1269 			return folder;
1270 	}
1271 
1272 	return NULL;
1273 }
1274 
folder_find_from_name(const gchar * name,FolderClass * klass)1275 Folder *folder_find_from_name(const gchar *name, FolderClass *klass)
1276 {
1277 	GList *list;
1278 	Folder *folder;
1279 
1280 	for (list = folder_list; list != NULL; list = list->next) {
1281 		folder = list->data;
1282 		if (folder->klass == klass &&
1283 		    g_strcmp0(name, folder->name) == 0)
1284 			return folder;
1285 	}
1286 
1287 	return NULL;
1288 }
1289 
folder_item_find_func(GNode * node,gpointer data)1290 static gboolean folder_item_find_func(GNode *node, gpointer data)
1291 {
1292 	FolderItem *item = node->data;
1293 	gpointer *d = data;
1294 	const gchar *path = d[0];
1295 
1296 	if (path_cmp(path, item->path) != 0)
1297 		return FALSE;
1298 
1299 	d[1] = item;
1300 
1301 	return TRUE;
1302 }
1303 
folder_find_item_from_path(const gchar * path)1304 FolderItem *folder_find_item_from_path(const gchar *path)
1305 {
1306 	Folder *folder;
1307 	gpointer d[2];
1308 	GList *list = folder_get_list();
1309 
1310 	folder = list ? list->data:NULL;
1311 
1312 	cm_return_val_if_fail(folder != NULL, NULL);
1313 
1314 	d[0] = (gpointer)path;
1315 	d[1] = NULL;
1316 	while (d[1] == NULL && list) {
1317 		folder = FOLDER(list->data);
1318 		g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1319 			folder_item_find_func, d);
1320 		list = list->next;
1321 	}
1322 	return d[1];
1323 }
1324 
folder_item_find_func_real_path(GNode * node,gpointer data)1325 static gboolean folder_item_find_func_real_path(GNode *node, gpointer data)
1326 {
1327 	FolderItem *item = node->data;
1328 	gpointer *d = data;
1329 	const gchar *path = d[0];
1330 	gchar *tmp = folder_item_get_path(item);
1331 	if (path_cmp(path, tmp) != 0) {
1332 		g_free(tmp);
1333 		return FALSE;
1334 	}
1335 	g_free(tmp);
1336 	d[1] = item;
1337 
1338 	return TRUE;
1339 }
1340 
folder_find_item_from_real_path(const gchar * path)1341 FolderItem *folder_find_item_from_real_path(const gchar *path)
1342 {
1343 	Folder *folder;
1344 	gpointer d[2];
1345 	GList *list = folder_get_list();
1346 
1347 	folder = list ? list->data:NULL;
1348 
1349 	cm_return_val_if_fail(folder != NULL, NULL);
1350 
1351 	d[0] = (gpointer)path;
1352 	d[1] = NULL;
1353 	while (d[1] == NULL && list) {
1354 		folder = FOLDER(list->data);
1355 		g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1356 			folder_item_find_func_real_path, d);
1357 		list = list->next;
1358 	}
1359 	return d[1];
1360 }
1361 
folder_find_child_item_by_name(FolderItem * item,const gchar * name)1362 FolderItem *folder_find_child_item_by_name(FolderItem *item, const gchar *name)
1363 {
1364 	GNode *node;
1365 	FolderItem *child;
1366 
1367 	for (node = item->node->children; node != NULL; node = node->next) {
1368 		child = FOLDER_ITEM(node->data);
1369 		if (g_strcmp0(child->name, name) == 0) {
1370 			return child;
1371 		}
1372 	}
1373 
1374 	return NULL;
1375 }
1376 
folder_get_class_from_string(const gchar * str)1377 FolderClass *folder_get_class_from_string(const gchar *str)
1378 {
1379 	GSList *classlist;
1380 
1381 	classlist = folder_get_class_list();
1382 	for (; classlist != NULL; classlist = g_slist_next(classlist)) {
1383 		FolderClass *class = (FolderClass *) classlist->data;
1384 		if (g_ascii_strcasecmp(class->idstr, str) == 0)
1385 			return class;
1386 	}
1387 
1388 	return NULL;
1389 }
1390 
folder_get_identifier(Folder * folder)1391 gchar *folder_get_identifier(Folder *folder)
1392 {
1393 	gchar *type_str;
1394 
1395 	cm_return_val_if_fail(folder != NULL, NULL);
1396 
1397 	type_str = folder->klass->idstr;
1398 	return g_strconcat("#", type_str, "/", folder->name, NULL);
1399 }
1400 
folder_item_get_identifier(FolderItem * item)1401 gchar *folder_item_get_identifier(FolderItem *item)
1402 {
1403 	gchar *id = NULL;
1404 	gchar *folder_id = NULL;
1405 
1406 	cm_return_val_if_fail(item != NULL, NULL);
1407 
1408 	if (item->path == NULL)
1409 		return NULL;
1410 
1411 	folder_id = folder_get_identifier(item->folder);
1412 	id = g_strconcat(folder_id, "/", item->path, NULL);
1413 	g_free(folder_id);
1414 
1415 	return id;
1416 }
1417 
folder_find_from_identifier(const gchar * identifier)1418 Folder *folder_find_from_identifier(const gchar *identifier)
1419 {
1420 	gchar *str;
1421 	gchar *p;
1422 	gchar *name;
1423 	FolderClass *class;
1424 
1425 	cm_return_val_if_fail(identifier != NULL, NULL);
1426 
1427 	if (*identifier != '#')
1428 		return NULL;
1429 
1430 	Xstrdup_a(str, identifier, return NULL);
1431 
1432 	p = strchr(str, '/');
1433 	if (!p)
1434 		return NULL;
1435 	*p = '\0';
1436 	p++;
1437 	class = folder_get_class_from_string(&str[1]);
1438 	if (class == NULL)
1439 		return NULL;
1440 
1441 	name = p;
1442 	p = strchr(p, '/');
1443 	if (p)
1444 		return NULL;
1445 
1446 	return folder_find_from_name(name, class);
1447 }
1448 
folder_find_item_from_identifier(const gchar * identifier)1449 FolderItem *folder_find_item_from_identifier(const gchar *identifier)
1450 {
1451 	Folder *folder;
1452 	gpointer d[2];
1453 	gchar *str;
1454 	gchar *p;
1455 	gchar *name;
1456 	gchar *path;
1457 	FolderClass *class;
1458 
1459 	cm_return_val_if_fail(identifier != NULL, NULL);
1460 
1461 	if (*identifier != '#')
1462 		return folder_find_item_from_path(identifier);
1463 
1464 	Xstrdup_a(str, identifier, return NULL);
1465 
1466 	p = strchr(str, '/');
1467 	if (!p)
1468 		return folder_find_item_from_path(identifier);
1469 	*p = '\0';
1470 	p++;
1471 	class = folder_get_class_from_string(&str[1]);
1472 	if (class == NULL)
1473 		return folder_find_item_from_path(identifier);
1474 
1475 	name = p;
1476 	p = strchr(p, '/');
1477 	if (!p)
1478 		return folder_find_item_from_path(identifier);
1479 	*p = '\0';
1480 	p++;
1481 
1482 	folder = folder_find_from_name(name, class);
1483 	if (!folder)
1484 		return folder_find_item_from_path(identifier);
1485 
1486 	path = p;
1487 
1488 	d[0] = (gpointer)path;
1489 	d[1] = NULL;
1490 	g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1491 			folder_item_find_func, d);
1492 	return d[1];
1493 }
1494 
1495 /** Returns the FolderItem from a given identifier
1496  *
1497  * The FolderItem is created if it doesn't already exist.
1498  * If creation failed, the function returns NULL.
1499  *
1500  * Identifiers are of the form #type/Mailbox/FolderA/FolderB/FolderC
1501  */
folder_get_item_from_identifier(const gchar * identifier)1502 FolderItem *folder_get_item_from_identifier(const gchar *identifier)
1503 {
1504 	FolderItem *item, *last_parent;
1505 	Folder *folder;
1506 	gchar *p1, *p2, *str;
1507 	size_t len;
1508 	FolderClass *class;
1509 	gboolean created_something = FALSE;
1510 
1511 	item = folder_find_item_from_identifier(identifier);
1512 	if(item)
1513 		return item;
1514 
1515 	/* trivial sanity check: need at least # and two slashes */
1516 	len = strlen(identifier);
1517 	if(len < 3)
1518 		return NULL;
1519 
1520 	/* make sure identifier ends with a slash */
1521 	if(identifier[len-1] == G_DIR_SEPARATOR) {
1522 		Xstrdup_a(str, identifier, return NULL);
1523 	}
1524 	else {
1525 		Xstrndup_a(str, identifier, len+1, return NULL);
1526 		str[len] = G_DIR_SEPARATOR;
1527 	}
1528 
1529 	/* find folder class */
1530 	p1 = strchr(str, G_DIR_SEPARATOR);
1531 	if(!p1)
1532 		return NULL;
1533 	*p1 = '\0';
1534 	class = folder_get_class_from_string(&str[1]);
1535 	if(!class)
1536 		return NULL;
1537 	*p1 = G_DIR_SEPARATOR;
1538 	++p1;
1539 
1540 	/* find folder from class and name */
1541 	p2 = strchr(p1, G_DIR_SEPARATOR);
1542 	if(!p2)
1543 		return NULL;
1544 	*p2 = '\0';
1545 	folder = folder_find_from_name(p1, class);
1546 	if(!folder)
1547 		return NULL;
1548 	*p2 = G_DIR_SEPARATOR;
1549 	++p2;
1550 	p1 = p2;
1551 
1552 	/* Now, move forward and make sure all sections in the path exist */
1553 	last_parent = folder->node->data;
1554 	while((p1 = strchr(p1, G_DIR_SEPARATOR)) != NULL) {
1555 		*p1 = '\0';
1556 		item = folder_find_item_from_identifier(str);
1557 		if(!item) {
1558 			item = folder_create_folder(last_parent, p2);
1559 			if(!item)
1560 				return NULL;
1561 			debug_print("Created folder '%s'\n", str);
1562 			created_something = TRUE;
1563 			if(prefs_common.inherit_folder_props && (last_parent != item->folder->node->data)) {
1564 				folder_item_prefs_copy_prefs(last_parent, item);
1565 			}
1566 		}
1567 		last_parent = item;
1568 		*p1 = G_DIR_SEPARATOR;
1569 		++p1;
1570 		p2 = p1;
1571 	}
1572 
1573 	if(created_something)
1574 		folder_write_list();
1575 
1576 	return item;
1577 }
1578 
1579 
1580 /**
1581  * Get a displayable name for a FolderItem
1582  *
1583  * \param item FolderItem for that a name should be created
1584  * \return Displayable name for item, returned string has to
1585  *         be freed
1586  */
folder_item_get_name(FolderItem * item)1587 gchar *folder_item_get_name(FolderItem *item)
1588 {
1589 	gchar *name = NULL;
1590 
1591 	cm_return_val_if_fail(item != NULL, g_strdup(""));
1592 
1593 	switch (item->stype) {
1594 	case F_INBOX:
1595 		name = g_strdup(!g_strcmp0(item->name, INBOX_DIR) ? _("Inbox") :
1596 				item->name);
1597 		break;
1598 	case F_OUTBOX:
1599 		name = g_strdup(!g_strcmp0(item->name, OUTBOX_DIR) ? _("Sent") :
1600 				item->name);
1601 		break;
1602 	case F_QUEUE:
1603 		name = g_strdup(!g_strcmp0(item->name, QUEUE_DIR) ? _("Queue") :
1604 				item->name);
1605 		break;
1606 	case F_TRASH:
1607 		name = g_strdup(!g_strcmp0(item->name, TRASH_DIR) ? _("Trash") :
1608 				item->name);
1609 		break;
1610 	case F_DRAFT:
1611 		name = g_strdup(!g_strcmp0(item->name, DRAFT_DIR) ? _("Drafts") :
1612 				item->name);
1613 		break;
1614 	default:
1615 		break;
1616 	}
1617 
1618 	if (name == NULL) {
1619 		/*
1620 		 * should probably be done by a virtual function,
1621 		 * the folder knows the ui string and how to abbrev
1622 		*/
1623 		if (folder_item_parent(item) == NULL) {
1624 			name = g_strconcat(item->name, " (", item->folder->klass->uistr, ")", NULL);
1625 		} else {
1626 			if (FOLDER_CLASS(item->folder) == news_get_class() &&
1627 			    item->path && !g_strcmp0(item->name, item->path))
1628 				name = get_abbrev_newsgroup_name
1629 					(item->path,
1630 					 prefs_common.ng_abbrev_len);
1631 			else
1632 				name = g_strdup(item->name);
1633 		}
1634 	}
1635 
1636 	if (name == NULL)
1637 		name = g_strdup("");
1638 
1639 	return name;
1640 }
1641 
folder_have_mailbox(void)1642 gboolean folder_have_mailbox (void)
1643 {
1644 	GList *cur;
1645 	for (cur = folder_list; cur != NULL; cur = g_list_next(cur)) {
1646 		Folder *folder = FOLDER(cur->data);
1647 		if (folder->inbox && folder->outbox)
1648 			return TRUE;
1649 	}
1650 	return FALSE;
1651 }
1652 
folder_get_default_inbox(void)1653 FolderItem *folder_get_default_inbox(void)
1654 {
1655 	GList *flist;
1656 
1657 	for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1658 		Folder * folder = FOLDER(flist->data);
1659 
1660 		if (folder == NULL)
1661 			continue;
1662 		if (folder->inbox == NULL)
1663 			continue;
1664 		if (folder->klass->type == F_UNKNOWN)
1665 			continue;
1666 
1667 		return folder->inbox;
1668 	}
1669 
1670 	return NULL;
1671 }
1672 
folder_get_default_inbox_for_class(FolderType type)1673 FolderItem *folder_get_default_inbox_for_class(FolderType type)
1674 {
1675 	GList *flist;
1676 
1677 	for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1678 		Folder * folder = FOLDER(flist->data);
1679 
1680 		if (folder == NULL)
1681 			continue;
1682 		if (folder->inbox == NULL)
1683 			continue;
1684 		if (folder->klass->type != type)
1685 			continue;
1686 
1687 		return folder->inbox;
1688 	}
1689 
1690 	return NULL;
1691 }
1692 
folder_get_default_outbox(void)1693 FolderItem *folder_get_default_outbox(void)
1694 {
1695 	GList *flist;
1696 
1697 	for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1698 		Folder * folder = FOLDER(flist->data);
1699 
1700 		if (folder == NULL)
1701 			continue;
1702 		if (folder->outbox == NULL)
1703 			continue;
1704 		if (folder->klass->type == F_UNKNOWN)
1705 			continue;
1706 
1707 		return folder->outbox;
1708 	}
1709 
1710 	return NULL;
1711 }
1712 
folder_get_default_outbox_for_class(FolderType type)1713 FolderItem *folder_get_default_outbox_for_class(FolderType type)
1714 {
1715 	GList *flist;
1716 
1717 	for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1718 		Folder * folder = FOLDER(flist->data);
1719 
1720 		if (folder == NULL)
1721 			continue;
1722 		if (folder->outbox == NULL)
1723 			continue;
1724 		if (folder->klass->type != type)
1725 			continue;
1726 
1727 		return folder->outbox;
1728 	}
1729 
1730 	return NULL;
1731 }
1732 
folder_get_default_draft(void)1733 FolderItem *folder_get_default_draft(void)
1734 {
1735 	GList *flist;
1736 
1737 	for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1738 		Folder * folder = FOLDER(flist->data);
1739 
1740 		if (folder == NULL)
1741 			continue;
1742 		if (folder->draft == NULL)
1743 			continue;
1744 		if (folder->klass->type == F_UNKNOWN)
1745 			continue;
1746 
1747 		return folder->draft;
1748 	}
1749 
1750 	return NULL;
1751 }
1752 
folder_get_default_draft_for_class(FolderType type)1753 FolderItem *folder_get_default_draft_for_class(FolderType type)
1754 {
1755 	GList *flist;
1756 
1757 	for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1758 		Folder * folder = FOLDER(flist->data);
1759 
1760 		if (folder == NULL)
1761 			continue;
1762 		if (folder->draft == NULL)
1763 			continue;
1764 		if (folder->klass->type != type)
1765 			continue;
1766 
1767 		return folder->draft;
1768 	}
1769 
1770 	return NULL;
1771 }
1772 
folder_get_default_queue(void)1773 FolderItem *folder_get_default_queue(void)
1774 {
1775 	GList *flist;
1776 
1777 	for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1778 		Folder * folder = FOLDER(flist->data);
1779 
1780 		if (folder == NULL)
1781 			continue;
1782 		if (folder->queue == NULL)
1783 			continue;
1784 		if (folder->klass->type == F_UNKNOWN)
1785 			continue;
1786 
1787 		return folder->queue;
1788 	}
1789 
1790 	return NULL;
1791 }
1792 
folder_get_default_queue_for_class(FolderType type)1793 FolderItem *folder_get_default_queue_for_class(FolderType type)
1794 {
1795 	GList *flist;
1796 
1797 	for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1798 		Folder * folder = FOLDER(flist->data);
1799 
1800 		if (folder == NULL)
1801 			continue;
1802 		if (folder->queue == NULL)
1803 			continue;
1804 		if (folder->klass->type != type)
1805 			continue;
1806 
1807 		return folder->queue;
1808 	}
1809 
1810 	return NULL;
1811 }
1812 
folder_get_default_trash(void)1813 FolderItem *folder_get_default_trash(void)
1814 {
1815 	GList *flist;
1816 
1817 	for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1818 		Folder * folder = FOLDER(flist->data);
1819 
1820 		if (folder == NULL)
1821 			continue;
1822 		if (folder->trash == NULL)
1823 			continue;
1824 		if (folder->klass->type == F_UNKNOWN)
1825 			continue;
1826 
1827 		return folder->trash;
1828 	}
1829 
1830 	return NULL;
1831 }
1832 
folder_get_default_trash_for_class(FolderType type)1833 FolderItem *folder_get_default_trash_for_class(FolderType type)
1834 {
1835 	GList *flist;
1836 
1837 	for (flist = folder_list; flist != NULL; flist = g_list_next(flist)) {
1838 		Folder * folder = FOLDER(flist->data);
1839 
1840 		if (folder == NULL)
1841 			continue;
1842 		if (folder->trash == NULL)
1843 			continue;
1844 		if (folder->klass->type != type)
1845 			continue;
1846 
1847 		return folder->trash;
1848 	}
1849 
1850 	return NULL;
1851 }
1852 
1853 #define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type)		\
1854 {								\
1855 	if (!folder->member) {					\
1856 		item = folder_item_new(folder, dir, dir);	\
1857 		item->stype = type;				\
1858 		folder_item_append(rootitem, item);		\
1859 		folder->member = item;				\
1860 	}							\
1861 }
1862 
folder_set_missing_folders(void)1863 void folder_set_missing_folders(void)
1864 {
1865 	Folder *folder;
1866 	FolderItem *rootitem;
1867 	FolderItem *item;
1868 	GList *list;
1869 
1870 	for (list = folder_list; list != NULL; list = list->next) {
1871 		folder = list->data;
1872 		if (FOLDER_TYPE(folder) != F_MH) continue;
1873 		rootitem = FOLDER_ITEM(folder->node->data);
1874 		cm_return_if_fail(rootitem != NULL);
1875 
1876 		if (folder->inbox && folder->outbox && folder->draft &&
1877 		    folder->queue && folder->trash)
1878 			continue;
1879 
1880 		if (folder->klass->create_tree(folder) < 0) {
1881 			g_warning("%s: can't create the folder tree.",
1882 				  LOCAL_FOLDER(folder)->rootpath);
1883 			continue;
1884 		}
1885 
1886 		CREATE_FOLDER_IF_NOT_EXIST(inbox,  INBOX_DIR,  F_INBOX);
1887 		CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX);
1888 		CREATE_FOLDER_IF_NOT_EXIST(draft,  DRAFT_DIR,  F_DRAFT);
1889 		CREATE_FOLDER_IF_NOT_EXIST(queue,  QUEUE_DIR,  F_QUEUE);
1890 		CREATE_FOLDER_IF_NOT_EXIST(trash,  TRASH_DIR,  F_TRASH);
1891 	}
1892 }
1893 
folder_unref_account_func(GNode * node,gpointer data)1894 static gboolean folder_unref_account_func(GNode *node, gpointer data)
1895 {
1896 	FolderItem *item = node->data;
1897 	PrefsAccount *account = data;
1898 
1899 	if (item->account == account)
1900 		item->account = NULL;
1901 
1902 	return FALSE;
1903 }
1904 
folder_unref_account_all(PrefsAccount * account)1905 void folder_unref_account_all(PrefsAccount *account)
1906 {
1907 	Folder *folder;
1908 	GList *list;
1909 
1910 	if (!account) return;
1911 
1912 	for (list = folder_list; list != NULL; list = list->next) {
1913 		folder = list->data;
1914 		if (folder->account == account)
1915 			folder->account = NULL;
1916 		g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1917 				folder_unref_account_func, account);
1918 	}
1919 }
1920 
1921 #undef CREATE_FOLDER_IF_NOT_EXIST
1922 
folder_item_get_path(FolderItem * item)1923 gchar *folder_item_get_path(FolderItem *item)
1924 {
1925 	Folder *folder;
1926 
1927 	cm_return_val_if_fail(item != NULL, NULL);
1928 	folder = item->folder;
1929 	cm_return_val_if_fail(folder != NULL, NULL);
1930 
1931 	return folder->klass->item_get_path(folder, item);
1932 }
1933 
folder_sort_cache_list_by_msgnum(gconstpointer a,gconstpointer b)1934 static gint folder_sort_cache_list_by_msgnum(gconstpointer a, gconstpointer b)
1935 {
1936 	MsgInfo *msginfo_a = (MsgInfo *) a;
1937 	MsgInfo *msginfo_b = (MsgInfo *) b;
1938 
1939 	return (msginfo_a->msgnum - msginfo_b->msgnum);
1940 }
1941 
folder_sort_folder_list(gconstpointer a,gconstpointer b)1942 static gint folder_sort_folder_list(gconstpointer a, gconstpointer b)
1943 {
1944 	guint gint_a = GPOINTER_TO_INT(a);
1945 	guint gint_b = GPOINTER_TO_INT(b);
1946 
1947 	return (gint_a - gint_b);
1948 }
1949 
syncronize_flags(FolderItem * item,MsgInfoList * msglist)1950 static gint syncronize_flags(FolderItem *item, MsgInfoList *msglist)
1951 {
1952 	GHashTable *relation;
1953 	gint ret = 0;
1954 	GSList *cur;
1955 
1956 	if(msglist == NULL)
1957 		return 0;
1958 	if(item->folder->klass->get_flags == NULL)
1959 		return 0;
1960 	if (item->no_select)
1961 		return 0;
1962 
1963 	relation = g_hash_table_new(g_direct_hash, g_direct_equal);
1964 	if ((ret = item->folder->klass->get_flags(
1965 	    item->folder, item, msglist, relation)) == 0) {
1966 		gpointer data, old_key;
1967 		MsgInfo *msginfo;
1968 		MsgPermFlags permflags = 0;
1969 
1970 		folder_item_update_freeze();
1971 		folder_item_set_batch(item, TRUE);
1972 		for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
1973 			msginfo = (MsgInfo *) cur->data;
1974 
1975 			if (g_hash_table_lookup_extended(relation, msginfo, &old_key, &data)) {
1976 				permflags = GPOINTER_TO_INT(data);
1977 
1978 				if (msginfo->flags.perm_flags != permflags) {
1979 					procmsg_msginfo_change_flags(msginfo,
1980 						permflags & ~msginfo->flags.perm_flags, 0,
1981 						~permflags & msginfo->flags.perm_flags, 0);
1982 				}
1983 			}
1984 		}
1985 		folder_item_set_batch(item, FALSE);
1986 		folder_item_update_thaw();
1987 	}
1988 	g_hash_table_destroy(relation);
1989 
1990 	return ret;
1991 }
1992 
folder_item_syncronize_flags(FolderItem * item)1993 static gint folder_item_syncronize_flags(FolderItem *item)
1994 {
1995 	MsgInfoList *msglist = NULL;
1996 	GSList *cur;
1997 	gint ret = 0;
1998 
1999 	cm_return_val_if_fail(item != NULL, -1);
2000 	cm_return_val_if_fail(item->folder != NULL, -1);
2001 	cm_return_val_if_fail(item->folder->klass != NULL, -1);
2002 	if (item->no_select)
2003 		return -1;
2004 
2005 	item->scanning = ITEM_SCANNING_WITH_FLAGS;
2006 
2007 	if (item->cache == NULL)
2008 		folder_item_read_cache(item);
2009 
2010 	msglist = msgcache_get_msg_list(item->cache);
2011 
2012 	ret = syncronize_flags(item, msglist);
2013 
2014 	for (cur = msglist; cur != NULL; cur = g_slist_next(cur)) {
2015 		procmsg_msginfo_free((MsgInfo **)&(cur->data));
2016 	}
2017 
2018 	g_slist_free(msglist);
2019 
2020 	item->scanning = ITEM_NOT_SCANNING;
2021 
2022 	return ret;
2023 }
2024 
folder_item_process_open(FolderItem * item,void (* before_proc_func)(gpointer data),void (* after_proc_func)(gpointer data),gpointer data)2025 static void folder_item_process_open (FolderItem *item,
2026 				 void (*before_proc_func)(gpointer data),
2027 				 void (*after_proc_func)(gpointer data),
2028 				 gpointer data)
2029 {
2030 	gchar *buf;
2031 	if (item == NULL)
2032 		return;
2033 	if((item->folder->klass->scan_required != NULL) &&
2034 	   (item->folder->klass->scan_required(item->folder, item))) {
2035 		folder_item_scan_full(item, TRUE);
2036 	} else {
2037 		folder_item_syncronize_flags(item);
2038 	}
2039 
2040 	/* Processing */
2041 	if (item->prefs->enable_processing_when_opening) {
2042 		buf = g_strdup_printf(_("Processing (%s)...\n"),
2043 			      item->path ? item->path : item->name);
2044 		g_free(buf);
2045 
2046 		if (before_proc_func)
2047 			before_proc_func(data);
2048 
2049 		folder_item_apply_processing(item);
2050 
2051 		if (after_proc_func)
2052 			after_proc_func(data);
2053 	}
2054 	item->processing_pending = FALSE;
2055 	return;
2056 }
2057 
folder_item_open(FolderItem * item)2058 gint folder_item_open(FolderItem *item)
2059 {
2060 	START_TIMING("");
2061 	if (item->no_select)
2062 		return -1;
2063 
2064 	if (item->scanning != ITEM_NOT_SCANNING) {
2065 		debug_print("%s is scanning... \n", item->path ? item->path : item->name);
2066 		return -2;
2067 	}
2068 
2069 	item->processing_pending = TRUE;
2070 	folder_item_process_open (item, NULL, NULL, NULL);
2071 
2072 	item->opened = TRUE;
2073 	END_TIMING();
2074 	return 0;
2075 }
2076 
folder_item_close(FolderItem * item)2077 gint folder_item_close(FolderItem *item)
2078 {
2079 	GSList *mlist, *cur;
2080 	Folder *folder;
2081 
2082 	cm_return_val_if_fail(item != NULL, -1);
2083 
2084 	if (item->no_select)
2085 		return -1;
2086 
2087 	if (item->new_msgs) {
2088 		folder_item_update_freeze();
2089 		mlist = folder_item_get_msg_list(item);
2090 		for (cur = mlist ; cur != NULL ; cur = cur->next) {
2091 			MsgInfo * msginfo;
2092 
2093 			msginfo = (MsgInfo *) cur->data;
2094 			if (MSG_IS_NEW(msginfo->flags))
2095 				procmsg_msginfo_unset_flags(msginfo, MSG_NEW, 0);
2096 			procmsg_msginfo_free(&msginfo);
2097 		}
2098 		g_slist_free(mlist);
2099 		folder_item_update_thaw();
2100 	}
2101 
2102 	folder_item_write_cache(item);
2103 
2104 	folder_item_update(item, F_ITEM_UPDATE_MSGCNT);
2105 
2106 	item->opened = FALSE;
2107 	folder = item->folder;
2108 
2109 	if (folder->klass->close == NULL)
2110 		return 0;
2111 
2112 	return folder->klass->close(folder, item);
2113 }
2114 
get_msginfos(FolderItem * item,MsgNumberList * numlist)2115 static MsgInfoList *get_msginfos(FolderItem *item, MsgNumberList *numlist)
2116 {
2117 	MsgInfoList *msglist = NULL;
2118 	Folder *folder = item->folder;
2119 	if (item->no_select)
2120 		return NULL;
2121 
2122 	if (folder->klass->get_msginfos != NULL)
2123 		msglist = folder->klass->get_msginfos(folder, item, numlist);
2124 	else {
2125 		MsgNumberList *elem;
2126 
2127 		for (elem = numlist; elem != NULL; elem = g_slist_next(elem)) {
2128 			MsgInfo *msginfo;
2129 			guint num;
2130 
2131 			num = GPOINTER_TO_INT(elem->data);
2132 			msginfo = folder->klass->get_msginfo(folder, item, num);
2133 			if (msginfo != NULL)
2134 				msglist = g_slist_prepend(msglist, msginfo);
2135 		}
2136 	}
2137 
2138 	return msglist;
2139 }
2140 
get_msginfo(FolderItem * item,guint num)2141 static MsgInfo *get_msginfo(FolderItem *item, guint num)
2142 {
2143 	MsgNumberList numlist;
2144 	MsgInfoList *msglist;
2145 	MsgInfo *msginfo = NULL;
2146 
2147 	numlist.data = GINT_TO_POINTER(num);
2148 	numlist.next = NULL;
2149 	msglist = get_msginfos(item, &numlist);
2150 	if (msglist != NULL)
2151 		msginfo = procmsg_msginfo_new_ref(msglist->data);
2152 	procmsg_msg_list_free(msglist);
2153 
2154 	return msginfo;
2155 }
2156 
folder_item_scan_full(FolderItem * item,gboolean filtering)2157 gint folder_item_scan_full(FolderItem *item, gboolean filtering)
2158 {
2159 	Folder *folder;
2160 	GSList *folder_list = NULL, *cache_list = NULL;
2161 	GSList *folder_list_cur, *cache_list_cur, *new_list = NULL;
2162 	GSList *exists_list = NULL, *elem;
2163 	GSList *newmsg_list = NULL;
2164 	guint newcnt = 0, unreadcnt = 0, totalcnt = 0;
2165 	guint markedcnt = 0, unreadmarkedcnt = 0;
2166 	guint repliedcnt = 0, forwardedcnt = 0;
2167 	guint lockedcnt = 0, ignoredcnt = 0, watchedcnt = 0;
2168 
2169 	guint cache_max_num, folder_max_num, cache_cur_num, folder_cur_num;
2170 	gboolean update_flags = 0, old_uids_valid = FALSE;
2171 	GHashTable *subject_table = NULL;
2172 
2173 	cm_return_val_if_fail(item != NULL, -1);
2174 	if (item->path == NULL) return -1;
2175 
2176 	folder = item->folder;
2177 
2178 	cm_return_val_if_fail(folder != NULL, -1);
2179 	cm_return_val_if_fail(folder->klass->get_num_list != NULL, -1);
2180 
2181 	item->scanning = ITEM_SCANNING_WITH_FLAGS;
2182 
2183 	debug_print("Scanning folder %s for cache changes.\n", item->path ? item->path : "(null)");
2184 
2185 	/* Get list of messages for folder and cache */
2186 	if (folder->klass->get_num_list(item->folder, item, &folder_list, &old_uids_valid) < 0) {
2187 		debug_print("Error fetching list of message numbers\n");
2188 		item->scanning = ITEM_NOT_SCANNING;
2189 		return(-1);
2190 	}
2191 
2192 	if(prefs_common.thread_by_subject) {
2193 		subject_table = g_hash_table_new(g_str_hash, g_str_equal);
2194 	}
2195 
2196 	if (old_uids_valid) {
2197 		if (!item->cache)
2198 			folder_item_read_cache(item);
2199 		cache_list = msgcache_get_msg_list(item->cache);
2200 	} else {
2201 		if (item->cache)
2202 			msgcache_destroy(item->cache);
2203 		item->cache = msgcache_new();
2204 		item->cache_dirty = TRUE;
2205 		item->mark_dirty = TRUE;
2206 		item->tags_dirty = TRUE;
2207 		cache_list = NULL;
2208 	}
2209 
2210 	/* Sort both lists */
2211     	cache_list = g_slist_sort(cache_list, folder_sort_cache_list_by_msgnum);
2212 	folder_list = g_slist_sort(folder_list, folder_sort_folder_list);
2213 
2214 	cache_list_cur = cache_list;
2215 	folder_list_cur = folder_list;
2216 
2217 	if (cache_list_cur != NULL) {
2218 		GSList *cache_list_last;
2219 
2220 		cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
2221 		cache_list_last = g_slist_last(cache_list);
2222 		cache_max_num = ((MsgInfo *)cache_list_last->data)->msgnum;
2223 	} else {
2224 		cache_cur_num = G_MAXUINT;
2225 		cache_max_num = 0;
2226 	}
2227 
2228 	if (folder_list_cur != NULL) {
2229 		GSList *folder_list_last;
2230 
2231 		folder_cur_num = GPOINTER_TO_UINT(folder_list_cur->data);
2232 		folder_list_last = g_slist_last(folder_list);
2233 		folder_max_num = GPOINTER_TO_UINT(folder_list_last->data);
2234 	} else {
2235 		folder_cur_num = G_MAXUINT;
2236 		folder_max_num = 0;
2237 	}
2238 
2239 	while ((cache_cur_num != G_MAXUINT) || (folder_cur_num != G_MAXUINT)) {
2240 		/*
2241 		 *  Message only exists in the folder
2242 		 *  Remember message for fetching
2243 		 */
2244 		if (folder_cur_num < cache_cur_num) {
2245 			gboolean add = FALSE;
2246 
2247 			switch(FOLDER_TYPE(folder)) {
2248 				case F_NEWS:
2249 					if (folder_cur_num < cache_max_num)
2250 						break;
2251 
2252 					if (folder->account->max_articles == 0) {
2253 						add = TRUE;
2254 					}
2255 
2256 					if (folder_max_num <= folder->account->max_articles) {
2257 						add = TRUE;
2258 					} else if (folder_cur_num > (folder_max_num - folder->account->max_articles)) {
2259 						add = TRUE;
2260 					}
2261 					break;
2262 				default:
2263 					add = TRUE;
2264 					break;
2265 			}
2266 
2267 			if (add) {
2268 				new_list = g_slist_prepend(new_list, GUINT_TO_POINTER(folder_cur_num));
2269 				debug_print("Remembered message %u for fetching\n", folder_cur_num);
2270 			}
2271 
2272 			/* Move to next folder number */
2273 			if (folder_list_cur)
2274 				folder_list_cur = folder_list_cur->next;
2275 
2276 			if (folder_list_cur != NULL)
2277 				folder_cur_num = GPOINTER_TO_UINT(folder_list_cur->data);
2278 			else
2279 				folder_cur_num = G_MAXUINT;
2280 
2281 			continue;
2282 		}
2283 
2284 		/*
2285 		 *  Message only exists in the cache
2286 		 *  Remove the message from the cache
2287 		 */
2288 		if (cache_cur_num < folder_cur_num) {
2289 			msgcache_remove_msg(item->cache, cache_cur_num);
2290 			debug_print("Removed message %u from cache.\n", cache_cur_num);
2291 
2292 			/* Move to next cache number */
2293 			if (cache_list_cur)
2294 				cache_list_cur = cache_list_cur->next;
2295 
2296 			if (cache_list_cur != NULL)
2297 				cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
2298 			else
2299 				cache_cur_num = G_MAXUINT;
2300 
2301 			update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
2302 
2303 			continue;
2304 		}
2305 
2306 		/*
2307 		 *  Message number exists in folder and cache!
2308 		 *  Check if the message has been modified
2309 		 */
2310 		if (cache_cur_num == folder_cur_num) {
2311 			MsgInfo *msginfo;
2312 
2313 			msginfo = msgcache_get_msg(item->cache, folder_cur_num);
2314 			if (msginfo && folder->klass->is_msg_changed && folder->klass->is_msg_changed(folder, item, msginfo)) {
2315 				msgcache_remove_msg(item->cache, msginfo->msgnum);
2316 				new_list = g_slist_prepend(new_list, GINT_TO_POINTER(msginfo->msgnum));
2317 				procmsg_msginfo_free(&msginfo);
2318 
2319 				debug_print("Remembering message %u to update...\n", folder_cur_num);
2320 			} else if (msginfo) {
2321 				exists_list = g_slist_prepend(exists_list, msginfo);
2322 
2323 				if(prefs_common.thread_by_subject &&
2324 					MSG_IS_IGNORE_THREAD(msginfo->flags) &&
2325 					!subject_table_lookup(subject_table, msginfo->subject)) {
2326 					subject_table_insert(subject_table, msginfo->subject, msginfo);
2327 				}
2328 			}
2329 
2330 			/* Move to next folder and cache number */
2331 			if (cache_list_cur)
2332 				cache_list_cur = cache_list_cur->next;
2333 
2334 			if (folder_list_cur)
2335 				folder_list_cur = folder_list_cur->next;
2336 
2337 			if (cache_list_cur != NULL)
2338 				cache_cur_num = ((MsgInfo *)cache_list_cur->data)->msgnum;
2339 			else
2340 				cache_cur_num = G_MAXUINT;
2341 
2342 			if (folder_list_cur != NULL)
2343 				folder_cur_num = GPOINTER_TO_UINT(folder_list_cur->data);
2344 			else
2345 				folder_cur_num = G_MAXUINT;
2346 
2347 			continue;
2348 		}
2349 	}
2350 
2351 	for(cache_list_cur = cache_list; cache_list_cur != NULL; cache_list_cur = g_slist_next(cache_list_cur))
2352 		procmsg_msginfo_free((MsgInfo **)&(cache_list_cur->data));
2353 
2354 	g_slist_free(cache_list);
2355 	g_slist_free(folder_list);
2356 
2357 	if (new_list != NULL) {
2358 		GSList *tmp_list = NULL;
2359 		newmsg_list = get_msginfos(item, new_list);
2360 		g_slist_free(new_list);
2361 		tmp_list = g_slist_concat(g_slist_copy(exists_list), g_slist_copy(newmsg_list));
2362 		syncronize_flags(item, tmp_list);
2363 		g_slist_free(tmp_list);
2364 	} else {
2365 		syncronize_flags(item, exists_list);
2366 	}
2367 
2368 	folder_item_update_freeze();
2369 
2370 	item->scanning = ITEM_SCANNING;
2371 
2372 	if (newmsg_list != NULL) {
2373 		GSList *elem, *to_filter = NULL;
2374 		gboolean do_filter = (filtering == TRUE) &&
2375 			(item->folder->account != NULL) &&
2376 			(item->folder->account->filter_on_recv) &&
2377 			((item->stype == F_INBOX) ||
2378 			 ((item->stype == F_NORMAL) &&
2379 			  (FOLDER_TYPE(item->folder) == F_NEWS)));
2380 
2381 		for (elem = newmsg_list; elem != NULL; elem = g_slist_next(elem)) {
2382 			MsgInfo *msginfo = (MsgInfo *) elem->data;
2383 
2384 			msgcache_add_msg(item->cache, msginfo);
2385 			if (!do_filter) {
2386 				exists_list = g_slist_prepend(exists_list, msginfo);
2387 
2388 				if(prefs_common.thread_by_subject &&
2389 					MSG_IS_IGNORE_THREAD(msginfo->flags) &&
2390 					!subject_table_lookup(subject_table, msginfo->subject)) {
2391 					subject_table_insert(subject_table, msginfo->subject, msginfo);
2392 				}
2393 			}
2394 		}
2395 
2396 		if (do_filter) {
2397 			GSList *unfiltered;
2398 
2399 			folder_item_set_batch(item, TRUE);
2400 			procmsg_msglist_filter(newmsg_list, item->folder->account,
2401 					&to_filter, &unfiltered,
2402 					TRUE);
2403 			folder_item_set_batch(item, FALSE);
2404 
2405 			filtering_move_and_copy_msgs(newmsg_list);
2406 			if (to_filter != NULL) {
2407 				for (elem = to_filter; elem; elem = g_slist_next(elem)) {
2408 					MsgInfo *msginfo = (MsgInfo *)elem->data;
2409 					procmsg_msginfo_free(&msginfo);
2410 				}
2411 				g_slist_free(to_filter);
2412 			}
2413 			if (unfiltered != NULL) {
2414 				for (elem = unfiltered; elem; elem = g_slist_next(elem)) {
2415 					MsgInfo *msginfo = (MsgInfo *)elem->data;
2416 					exists_list = g_slist_prepend(exists_list, msginfo);
2417 
2418 					if(prefs_common.thread_by_subject &&
2419 						MSG_IS_IGNORE_THREAD(msginfo->flags) &&
2420 						!subject_table_lookup(subject_table, msginfo->subject)) {
2421 						subject_table_insert(subject_table, msginfo->subject, msginfo);
2422 					}
2423 				}
2424 				g_slist_free(unfiltered);
2425 			}
2426 			if (prefs_common.real_time_sync)
2427 				folder_item_synchronise(item);
2428 		} else {
2429 			if (prefs_common.real_time_sync)
2430 				folder_item_synchronise(item);
2431 		}
2432 
2433 		g_slist_free(newmsg_list);
2434 
2435 		update_flags |= F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT;
2436 	}
2437 
2438 	folder_item_set_batch(item, TRUE);
2439 	for (elem = exists_list; elem != NULL; elem = g_slist_next(elem)) {
2440 		MsgInfo *msginfo, *parent_msginfo;
2441 
2442 		msginfo = elem->data;
2443 		if (MSG_IS_IGNORE_THREAD(msginfo->flags) && (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
2444 			procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
2445 		if (!MSG_IS_IGNORE_THREAD(msginfo->flags) && procmsg_msg_has_flagged_parent(msginfo, MSG_IGNORE_THREAD)) {
2446 			procmsg_msginfo_change_flags(msginfo, MSG_IGNORE_THREAD, 0, MSG_NEW | MSG_UNREAD, 0);
2447 		}
2448 		if (!MSG_IS_WATCH_THREAD(msginfo->flags) && procmsg_msg_has_flagged_parent(msginfo, MSG_WATCH_THREAD)) {
2449 			procmsg_msginfo_set_flags(msginfo, MSG_WATCH_THREAD, 0);
2450 		}
2451 		if(prefs_common.thread_by_subject && !msginfo->inreplyto &&
2452 			!msginfo->references && !MSG_IS_IGNORE_THREAD(msginfo->flags) &&
2453 			(parent_msginfo = subject_table_lookup(subject_table, msginfo->subject)))
2454 		{
2455 			if(MSG_IS_IGNORE_THREAD(parent_msginfo->flags)) {
2456 				procmsg_msginfo_change_flags(msginfo, MSG_IGNORE_THREAD, 0,
2457 						MSG_NEW | MSG_UNREAD, 0);
2458 			}
2459 		}
2460 		if ((folder_has_parent_of_type(item, F_OUTBOX) ||
2461 		     folder_has_parent_of_type(item, F_QUEUE)  ||
2462 		     folder_has_parent_of_type(item, F_TRASH)) &&
2463 		    (MSG_IS_NEW(msginfo->flags) || MSG_IS_UNREAD(msginfo->flags)))
2464 			procmsg_msginfo_unset_flags(msginfo, MSG_NEW | MSG_UNREAD, 0);
2465 		if (MSG_IS_NEW(msginfo->flags))
2466 			newcnt++;
2467 		if (MSG_IS_UNREAD(msginfo->flags))
2468 			unreadcnt++;
2469 		if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
2470 			unreadmarkedcnt++;
2471 		if (MSG_IS_MARKED(msginfo->flags))
2472 			markedcnt++;
2473 		if (MSG_IS_REPLIED(msginfo->flags))
2474 			repliedcnt++;
2475 		if (MSG_IS_FORWARDED(msginfo->flags))
2476 			forwardedcnt++;
2477 		if (MSG_IS_LOCKED(msginfo->flags))
2478 			lockedcnt++;
2479 		if (MSG_IS_IGNORE_THREAD(msginfo->flags))
2480 			ignoredcnt++;
2481 		if (MSG_IS_WATCH_THREAD(msginfo->flags))
2482 			watchedcnt++;
2483 
2484 		totalcnt++;
2485 
2486 		procmsg_msginfo_free(&msginfo);
2487 	}
2488 	folder_item_set_batch(item, FALSE);
2489 	g_slist_free(exists_list);
2490 
2491 	if(prefs_common.thread_by_subject) {
2492 		g_hash_table_destroy(subject_table);
2493 	}
2494 
2495 	if (item->new_msgs != newcnt || item->unread_msgs != unreadcnt
2496 	||  item->total_msgs != totalcnt || item->marked_msgs != markedcnt
2497 	||  item->unreadmarked_msgs != unreadmarkedcnt
2498 	||  item->replied_msgs != repliedcnt || item->forwarded_msgs != forwardedcnt
2499 	||  item->locked_msgs != lockedcnt || item->ignored_msgs != ignoredcnt
2500 	||  item->watched_msgs != watchedcnt) {
2501 		update_flags |= F_ITEM_UPDATE_CONTENT;
2502 	}
2503 
2504 	item->new_msgs = newcnt;
2505 	item->unread_msgs = unreadcnt;
2506 	item->total_msgs = totalcnt;
2507 	item->unreadmarked_msgs = unreadmarkedcnt;
2508 	item->marked_msgs = markedcnt;
2509 	item->replied_msgs = repliedcnt;
2510 	item->forwarded_msgs = forwardedcnt;
2511 	item->locked_msgs = lockedcnt;
2512 	item->ignored_msgs = ignoredcnt;
2513 	item->watched_msgs = watchedcnt;
2514 
2515 	update_flags |= F_ITEM_UPDATE_MSGCNT;
2516 
2517 	folder_item_update(item, update_flags);
2518 	folder_item_update_thaw();
2519 
2520 	item->scanning = ITEM_NOT_SCANNING;
2521 
2522 	return 0;
2523 }
2524 
folder_item_scan(FolderItem * item)2525 gint folder_item_scan(FolderItem *item)
2526 {
2527 	return folder_item_scan_full(item, TRUE);
2528 }
2529 
folder_count_total_cache_memusage(FolderItem * item,gpointer data)2530 static void folder_count_total_cache_memusage(FolderItem *item, gpointer data)
2531 {
2532 	gint *memusage = (gint *)data;
2533 
2534 	if (item->cache == NULL)
2535 		return;
2536 
2537 	*memusage += msgcache_get_memory_usage(item->cache);
2538 }
2539 
folder_cache_time_compare_func(gconstpointer a,gconstpointer b)2540 static gint folder_cache_time_compare_func(gconstpointer a, gconstpointer b)
2541 {
2542 	FolderItem *fa = (FolderItem *)a;
2543 	FolderItem *fb = (FolderItem *)b;
2544 
2545 	return (gint) (msgcache_get_last_access_time(fa->cache) - msgcache_get_last_access_time(fb->cache));
2546 }
2547 
folder_find_expired_caches(FolderItem * item,gpointer data)2548 static void folder_find_expired_caches(FolderItem *item, gpointer data)
2549 {
2550 	GSList **folder_item_list = (GSList **)data;
2551 	gint difftime, expiretime;
2552 
2553 	if (item->cache == NULL)
2554 		return;
2555 
2556 	if (item->opened > 0)
2557 		return;
2558 
2559 	difftime = (gint) (time(NULL) - msgcache_get_last_access_time(item->cache));
2560 	expiretime = prefs_common.cache_min_keep_time * 60;
2561 	debug_print("Cache unused time: %d (Expire time: %d)\n", difftime, expiretime);
2562 
2563 	if (difftime > expiretime && !item->opened && !item->processing_pending) {
2564 		*folder_item_list = g_slist_insert_sorted(*folder_item_list, item, folder_cache_time_compare_func);
2565 	}
2566 }
2567 
folder_item_free_cache(FolderItem * item,gboolean force)2568 gboolean folder_item_free_cache(FolderItem *item, gboolean force)
2569 {
2570 	cm_return_val_if_fail(item != NULL, TRUE);
2571 
2572 	if (item->cache == NULL)
2573 		return TRUE;
2574 
2575 	if (item->opened > 0 && !force)
2576 		return FALSE;
2577 
2578 	folder_item_write_cache(item);
2579 	msgcache_destroy(item->cache);
2580 	item->cache = NULL;
2581 	return TRUE;
2582 }
2583 
folder_clean_cache_memory_force(void)2584 void folder_clean_cache_memory_force(void)
2585 {
2586 	int old_cache_max_mem_usage = prefs_common.cache_max_mem_usage;
2587 	int old_cache_min_keep_time = prefs_common.cache_min_keep_time;
2588 
2589 	prefs_common.cache_max_mem_usage = 0;
2590 	prefs_common.cache_min_keep_time = 0;
2591 
2592 	folder_clean_cache_memory(NULL);
2593 
2594 	prefs_common.cache_max_mem_usage = old_cache_max_mem_usage;
2595 	prefs_common.cache_min_keep_time = old_cache_min_keep_time;
2596 }
2597 
folder_clean_cache_memory(FolderItem * protected_item)2598 void folder_clean_cache_memory(FolderItem *protected_item)
2599 {
2600 	gint memusage = 0;
2601 
2602 	folder_func_to_all_folders(folder_count_total_cache_memusage, &memusage);
2603 	debug_print("Total cache memory usage: %d\n", memusage);
2604 
2605 	if (memusage > (prefs_common.cache_max_mem_usage * 1024)) {
2606 		GSList *folder_item_list = NULL, *listitem;
2607 
2608 		debug_print("Trying to free cache memory\n");
2609 
2610 		folder_func_to_all_folders(folder_find_expired_caches, &folder_item_list);
2611 		listitem = folder_item_list;
2612 		while((listitem != NULL) && (memusage > (prefs_common.cache_max_mem_usage * 1024))) {
2613 			FolderItem *item = (FolderItem *)(listitem->data);
2614 			gint cache_size = 0;
2615 			if (item == protected_item) {
2616 				listitem = listitem->next;
2617 				continue;
2618 			}
2619 			debug_print("Freeing cache memory for %s\n", item->path ? item->path : item->name);
2620 			cache_size = msgcache_get_memory_usage(item->cache);
2621 		        if (folder_item_free_cache(item, FALSE))
2622 				memusage -= cache_size;
2623 
2624 			listitem = listitem->next;
2625 		}
2626 		g_slist_free(folder_item_list);
2627 	}
2628 }
2629 
folder_item_remove_cached_msg(FolderItem * item,MsgInfo * msginfo)2630 static void folder_item_remove_cached_msg(FolderItem *item, MsgInfo *msginfo)
2631 {
2632 	Folder *folder = item->folder;
2633 
2634 	cm_return_if_fail(folder != NULL);
2635 
2636 	if (folder->klass->remove_cached_msg == NULL)
2637 		return;
2638 
2639 	folder->klass->remove_cached_msg(folder, item, msginfo);
2640 }
2641 
folder_item_clean_local_files(FolderItem * item,gint days)2642 static void folder_item_clean_local_files(FolderItem *item, gint days)
2643 {
2644 	cm_return_if_fail(item != NULL);
2645 	cm_return_if_fail(item->folder != NULL);
2646 
2647 	if (FOLDER_TYPE(item->folder) == F_IMAP ||
2648 	    FOLDER_TYPE(item->folder) == F_NEWS) {
2649 		GSList *msglist = folder_item_get_msg_list(item);
2650 		GSList *cur;
2651 		time_t t = time(NULL);
2652 		for (cur = msglist; cur; cur = cur->next) {
2653 			MsgInfo *msginfo = (MsgInfo *)cur->data;
2654 			gint age = (t - msginfo->date_t) / (60*60*24);
2655 			if (age > days)
2656 				folder_item_remove_cached_msg(item, msginfo);
2657 		}
2658 		procmsg_msg_list_free(msglist);
2659 	}
2660 }
2661 
folder_item_read_cache(FolderItem * item)2662 static void folder_item_read_cache(FolderItem *item)
2663 {
2664 	gchar *cache_file, *mark_file, *tags_file;
2665 	START_TIMING("");
2666 	cm_return_if_fail(item != NULL);
2667 
2668 	if (item->path != NULL) {
2669 	        cache_file = folder_item_get_cache_file(item);
2670 		mark_file = folder_item_get_mark_file(item);
2671 		tags_file = folder_item_get_tags_file(item);
2672 		item->cache = msgcache_read_cache(item, cache_file);
2673 		item->cache_dirty = FALSE;
2674 		item->mark_dirty = FALSE;
2675 		item->tags_dirty = FALSE;
2676 		if (!item->cache) {
2677 			MsgInfoList *list, *cur;
2678 			guint newcnt = 0, unreadcnt = 0;
2679 			guint markedcnt = 0, unreadmarkedcnt = 0;
2680 			guint repliedcnt = 0, forwardedcnt = 0;
2681 			guint lockedcnt = 0, ignoredcnt = 0;
2682 			guint watchedcnt = 0;
2683 			MsgInfo *msginfo;
2684 
2685 			item->cache = msgcache_new();
2686 			item->cache_dirty = TRUE;
2687 			item->mark_dirty = TRUE;
2688 			item->tags_dirty = TRUE;
2689 			folder_item_scan_full(item, TRUE);
2690 
2691 			msgcache_read_mark(item->cache, mark_file);
2692 
2693 			list = msgcache_get_msg_list(item->cache);
2694 			for (cur = list; cur != NULL; cur = g_slist_next(cur)) {
2695 				msginfo = cur->data;
2696 
2697 				if (MSG_IS_NEW(msginfo->flags))
2698 					newcnt++;
2699 				if (MSG_IS_UNREAD(msginfo->flags))
2700 					unreadcnt++;
2701 				if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
2702 					unreadmarkedcnt++;
2703 				if (MSG_IS_MARKED(msginfo->flags))
2704 					markedcnt++;
2705 				if (MSG_IS_REPLIED(msginfo->flags))
2706 					repliedcnt++;
2707 				if (MSG_IS_FORWARDED(msginfo->flags))
2708 					forwardedcnt++;
2709 				if (MSG_IS_LOCKED(msginfo->flags))
2710 					lockedcnt++;
2711 				if (MSG_IS_IGNORE_THREAD(msginfo->flags))
2712 					ignoredcnt++;
2713 				if (MSG_IS_WATCH_THREAD(msginfo->flags))
2714 					watchedcnt++;
2715 				procmsg_msginfo_unset_flags(msginfo, MSG_FULLY_CACHED, 0);
2716 			}
2717 			item->new_msgs = newcnt;
2718 		        item->unread_msgs = unreadcnt;
2719 			item->unreadmarked_msgs = unreadmarkedcnt;
2720 			item->marked_msgs = markedcnt;
2721 			item->replied_msgs = repliedcnt;
2722 			item->forwarded_msgs = forwardedcnt;
2723 			item->locked_msgs = lockedcnt;
2724 			item->ignored_msgs = ignoredcnt;
2725 			item->watched_msgs = watchedcnt;
2726 			procmsg_msg_list_free(list);
2727 		} else
2728 			msgcache_read_mark(item->cache, mark_file);
2729 
2730 		msgcache_read_tags(item->cache, tags_file);
2731 
2732 		g_free(cache_file);
2733 		g_free(mark_file);
2734 		g_free(tags_file);
2735 	} else {
2736 		item->cache = msgcache_new();
2737 		item->cache_dirty = TRUE;
2738 		item->mark_dirty = TRUE;
2739 		item->tags_dirty = TRUE;
2740 	}
2741 
2742 	END_TIMING();
2743 	folder_clean_cache_memory(item);
2744 }
2745 
folder_item_write_cache(FolderItem * item)2746 void folder_item_write_cache(FolderItem *item)
2747 {
2748 	gchar *cache_file = NULL, *mark_file = NULL, *tags_file = NULL;
2749 	FolderItemPrefs *prefs;
2750 	gint filemode = 0;
2751 	gchar *id;
2752 	time_t last_mtime = (time_t)0;
2753 	gboolean need_scan = FALSE;
2754 
2755 	if (!item || !item->path || !item->cache)
2756 		return;
2757 
2758 	last_mtime = item->mtime;
2759 	if (item->folder->klass->set_mtime) {
2760 		if (item->folder->klass->scan_required)
2761 			need_scan = item->folder->klass->scan_required(item->folder, item);
2762 		else
2763 			need_scan = TRUE;
2764 	}
2765 
2766 	id = folder_item_get_identifier(item);
2767 	debug_print("Save cache for folder %s\n", id);
2768 	g_free(id);
2769 
2770 	if (item->cache_dirty)
2771 		cache_file = folder_item_get_cache_file(item);
2772 	if (item->cache_dirty || item->mark_dirty)
2773 		mark_file = folder_item_get_mark_file(item);
2774 	if (item->cache_dirty || item->tags_dirty)
2775 		tags_file = folder_item_get_tags_file(item);
2776 	if (msgcache_write(cache_file, mark_file, tags_file, item->cache) == 0) {
2777 		prefs = item->prefs;
2778 		if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
2779 			filemode = prefs->folder_chmod;
2780 			if (filemode & S_IRGRP) filemode |= S_IWGRP;
2781 			if (filemode & S_IROTH) filemode |= S_IWOTH;
2782 			if (cache_file) {
2783 				if (chmod(cache_file, filemode) < 0)
2784 					FILE_OP_ERROR(cache_file, "chmod");
2785 			}
2786 			if (mark_file) {
2787 				if (chmod(mark_file, filemode) < 0)
2788 					FILE_OP_ERROR(mark_file, "chmod");
2789 			}
2790 		}
2791         } else {
2792 		item->cache_dirty = TRUE;
2793 		item->mark_dirty = TRUE;
2794 		item->tags_dirty = TRUE;
2795 	}
2796 
2797 	if (!need_scan && item->folder->klass->set_mtime) {
2798 		if (item->mtime == last_mtime) {
2799 			item->folder->klass->set_mtime(item->folder, item);
2800 		}
2801 	}
2802 
2803 	g_free(cache_file);
2804 	g_free(mark_file);
2805 	g_free(tags_file);
2806 }
2807 
folder_item_get_msginfo(FolderItem * item,gint num)2808 MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num)
2809 {
2810 	MsgInfo *msginfo = NULL;
2811 
2812 	cm_return_val_if_fail(item != NULL, NULL);
2813 	if (item->no_select)
2814 		return NULL;
2815 	if (!item->cache)
2816 		folder_item_read_cache(item);
2817 
2818 	if ((msginfo = msgcache_get_msg(item->cache, num)) != NULL)
2819 		return msginfo;
2820 
2821 	msginfo = get_msginfo(item, num);
2822 	if (msginfo != NULL) {
2823 		msgcache_add_msg(item->cache, msginfo);
2824 		return msginfo;
2825 	}
2826 
2827 	return NULL;
2828 }
2829 
folder_item_get_msginfo_by_msgid(FolderItem * item,const gchar * msgid)2830 MsgInfo *folder_item_get_msginfo_by_msgid(FolderItem *item, const gchar *msgid)
2831 {
2832 	MsgInfo *msginfo;
2833 
2834 	cm_return_val_if_fail(item != NULL, NULL);
2835 	cm_return_val_if_fail(msgid != NULL, NULL);
2836 	if (item->no_select)
2837 		return NULL;
2838 
2839 	if (!item->cache)
2840 		folder_item_read_cache(item);
2841 
2842 	if ((msginfo = msgcache_get_msg_by_id(item->cache, msgid)) != NULL)
2843 		return msginfo;
2844 
2845 	return NULL;
2846 }
2847 
folder_item_get_msg_list(FolderItem * item)2848 GSList *folder_item_get_msg_list(FolderItem *item)
2849 {
2850 	cm_return_val_if_fail(item != NULL, NULL);
2851 	if (item->no_select)
2852 		return NULL;
2853 
2854 	if (item->cache == 0)
2855 		folder_item_read_cache(item);
2856 
2857 	cm_return_val_if_fail(item->cache != NULL, NULL);
2858 
2859 	return msgcache_get_msg_list(item->cache);
2860 }
2861 
msginfo_set_mime_flags(GNode * node,gpointer data)2862 static void msginfo_set_mime_flags(GNode *node, gpointer data)
2863 {
2864 	MsgInfo *msginfo = data;
2865 	MimeInfo *mimeinfo = node->data;
2866 
2867 	if (mimeinfo->disposition == DISPOSITIONTYPE_ATTACHMENT &&
2868 	    (!mimeinfo->subtype || (strcmp(mimeinfo->subtype, "pgp-signature") &&
2869 	     strcmp(mimeinfo->subtype, "x-pkcs7-signature") &&
2870 	     strcmp(mimeinfo->subtype, "pkcs7-signature")))) {
2871 		procmsg_msginfo_set_flags(msginfo, 0, MSG_HAS_ATTACHMENT);
2872 	} else if (mimeinfo->disposition == DISPOSITIONTYPE_UNKNOWN &&
2873 		 mimeinfo->id == NULL &&
2874 		 mimeinfo->type != MIMETYPE_TEXT &&
2875 		 mimeinfo->type != MIMETYPE_MULTIPART) {
2876 		if (!mimeinfo->subtype
2877 		|| (strcmp(mimeinfo->subtype, "pgp-signature") &&
2878 		    strcmp(mimeinfo->subtype, "x-pkcs7-signature") &&
2879 		    strcmp(mimeinfo->subtype, "pkcs7-signature")))
2880 			procmsg_msginfo_set_flags(msginfo, 0, MSG_HAS_ATTACHMENT);
2881 	} else if (mimeinfo->disposition == DISPOSITIONTYPE_INLINE &&
2882 		 mimeinfo->id == NULL &&
2883 		(strcmp(mimeinfo->subtype, "pgp-signature") &&
2884 		 strcmp(mimeinfo->subtype, "x-pkcs7-signature") &&
2885 		 strcmp(mimeinfo->subtype, "pkcs7-signature")) &&
2886 		(procmime_mimeinfo_get_parameter(mimeinfo, "name") != NULL ||
2887 		 procmime_mimeinfo_get_parameter(mimeinfo, "filename") != NULL)) {
2888 		procmsg_msginfo_set_flags(msginfo, 0, MSG_HAS_ATTACHMENT);
2889 	}
2890 
2891 	/* don't descend below top level message for signed and encrypted info */
2892 	if (mimeinfo->type == MIMETYPE_MESSAGE)
2893 		return;
2894 
2895 	if (privacy_mimeinfo_is_signed(mimeinfo)) {
2896 		procmsg_msginfo_set_flags(msginfo, 0, MSG_SIGNED);
2897 	}
2898 
2899 	if (privacy_mimeinfo_is_encrypted(mimeinfo)) {
2900 		procmsg_msginfo_set_flags(msginfo, 0, MSG_ENCRYPTED);
2901 	} else {
2902 		/* searching inside encrypted parts doesn't really make sense */
2903 		g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_mime_flags, msginfo);
2904 	}
2905 }
2906 
folder_item_fetch_msg(FolderItem * item,gint num)2907 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
2908 {
2909 	Folder *folder;
2910 	gchar *msgfile;
2911 	MsgInfo *msginfo;
2912 
2913 	cm_return_val_if_fail(item != NULL, NULL);
2914 
2915 	folder = item->folder;
2916 
2917 	cm_return_val_if_fail(folder->klass->fetch_msg != NULL, NULL);
2918 	if (item->no_select)
2919 		return NULL;
2920 
2921 	msgfile = folder->klass->fetch_msg(folder, item, num);
2922 
2923 	if (msgfile != NULL) {
2924 		msginfo = folder_item_get_msginfo(item, num);
2925 		if ((msginfo != NULL) && !MSG_IS_SCANNED(msginfo->flags)) {
2926 			MimeInfo *mimeinfo;
2927 
2928 			if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2929 			    !folder_has_parent_of_type(msginfo->folder, F_DRAFT))
2930 				mimeinfo = procmime_scan_file(msgfile);
2931 			else
2932 				mimeinfo = procmime_scan_queue_file(msgfile);
2933 			/* check for attachments */
2934 			if (mimeinfo != NULL) {
2935 				g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_mime_flags, msginfo);
2936 				procmime_mimeinfo_free_all(&mimeinfo);
2937 
2938 				procmsg_msginfo_set_flags(msginfo, 0, MSG_SCANNED);
2939 			}
2940 		}
2941 		procmsg_msginfo_free(&msginfo);
2942 	}
2943 
2944 	return msgfile;
2945 }
2946 
folder_item_fetch_msg_full(FolderItem * item,gint num,gboolean headers,gboolean body)2947 gchar *folder_item_fetch_msg_full(FolderItem *item, gint num, gboolean headers,
2948 				  gboolean body)
2949 {
2950 	Folder *folder;
2951 	gchar *msgfile;
2952 	MsgInfo *msginfo;
2953 
2954 	cm_return_val_if_fail(item != NULL, NULL);
2955 	if (item->no_select)
2956 		return NULL;
2957 
2958 	folder = item->folder;
2959 
2960 	if (folder->klass->fetch_msg_full == NULL)
2961 		return folder_item_fetch_msg(item, num);
2962 
2963 	if (item->prefs->offlinesync && prefs_common.real_time_sync)
2964 		msgfile = folder->klass->fetch_msg_full(folder, item, num,
2965 						TRUE, TRUE);
2966 	else
2967 		msgfile = folder->klass->fetch_msg_full(folder, item, num,
2968 						headers, body);
2969 
2970 	if (msgfile != NULL) {
2971 		msginfo = folder_item_get_msginfo(item, num);
2972 		if ((msginfo != NULL) && !MSG_IS_SCANNED(msginfo->flags)) {
2973 			MimeInfo *mimeinfo;
2974 
2975 			if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2976 			    !folder_has_parent_of_type(msginfo->folder, F_DRAFT))
2977 				mimeinfo = procmime_scan_file(msgfile);
2978 			else
2979 				mimeinfo = procmime_scan_queue_file(msgfile);
2980 			/* check for attachments */
2981 			if (mimeinfo != NULL) {
2982 				g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_mime_flags, msginfo);
2983 				procmime_mimeinfo_free_all(&mimeinfo);
2984 
2985 				procmsg_msginfo_set_flags(msginfo, 0, MSG_SCANNED);
2986 			}
2987 		}
2988 		procmsg_msginfo_free(&msginfo);
2989 	}
2990 
2991 	return msgfile;
2992 }
2993 
2994 
folder_item_get_msg_num_by_file(FolderItem * dest,const gchar * file)2995 static gint folder_item_get_msg_num_by_file(FolderItem *dest, const gchar *file)
2996 {
2997 	static HeaderEntry hentry[] = {{"Message-ID:",  NULL, TRUE},
2998 				       {NULL,		NULL, FALSE}};
2999 	FILE *fp;
3000 	MsgInfo *msginfo;
3001 	gint msgnum = 0;
3002 	gchar buf[BUFFSIZE];
3003 
3004 	if ((fp = claws_fopen(file, "rb")) == NULL)
3005 		return 0;
3006 
3007 	if ((folder_has_parent_of_type(dest, F_QUEUE)) ||
3008 	    (folder_has_parent_of_type(dest, F_DRAFT)))
3009 		while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
3010 			/* new way */
3011 			if ((!strncmp(buf, "X-Claws-End-Special-Headers: 1",
3012 				strlen("X-Claws-End-Special-Headers:"))) ||
3013 			    (!strncmp(buf, "X-Sylpheed-End-Special-Headers: 1",
3014 				strlen("X-Sylpheed-End-Special-Headers:"))))
3015 				break;
3016 			/* old way */
3017 			if (buf[0] == '\r' || buf[0] == '\n') break;
3018 			/* from other mailers */
3019 			if (!strncmp(buf, "Date: ", 6)
3020 			||  !strncmp(buf, "To: ", 4)
3021 			||  !strncmp(buf, "From: ", 6)
3022 			||  !strncmp(buf, "Subject: ", 9)) {
3023 				rewind(fp);
3024 				break;
3025 			}
3026 		}
3027 
3028 	procheader_get_header_fields(fp, hentry);
3029 	debug_print("looking for %s\n", hentry[0].body);
3030 	if (hentry[0].body) {
3031     		extract_parenthesis(hentry[0].body, '<', '>');
3032 		remove_space(hentry[0].body);
3033 		if ((msginfo = msgcache_get_msg_by_id(dest->cache, hentry[0].body)) != NULL) {
3034 			msgnum = msginfo->msgnum;
3035 			procmsg_msginfo_free(&msginfo);
3036 
3037 			debug_print("found message as uid %d\n", msgnum);
3038 		}
3039 	}
3040 
3041 	g_free(hentry[0].body);
3042 	hentry[0].body = NULL;
3043 	claws_fclose(fp);
3044 
3045 	return msgnum;
3046 }
3047 
copy_msginfo_flags(MsgInfo * source,MsgInfo * dest)3048 static void copy_msginfo_flags(MsgInfo *source, MsgInfo *dest)
3049 {
3050 	MsgPermFlags perm_flags = 0;
3051 	MsgTmpFlags tmp_flags = 0;
3052 
3053 	/* create new flags */
3054 	if (source != NULL) {
3055 		/* copy original flags */
3056 		perm_flags = source->flags.perm_flags;
3057 		tmp_flags = source->flags.tmp_flags;
3058 	} else {
3059 		perm_flags = dest->flags.perm_flags;
3060 		tmp_flags = dest->flags.tmp_flags;
3061 	}
3062 
3063 	/* remove new, unread and deleted in special folders */
3064 	if (folder_has_parent_of_type(dest->folder, F_OUTBOX) ||
3065 	    folder_has_parent_of_type(dest->folder, F_QUEUE) ||
3066 	    folder_has_parent_of_type(dest->folder, F_DRAFT) ||
3067 	    folder_has_parent_of_type(dest->folder, F_TRASH))
3068 		perm_flags &= ~(MSG_NEW | MSG_UNREAD | MSG_DELETED);
3069 
3070 	/* set ignore flag of ignored parent exists */
3071 	if (procmsg_msg_has_flagged_parent(dest, MSG_IGNORE_THREAD))
3072 		perm_flags |= MSG_IGNORE_THREAD;
3073 
3074 	/* unset FULLY_CACHED flags */
3075 	perm_flags &= ~MSG_FULLY_CACHED;
3076 
3077 	if (procmsg_msg_has_flagged_parent(dest, MSG_WATCH_THREAD))
3078 		perm_flags |= MSG_WATCH_THREAD;
3079 
3080 	/* Unset tmp flags that should not be copied */
3081 	tmp_flags &= ~(MSG_MOVE | MSG_COPY | MSG_MOVE_DONE);
3082 
3083 	/* unset flags that are set but should not */
3084 	/* and set new flags */
3085 	procmsg_msginfo_change_flags(dest,
3086 				  ~dest->flags.perm_flags & perm_flags,
3087 				  ~dest->flags.tmp_flags  & tmp_flags,
3088 				   dest->flags.perm_flags & ~perm_flags,
3089 				   dest->flags.tmp_flags  & ~tmp_flags);
3090 
3091 	if (source && source->tags) {
3092 		g_slist_free(dest->tags);
3093 		dest->tags = g_slist_copy(source->tags);
3094 		folder_item_commit_tags(dest->folder, dest, dest->tags, NULL);
3095 	}
3096 }
3097 
add_msginfo_to_cache(FolderItem * item,MsgInfo * newmsginfo,MsgInfo * flagsource)3098 static void add_msginfo_to_cache(FolderItem *item, MsgInfo *newmsginfo, MsgInfo *flagsource)
3099 {
3100 	/* update folder stats */
3101 	if (MSG_IS_NEW(newmsginfo->flags))
3102 		item->new_msgs++;
3103 	if (MSG_IS_UNREAD(newmsginfo->flags))
3104 		item->unread_msgs++;
3105 	if (MSG_IS_UNREAD(newmsginfo->flags) && procmsg_msg_has_marked_parent(newmsginfo))
3106 		item->unreadmarked_msgs++;
3107 	if (MSG_IS_MARKED(newmsginfo->flags))
3108 		item->marked_msgs++;
3109 	if (MSG_IS_REPLIED(newmsginfo->flags))
3110 		item->replied_msgs++;
3111 	if (MSG_IS_FORWARDED(newmsginfo->flags))
3112 		item->forwarded_msgs++;
3113 	if (MSG_IS_LOCKED(newmsginfo->flags))
3114 		item->locked_msgs++;
3115 	if (MSG_IS_IGNORE_THREAD(newmsginfo->flags))
3116 		item->ignored_msgs++;
3117 	if (MSG_IS_WATCH_THREAD(newmsginfo->flags))
3118 		item->watched_msgs++;
3119 	item->total_msgs++;
3120 
3121 	folder_item_update_freeze();
3122 
3123 	if (!item->cache)
3124 		folder_item_read_cache(item);
3125 
3126 	msgcache_add_msg(item->cache, newmsginfo);
3127 	copy_msginfo_flags(flagsource, newmsginfo);
3128 	folder_item_update_with_msg(item,  F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT | F_ITEM_UPDATE_ADDMSG, newmsginfo);
3129 	folder_item_update_thaw();
3130 }
3131 
remove_msginfo_from_cache(FolderItem * item,MsgInfo * msginfo)3132 static void remove_msginfo_from_cache(FolderItem *item, MsgInfo *msginfo)
3133 {
3134 	MsgInfoUpdate msginfo_update;
3135 
3136 	if (!item->cache)
3137 		folder_item_read_cache(item);
3138 
3139 	if (MSG_IS_NEW(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
3140 		msginfo->folder->new_msgs--;
3141 	if (MSG_IS_UNREAD(msginfo->flags) && !MSG_IS_IGNORE_THREAD(msginfo->flags))
3142 		msginfo->folder->unread_msgs--;
3143 	if (MSG_IS_UNREAD(msginfo->flags) && procmsg_msg_has_marked_parent(msginfo))
3144 		msginfo->folder->unreadmarked_msgs--;
3145 	if (MSG_IS_MARKED(msginfo->flags))
3146 		item->marked_msgs--;
3147 	if (MSG_IS_REPLIED(msginfo->flags))
3148 		item->replied_msgs--;
3149 	if (MSG_IS_FORWARDED(msginfo->flags))
3150 		item->forwarded_msgs--;
3151 	if (MSG_IS_LOCKED(msginfo->flags))
3152 		item->locked_msgs--;
3153 	if (MSG_IS_IGNORE_THREAD(msginfo->flags))
3154 		item->ignored_msgs--;
3155 	if (MSG_IS_WATCH_THREAD(msginfo->flags))
3156 		item->watched_msgs--;
3157 
3158 	msginfo->folder->total_msgs--;
3159 
3160 	msginfo_update.msginfo = msginfo;
3161 	msginfo_update.flags = MSGINFO_UPDATE_DELETED;
3162 	hooks_invoke(MSGINFO_UPDATE_HOOKLIST, &msginfo_update);
3163 
3164 	msgcache_remove_msg(item->cache, msginfo->msgnum);
3165 	folder_item_update_with_msg(msginfo->folder, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT | F_ITEM_UPDATE_REMOVEMSG, msginfo);
3166 }
3167 
folder_item_add_msg(FolderItem * dest,const gchar * file,MsgFlags * flags,gboolean remove_source)3168 gint folder_item_add_msg(FolderItem *dest, const gchar *file,
3169 			 MsgFlags *flags, gboolean remove_source)
3170 {
3171         GSList file_list;
3172         MsgFileInfo fileinfo;
3173 
3174 	cm_return_val_if_fail(dest != NULL, -1);
3175 	cm_return_val_if_fail(file != NULL, -1);
3176 
3177 	fileinfo.msginfo = NULL;
3178         fileinfo.file = (gchar *)file;
3179         fileinfo.flags = flags;
3180         file_list.data = &fileinfo;
3181         file_list.next = NULL;
3182 
3183 	return folder_item_add_msgs(dest, &file_list, remove_source);
3184 }
3185 
folder_item_add_msgs(FolderItem * dest,GSList * file_list,gboolean remove_source)3186 gint folder_item_add_msgs(FolderItem *dest, GSList *file_list,
3187                           gboolean remove_source)
3188 {
3189         Folder *folder;
3190         gint ret, num, lastnum = -1;
3191 	GSList *file_cur;
3192 	GHashTable *relation;
3193 	MsgFileInfo *fileinfo = NULL;
3194 	gboolean folderscan = FALSE;
3195 
3196         cm_return_val_if_fail(dest != NULL, -1);
3197         cm_return_val_if_fail(file_list != NULL, -1);
3198         cm_return_val_if_fail(dest->folder != NULL, -1);
3199 	if (dest->no_select)
3200 		return -1;
3201 
3202         folder = dest->folder;
3203 
3204 	relation = g_hash_table_new(g_direct_hash, g_direct_equal);
3205 
3206 	if (folder->klass->add_msgs != NULL) {
3207     		ret = folder->klass->add_msgs(folder, dest, file_list, relation);
3208 		if (ret < 0) {
3209 			g_hash_table_destroy(relation);
3210 			return ret;
3211 		}
3212 	} else {
3213 		for (file_cur = file_list; file_cur != NULL; file_cur = g_slist_next(file_cur)) {
3214 			fileinfo = (MsgFileInfo *) file_cur->data;
3215 
3216     			ret = folder->klass->add_msg(folder, dest, fileinfo->file, fileinfo->flags);
3217 			if (ret < 0) {
3218 				g_hash_table_destroy(relation);
3219 				return ret;
3220 			}
3221 			g_hash_table_insert(relation, fileinfo, GINT_TO_POINTER(ret));
3222 		}
3223 	}
3224 
3225 	for (file_cur = file_list; file_cur != NULL; file_cur = g_slist_next(file_cur)) {
3226 		gpointer data, old_key;
3227 
3228 		fileinfo = (MsgFileInfo *) file_cur->data;
3229 		if (g_hash_table_lookup_extended(relation, fileinfo, &old_key, &data))
3230 			num = GPOINTER_TO_INT(data);
3231 		else
3232 			num = -1;
3233 
3234 		if (num >= 0) {
3235 			MsgInfo *newmsginfo;
3236 
3237 			if (num == 0) {
3238 				if (!folderscan) {
3239 					folder_item_scan_full(dest, FALSE);
3240 					folderscan = TRUE;
3241 				}
3242 				num = folder_item_get_msg_num_by_file(dest, fileinfo->file);
3243 				debug_print("got num %d\n", num);
3244 			}
3245 
3246 			if (num > lastnum)
3247 				lastnum = num;
3248 
3249 			if (num >= 0 && remove_source) {
3250 				if (claws_unlink(fileinfo->file) < 0)
3251 					FILE_OP_ERROR(fileinfo->file, "unlink");
3252 			}
3253 
3254 			if (num == 0)
3255 				continue;
3256 
3257 			if (!folderscan &&
3258 			    ((newmsginfo = get_msginfo(dest, num)) != NULL)) {
3259 				add_msginfo_to_cache(dest, newmsginfo, NULL);
3260 				procmsg_msginfo_free(&newmsginfo);
3261 			} else if ((newmsginfo = msgcache_get_msg(dest->cache, num)) != NULL) {
3262 				/* TODO: set default flags */
3263 				procmsg_msginfo_free(&newmsginfo);
3264 			}
3265 		}
3266 	}
3267 
3268 	g_hash_table_destroy(relation);
3269 
3270         return lastnum;
3271 }
3272 
folder_item_move_recursive(FolderItem * src,FolderItem * dest,gboolean copy)3273 static FolderItem *folder_item_move_recursive(FolderItem *src, FolderItem *dest, gboolean copy)
3274 {
3275 	GSList *mlist;
3276 	FolderItem *new_item;
3277 	FolderItem *next_item;
3278 	GNode *srcnode;
3279 	gchar *old_id, *new_id;
3280 	FolderUpdateData hookdata;
3281 
3282 	/* move messages */
3283 	debug_print("%s %s to %s\n", copy?"Copying":"Moving", src->path, dest->path);
3284 	new_item = folder_create_folder(dest, src->name);
3285 	if (new_item == NULL) {
3286 		g_print("Can't create folder\n");
3287 		return NULL;
3288 	}
3289 
3290 	if (new_item->folder == NULL)
3291 		new_item->folder = dest->folder;
3292 
3293 	/* move messages */
3294 	log_message(LOG_PROTOCOL, copy ?_("Copying %s to %s...\n"):_("Moving %s to %s...\n"),
3295 			src->name, new_item->path);
3296 
3297 	/*copy prefs*/
3298 	folder_item_prefs_copy_prefs(src, new_item);
3299 
3300 	/* copy internal data */
3301 	if (src->folder->klass == new_item->folder->klass &&
3302 	    src->folder->klass->copy_private_data != NULL)
3303 		src->folder->klass->copy_private_data(src->folder,
3304 					src, new_item);
3305 	new_item->collapsed = src->collapsed;
3306 	new_item->thread_collapsed = src->thread_collapsed;
3307 	new_item->threaded  = src->threaded;
3308 	new_item->ret_rcpt  = src->ret_rcpt;
3309 	new_item->hide_read_msgs = src->hide_read_msgs;
3310 	new_item->hide_del_msgs = src->hide_del_msgs;
3311 	new_item->hide_read_threads = src->hide_read_threads;
3312 	new_item->sort_key  = src->sort_key;
3313 	new_item->sort_type = src->sort_type;
3314 
3315 	mlist = folder_item_get_msg_list(src);
3316 
3317 	if (mlist != NULL) {
3318 		if (copy)
3319 			folder_item_copy_msgs(new_item, mlist);
3320 		else
3321 			folder_item_move_msgs(new_item, mlist);
3322 		procmsg_msg_list_free(mlist);
3323 	}
3324 
3325 	prefs_matcher_write_config();
3326 
3327 	/* recurse */
3328 	srcnode = src->folder->node;
3329 	srcnode = g_node_find(srcnode, G_PRE_ORDER, G_TRAVERSE_ALL, src);
3330 	srcnode = srcnode->children;
3331 	while (srcnode != NULL) {
3332 		if (srcnode && srcnode->data) {
3333 			next_item = (FolderItem*) srcnode->data;
3334 			srcnode = srcnode->next;
3335 			if (folder_item_move_recursive(next_item, new_item, copy) == NULL) {
3336 				return NULL;
3337 			}
3338 		}
3339 	}
3340 	old_id = folder_item_get_identifier(src);
3341 	new_id = folder_item_get_identifier(new_item);
3342 
3343 	hookdata.folder = src->folder;
3344 	hookdata.update_flags = FOLDER_TREE_CHANGED | FOLDER_MOVE_FOLDERITEM;
3345 	hookdata.item = src;
3346 	hookdata.item2 = new_item;
3347 	hooks_invoke(FOLDER_UPDATE_HOOKLIST, &hookdata);
3348 
3349 	/* if src supports removing, otherwise only copy folder */
3350 	if (src->folder->klass->remove_folder != NULL && !copy)
3351 		src->folder->klass->remove_folder(src->folder, src);
3352 	folder_write_list();
3353 
3354 	if (!copy) {
3355 		debug_print("updating rules : %s => %s\n", old_id, new_id);
3356 		if (old_id != NULL && new_id != NULL) {
3357 			prefs_filtering_rename_path(old_id, new_id);
3358 			account_rename_path(old_id, new_id);
3359 		}
3360 	}
3361 	g_free(old_id);
3362 	g_free(new_id);
3363 
3364 	return new_item;
3365 }
3366 
folder_item_move_to(FolderItem * src,FolderItem * dest,FolderItem ** new_item,gboolean copy)3367 gint folder_item_move_to(FolderItem *src, FolderItem *dest, FolderItem **new_item, gboolean copy)
3368 {
3369 	FolderItem *tmp = folder_item_parent(dest);
3370 	gchar * src_identifier, * dst_identifier;
3371 	gchar * phys_srcpath, * phys_dstpath, *tmppath;
3372 
3373 	while (tmp) {
3374 		if (tmp == src) {
3375 			return F_MOVE_FAILED_DEST_IS_CHILD;
3376 		}
3377 		tmp = folder_item_parent(tmp);
3378 	}
3379 
3380 	/* both dst and src can be root folders */
3381 	src_identifier = folder_item_get_identifier(src);
3382 	if (src_identifier == NULL && src->folder && folder_item_parent(src) == NULL) {
3383 		src_identifier = folder_get_identifier(src->folder);
3384 	}
3385 
3386 	dst_identifier = folder_item_get_identifier(dest);
3387 	if(dst_identifier == NULL && dest->folder && folder_item_parent(dest) == NULL) {
3388 		dst_identifier = folder_get_identifier(dest->folder);
3389 	}
3390 
3391 	if (src_identifier == NULL || dst_identifier == NULL) {
3392 		debug_print("Can't get identifiers\n");
3393 		return F_MOVE_FAILED;
3394 	}
3395 
3396 	if (src->folder != dest->folder && !copy) {
3397 		return F_MOVE_FAILED_DEST_OUTSIDE_MAILBOX;
3398 	}
3399 
3400 	phys_srcpath = folder_item_get_path(src);
3401 	tmppath = folder_item_get_path(dest);
3402 	phys_dstpath = g_strconcat(tmppath,
3403 		       G_DIR_SEPARATOR_S,
3404 		       g_path_get_basename(phys_srcpath),
3405 		       NULL);
3406 	g_free(tmppath);
3407 
3408 	if (folder_item_parent(src) == dest || src == dest) {
3409 		g_free(src_identifier);
3410 		g_free(dst_identifier);
3411 		g_free(phys_srcpath);
3412 		g_free(phys_dstpath);
3413 		return F_MOVE_FAILED_DEST_IS_PARENT;
3414 	}
3415 	debug_print("moving \"%s\" to \"%s\"\n", phys_srcpath, phys_dstpath);
3416 	if ((tmp = folder_item_move_recursive(src, dest, copy)) == NULL) {
3417 		return F_MOVE_FAILED;
3418 	}
3419 
3420 	g_free(src_identifier);
3421 	g_free(dst_identifier);
3422 	g_free(phys_srcpath);
3423 	g_free(phys_dstpath);
3424 
3425 	*new_item = tmp;
3426 
3427 	return F_MOVE_OK;
3428 }
3429 
3430 struct find_data
3431 {
3432 	gboolean found;
3433 };
find_num(gpointer key,gpointer value,gpointer data)3434 static void find_num(gpointer key, gpointer value, gpointer data)
3435 {
3436 	struct find_data *fdata = (struct find_data *)data;
3437 	if (GPOINTER_TO_INT(value) == 0)
3438 		fdata->found = TRUE;
3439 }
3440 
some_msgs_have_zero_num(GHashTable * hashtable)3441 static gboolean some_msgs_have_zero_num(GHashTable *hashtable)
3442 {
3443 	struct find_data fdata;
3444 
3445 	fdata.found = FALSE;
3446 	g_hash_table_foreach(hashtable, find_num, &fdata);
3447 
3448 	return fdata.found;
3449 }
3450 
3451 /**
3452  * Copy a list of message to a new folder and remove
3453  * source messages if wanted
3454  */
do_copy_msgs(FolderItem * dest,GSList * msglist,gboolean remove_source)3455 static gint do_copy_msgs(FolderItem *dest, GSList *msglist, gboolean remove_source)
3456 {
3457 	Folder *folder;
3458 	GSList *l;
3459 	gint num, lastnum = -1;
3460 	gboolean folderscan = FALSE;
3461 	GHashTable *relation;
3462 	GSList *not_moved = NULL;
3463 	gint total = 0, curmsg = 0;
3464 	MsgInfo *msginfo = NULL;
3465 
3466 	cm_return_val_if_fail(dest != NULL, -1);
3467 	cm_return_val_if_fail(msglist != NULL, -1);
3468 
3469 	folder = dest->folder;
3470 
3471 	cm_return_val_if_fail(folder->klass->copy_msg != NULL, -1);
3472 	if (dest->no_select)
3473 		return -1;
3474 
3475 	msginfo = (MsgInfo *)msglist->data;
3476 
3477 	if (!msginfo)
3478 		return -1;
3479 
3480 	if (!MSG_IS_QUEUED(msginfo->flags) &&
3481 	    MSG_IS_DRAFT(msginfo->flags) &&
3482 	    folder_has_parent_of_type(dest, F_QUEUE)) {
3483 		GSList *cur = msglist;
3484 		gboolean queue_err = FALSE;
3485 		for (; cur; cur = cur->next) {
3486 			Compose *compose = NULL;
3487 			FolderItem *queue = dest;
3488 			ComposeQueueResult val = COMPOSE_QUEUE_SUCCESS;
3489 
3490 			msginfo = (MsgInfo *)cur->data;
3491 			compose = compose_reedit(msginfo, TRUE);
3492 			if (compose == NULL) {
3493 				queue_err = TRUE;
3494 				continue;
3495 			}
3496 			val = compose_queue(compose, NULL, &queue, NULL,
3497 					FALSE);
3498 			if (val != COMPOSE_QUEUE_SUCCESS) {
3499 				queue_err = TRUE;
3500 			} else if (remove_source) {
3501 				folder_item_remove_msg(msginfo->folder, msginfo->msgnum);
3502 			}
3503 			if (val == COMPOSE_QUEUE_SUCCESS)
3504 				compose_close(compose);
3505 		}
3506 		return queue_err ? -1:0;
3507 	}
3508 
3509 	relation = g_hash_table_new(g_direct_hash, g_direct_equal);
3510 
3511 	for (l = msglist ; l != NULL ; l = g_slist_next(l)) {
3512 		MsgInfo * msginfo = (MsgInfo *) l->data;
3513 
3514 		if (msginfo->planned_download != 0) {
3515 			int old_planned = msginfo->planned_download;
3516 			partial_unmark(msginfo);
3517 			/* little hack to reenable after */
3518 			msginfo->planned_download = old_planned;
3519 		}
3520 	}
3521 
3522 	/*
3523 	 * Copy messages to destination folder and
3524 	 * store new message numbers in newmsgnums
3525 	 */
3526 	if (folder->klass->copy_msgs != NULL) {
3527 		if (folder->klass->copy_msgs(folder, dest, msglist, relation) < 0) {
3528 			g_hash_table_destroy(relation);
3529 			return -1;
3530 		}
3531 	} else {
3532 		MsgInfo * msginfo;
3533 		l = msglist;
3534 
3535 		/* immediately stop if src and dest folders are identical */
3536 		if (l != NULL) {
3537 			msginfo = (MsgInfo *) l->data;
3538 			if (msginfo != NULL && msginfo->folder == dest) {
3539 				g_hash_table_destroy(relation);
3540 				return -1;
3541 			}
3542 		}
3543 
3544 		for (; l != NULL ; l = g_slist_next(l)) {
3545 			msginfo = (MsgInfo *) l->data;
3546 
3547 			num = folder->klass->copy_msg(folder, dest, msginfo);
3548 			if (num > 0)
3549 				g_hash_table_insert(relation, msginfo, GINT_TO_POINTER(num));
3550 			else
3551 				not_moved = g_slist_prepend(not_moved, msginfo);
3552 		}
3553 	}
3554 
3555 	if (remove_source) {
3556 		MsgInfo *msginfo = (MsgInfo *) msglist->data;
3557 		FolderItem *item = msginfo->folder;
3558 		/*
3559 		 * Remove source messages from their folders if
3560 		 * copying was successfull and update folder
3561 		 * message counts
3562 		 */
3563 		if (not_moved == NULL && item->folder->klass->remove_msgs) {
3564 			item->folder->klass->remove_msgs(item->folder,
3565 					    		        msginfo->folder,
3566 						    		msglist,
3567 								relation);
3568 		}
3569 		for (l = msglist; l != NULL; l = g_slist_next(l)) {
3570             	        gpointer old_key, data;
3571 			msginfo = (MsgInfo *) l->data;
3572 			item = msginfo->folder;
3573 
3574             		if (g_hash_table_lookup_extended(relation, msginfo, &old_key, &data))
3575 	            	        num = GPOINTER_TO_INT(data);
3576 			else
3577 				num = 0;
3578 
3579 			if (g_slist_find(not_moved, msginfo))
3580 				continue;
3581 
3582 			if ((num >= 0) && (item->folder->klass->remove_msg != NULL)) {
3583 				if (!item->folder->klass->remove_msgs)
3584 					item->folder->klass->remove_msg(item->folder,
3585 					    		        msginfo->folder,
3586 						    		msginfo->msgnum);
3587 				remove_msginfo_from_cache(item, msginfo);
3588 			}
3589 		}
3590 	}
3591 
3592 	/* Read cache for dest folder */
3593 	if (!dest->cache) folder_item_read_cache(dest);
3594 
3595 	/*
3596 	 * Fetch new MsgInfos for new messages in dest folder,
3597 	 * add them to the msgcache and update folder message counts
3598 	 */
3599 	if (some_msgs_have_zero_num(relation)) {
3600 		folder_item_scan_full(dest, FALSE);
3601 		folderscan = TRUE;
3602 	}
3603 
3604 	statusbar_print_all(_("Updating cache for %s..."), dest->path ? dest->path : "(null)");
3605 	total = g_slist_length(msglist);
3606 
3607 	if (FOLDER_TYPE(dest->folder) == F_IMAP && total > 1) {
3608 		folder_item_scan_full(dest, FALSE);
3609 		folderscan = TRUE;
3610 	}
3611 	folder_item_set_batch(dest, TRUE);
3612 	for (l = msglist; l != NULL; l = g_slist_next(l)) {
3613 		MsgInfo *msginfo = (MsgInfo *) l->data;
3614                 gpointer data, old_key;
3615 
3616 		if (!msginfo)
3617 			continue;
3618 
3619                 if (g_hash_table_lookup_extended(relation, msginfo, &old_key, &data))
3620 	                num = GPOINTER_TO_INT(data);
3621 		else
3622 			num = 0;
3623 
3624 		statusbar_progress_all(curmsg++,total, 100);
3625 		if (curmsg % 100 == 0)
3626 			GTK_EVENTS_FLUSH();
3627 
3628 		if (num >= 0) {
3629 			MsgInfo *newmsginfo = NULL;
3630 
3631 			if (!folderscan && num > 0) {
3632 				newmsginfo = get_msginfo(dest, num);
3633 				if (newmsginfo != NULL) {
3634 					add_msginfo_to_cache(dest, newmsginfo, msginfo);
3635 				}
3636 			}
3637 			if (newmsginfo == NULL) {
3638 				if (!folderscan) {
3639 					folder_item_scan_full(dest, FALSE);
3640 					folderscan = TRUE;
3641 				}
3642 				if (msginfo->msgid != NULL) {
3643 					newmsginfo = folder_item_get_msginfo_by_msgid(dest, msginfo->msgid);
3644 					if (newmsginfo != NULL) {
3645 						copy_msginfo_flags(msginfo, newmsginfo);
3646 						num = newmsginfo->msgnum;
3647 					}
3648 				}
3649 			}
3650 			if (newmsginfo != NULL
3651 			 && msginfo->planned_download == POP3_PARTIAL_DLOAD_DELE) {
3652 				partial_mark_for_delete(newmsginfo);
3653 			}
3654 			if (newmsginfo != NULL
3655 			 && msginfo->planned_download == POP3_PARTIAL_DLOAD_DLOAD) {
3656 				partial_mark_for_download(newmsginfo);
3657 			}
3658 			if (!MSG_IS_POSTFILTERED (msginfo->flags)) {
3659 				procmsg_msginfo_set_flags (   msginfo, MSG_POSTFILTERED, 0);
3660 				if (newmsginfo) {
3661 					procmsg_msginfo_set_flags (newmsginfo, MSG_POSTFILTERED, 0);
3662 					hooks_invoke (MAIL_POSTFILTERING_HOOKLIST, newmsginfo);
3663 				}
3664 			}
3665 			procmsg_msginfo_free(&newmsginfo);
3666 
3667 
3668 			if (num > lastnum)
3669 				lastnum = num;
3670 		}
3671 	}
3672 	folder_item_set_batch(dest, FALSE);
3673 	statusbar_progress_all(0,0,0);
3674 	statusbar_pop_all();
3675 
3676 	g_hash_table_destroy(relation);
3677 	if (not_moved != NULL) {
3678 		g_slist_free(not_moved);
3679 		return -1;
3680 	} else
3681 		return lastnum;
3682 }
3683 
3684 /**
3685  * Move a message to a new folder.
3686  *
3687  * \param dest Destination folder
3688  * \param msginfo The message
3689  */
folder_item_move_msg(FolderItem * dest,MsgInfo * msginfo)3690 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
3691 {
3692 	GSList list;
3693 
3694 	cm_return_val_if_fail(dest != NULL, -1);
3695 	cm_return_val_if_fail(msginfo != NULL, -1);
3696 
3697 	list.data = msginfo;
3698 	list.next = NULL;
3699 
3700 	return do_copy_msgs(dest, &list, TRUE);
3701 }
3702 
3703 /**
3704  * Move a list of messages to a new folder.
3705  *
3706  * \param dest Destination folder
3707  * \param msglist List of messages
3708  */
folder_item_move_msgs(FolderItem * dest,GSList * msglist)3709 gint folder_item_move_msgs(FolderItem *dest, GSList *msglist)
3710 {
3711 	gint result = -1;
3712 	cm_return_val_if_fail(dest != NULL, -1);
3713 	cm_return_val_if_fail(msglist != NULL, -1);
3714 	inc_lock();
3715 	result = do_copy_msgs(dest, msglist, TRUE);
3716 	inc_unlock();
3717 	return result;
3718 }
3719 
3720 /**
3721  * Copy a message to a new folder.
3722  *
3723  * \param dest Destination folder
3724  * \param msginfo The message
3725  */
folder_item_copy_msg(FolderItem * dest,MsgInfo * msginfo)3726 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
3727 {
3728 	GSList list;
3729 
3730 	cm_return_val_if_fail(dest != NULL, -1);
3731 	cm_return_val_if_fail(msginfo != NULL, -1);
3732 
3733 	list.data = msginfo;
3734 	list.next = NULL;
3735 
3736 	return do_copy_msgs(dest, &list, FALSE);
3737 }
3738 
3739 /**
3740  * Copy a list of messages to a new folder.
3741  *
3742  * \param dest Destination folder
3743  * \param msglist List of messages
3744  */
folder_item_copy_msgs(FolderItem * dest,GSList * msglist)3745 gint folder_item_copy_msgs(FolderItem *dest, GSList *msglist)
3746 {
3747 	gint result;
3748 	cm_return_val_if_fail(dest != NULL, -1);
3749 	cm_return_val_if_fail(msglist != NULL, -1);
3750 
3751 	inc_lock();
3752 	result = do_copy_msgs(dest, msglist, FALSE);
3753 	inc_unlock();
3754 
3755 	return result;
3756 }
3757 
folder_item_remove_msg(FolderItem * item,gint num)3758 gint folder_item_remove_msg(FolderItem *item, gint num)
3759 {
3760 	Folder *folder;
3761 	gint ret;
3762 	MsgInfo *msginfo;
3763 
3764 	cm_return_val_if_fail(item != NULL, -1);
3765 	folder = item->folder;
3766 	cm_return_val_if_fail(folder->klass->remove_msg != NULL, -1);
3767 	if (item->no_select)
3768 		return -1;
3769 
3770 	if (!item->cache) folder_item_read_cache(item);
3771 
3772 	msginfo = msgcache_get_msg(item->cache, num);
3773 	if (msginfo && MSG_IS_LOCKED(msginfo->flags)) {
3774 		procmsg_msginfo_free(&msginfo);
3775 		return -1;
3776 	}
3777 	ret = folder->klass->remove_msg(folder, item, num);
3778 
3779 	if (msginfo != NULL) {
3780 		if (ret == 0)
3781 			remove_msginfo_from_cache(item, msginfo);
3782 		procmsg_msginfo_free(&msginfo);
3783 	}
3784 
3785 	return ret;
3786 }
3787 
folder_item_remove_msgs(FolderItem * item,GSList * msglist)3788 gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
3789 {
3790 	Folder *folder;
3791 	gint ret = 0;
3792 	GSList *real_list = NULL, *cur = NULL;
3793 
3794 	cm_return_val_if_fail(item != NULL, -1);
3795 	folder = item->folder;
3796 	cm_return_val_if_fail(folder != NULL, -1);
3797 	if (item->no_select)
3798 		return -1;
3799 	inc_lock();
3800 	if (!item->cache) folder_item_read_cache(item);
3801 
3802 	folder_item_update_freeze();
3803 
3804 	/* filter out locked mails */
3805 	for (cur = msglist; cur; cur = cur->next) {
3806 		MsgInfo *info = (MsgInfo *)cur->data;
3807 		if (!MSG_IS_LOCKED(info->flags))
3808 			real_list = g_slist_prepend(real_list, info);
3809 	}
3810 
3811 	real_list = g_slist_reverse(real_list);
3812 
3813 	if (item->folder->klass->remove_msgs) {
3814 		ret = item->folder->klass->remove_msgs(item->folder,
3815 					    		item,
3816 						    	real_list,
3817 							NULL);
3818 	}
3819 	cur = real_list;
3820 	while (ret == 0 && cur != NULL) {
3821 		MsgInfo *msginfo = (MsgInfo *)cur->data;
3822 		if (msginfo && MSG_IS_LOCKED(msginfo->flags)) {
3823 			cur = cur->next;
3824 			continue;
3825 		}
3826 		if (!item->folder->klass->remove_msgs)
3827 			ret = folder_item_remove_msg(item, msginfo->msgnum);
3828 		if (ret != 0) break;
3829 		msgcache_remove_msg(item->cache, msginfo->msgnum);
3830 		cur = cur->next;
3831 	}
3832 	g_slist_free(real_list);
3833 	folder_item_scan_full(item, FALSE);
3834 	folder_item_update_thaw();
3835 	inc_unlock();
3836 	return ret;
3837 }
3838 
folder_item_expunge(FolderItem * item)3839 gint folder_item_expunge(FolderItem *item)
3840 {
3841 	Folder *folder = item->folder;
3842 	gint result = 0;
3843 	if (folder == NULL)
3844 		return -1;
3845 	if (folder->klass->expunge) {
3846 		GSList *msglist = folder_item_get_msg_list(item);
3847 		GSList *cur;
3848 		result = folder->klass->expunge(folder, item);
3849 		if (result == 0) {
3850 			for (cur = msglist; cur; cur = cur->next) {
3851 				MsgInfo *msginfo = (MsgInfo *)cur->data;
3852 				if (MSG_IS_DELETED(msginfo->flags)) {
3853 					remove_msginfo_from_cache(item, msginfo);
3854 				}
3855 			}
3856 		}
3857 		procmsg_msg_list_free(msglist);
3858 	}
3859 	return result;
3860 }
3861 
folder_item_remove_all_msg(FolderItem * item)3862 gint folder_item_remove_all_msg(FolderItem *item)
3863 {
3864 	Folder *folder;
3865 	gint result;
3866 
3867 	cm_return_val_if_fail(item != NULL, -1);
3868 	if (item->no_select)
3869 		return -1;
3870 
3871 	folder = item->folder;
3872 
3873 	inc_lock();
3874 	if (folder->klass->remove_all_msg != NULL) {
3875 		result = folder->klass->remove_all_msg(folder, item);
3876 
3877 		if (result == 0) {
3878 			folder_item_free_cache(item, TRUE);
3879 			item->cache = msgcache_new();
3880 			item->cache_dirty = TRUE;
3881 			item->mark_dirty = TRUE;
3882 			item->tags_dirty = TRUE;
3883 		}
3884 	} else {
3885 		MsgInfoList *msglist;
3886 
3887 		msglist = folder_item_get_msg_list(item);
3888 		result = folder_item_remove_msgs(item, msglist);
3889 		procmsg_msg_list_free(msglist);
3890 	}
3891 
3892 	if (result == 0) {
3893 		item->new_msgs = 0;
3894 		item->unread_msgs = 0;
3895 		item->unreadmarked_msgs = 0;
3896 		item->marked_msgs = 0;
3897 		item->total_msgs = 0;
3898 		item->replied_msgs = 0;
3899 		item->forwarded_msgs = 0;
3900 		item->locked_msgs = 0;
3901 		item->ignored_msgs = 0;
3902 		item->watched_msgs = 0;
3903 		folder_item_update(item, F_ITEM_UPDATE_MSGCNT | F_ITEM_UPDATE_CONTENT);
3904 	}
3905 
3906 	inc_unlock();
3907 	return result;
3908 }
3909 
folder_item_change_msg_flags(FolderItem * item,MsgInfo * msginfo,MsgPermFlags newflags)3910 void folder_item_change_msg_flags(FolderItem *item, MsgInfo *msginfo, MsgPermFlags newflags)
3911 {
3912 	cm_return_if_fail(item != NULL);
3913 	cm_return_if_fail(msginfo != NULL);
3914 
3915 	item->mark_dirty = TRUE;
3916 
3917 	if (item->no_select)
3918 		return;
3919 
3920 	if (item->folder->klass->change_flags != NULL && item->scanning != ITEM_SCANNING_WITH_FLAGS) {
3921 		item->folder->klass->change_flags(item->folder, item, msginfo, newflags);
3922 	} else {
3923 		msginfo->flags.perm_flags = newflags;
3924 	}
3925 }
3926 
folder_item_commit_tags(FolderItem * item,MsgInfo * msginfo,GSList * tags_set,GSList * tags_unset)3927 void folder_item_commit_tags(FolderItem *item, MsgInfo *msginfo, GSList *tags_set, GSList *tags_unset)
3928 {
3929 	Folder *folder = NULL;
3930 
3931 	if (!msginfo)
3932 		return;
3933 	if (!item)
3934 		return;
3935 	if (!tags_set && !tags_unset)
3936 		return;
3937 
3938 	folder = item->folder;
3939 	if (!folder)
3940 		return;
3941 
3942 	item->tags_dirty = TRUE;
3943 
3944 	if (folder->klass->commit_tags == NULL)
3945 		return;
3946 
3947 	folder->klass->commit_tags(item, msginfo, tags_set, tags_unset);
3948 }
3949 
folder_item_is_msg_changed(FolderItem * item,MsgInfo * msginfo)3950 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
3951 {
3952 	Folder *folder;
3953 
3954 	cm_return_val_if_fail(item != NULL, FALSE);
3955 	if (item->no_select)
3956 		return FALSE;
3957 
3958 	folder = item->folder;
3959 
3960 	cm_return_val_if_fail(folder->klass->is_msg_changed != NULL, -1);
3961 
3962 	return folder->klass->is_msg_changed(folder, item, msginfo);
3963 }
3964 
folder_item_discard_cache(FolderItem * item)3965 void folder_item_discard_cache(FolderItem *item)
3966 {
3967 	gchar *dir;
3968 	gchar *cache;
3969 
3970 	if (!item)
3971 		return;
3972 
3973 	if (item->cache) {
3974 		msgcache_destroy(item->cache);
3975 		item->cache = NULL;
3976 	}
3977 	dir = folder_item_get_path(item);
3978 	if (is_dir_exist(dir))
3979 		remove_all_numbered_files(dir);
3980 	g_free(dir);
3981 
3982 	cache = folder_item_get_cache_file(item);
3983 	if (is_file_exist(cache))
3984 		claws_unlink(cache);
3985 	g_free(cache);
3986 
3987 }
3988 
folder_item_get_cache_file(FolderItem * item)3989 static gchar *folder_item_get_cache_file(FolderItem *item)
3990 {
3991 	gchar *path;
3992 	gchar *file;
3993 	gchar *old_file;
3994 
3995 	cm_return_val_if_fail(item != NULL, NULL);
3996 
3997 	path = folder_item_get_path(item);
3998 	cm_return_val_if_fail(path != NULL, NULL);
3999 	if (!is_dir_exist(path))
4000 		make_dir_hier(path);
4001 	file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
4002 	old_file = g_strconcat(path, G_DIR_SEPARATOR_S, OLD_CACHE_FILE, NULL);
4003 
4004 	if (!is_file_exist(file) && is_file_exist(old_file))
4005 		move_file(old_file, file, FALSE);
4006 	g_free(old_file);
4007 	g_free(path);
4008 
4009 	return file;
4010 }
4011 
folder_item_get_mark_file(FolderItem * item)4012 static gchar *folder_item_get_mark_file(FolderItem *item)
4013 {
4014 	gchar *path;
4015 	gchar *file;
4016 	gchar *old_file;
4017 
4018 	cm_return_val_if_fail(item != NULL, NULL);
4019 	cm_return_val_if_fail(item->path != NULL, NULL);
4020 
4021 	path = folder_item_get_path(item);
4022 	cm_return_val_if_fail(path != NULL, NULL);
4023 	if (!is_dir_exist(path))
4024 		make_dir_hier(path);
4025 	file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
4026 	old_file = g_strconcat(path, G_DIR_SEPARATOR_S, OLD_MARK_FILE, NULL);
4027 
4028 	if (!is_file_exist(file) && is_file_exist(old_file))
4029 		move_file(old_file, file, FALSE);
4030 	g_free(old_file);
4031 	g_free(path);
4032 
4033 	return file;
4034 }
4035 
folder_item_get_tags_file(FolderItem * item)4036 static gchar *folder_item_get_tags_file(FolderItem *item)
4037 {
4038 	gchar *path;
4039 	gchar *identifier;
4040 	gchar *file;
4041 
4042 	/* we save tags files in rc_dir, because tagsrc is there too,
4043 	 * and storing tags directly in the mailboxes would give strange
4044 	 * result when using another Claws mailbox from another install
4045 	 * with different tags. */
4046 
4047 	cm_return_val_if_fail(item != NULL, NULL);
4048 
4049 	identifier = folder_item_get_identifier(item);
4050 	cm_return_val_if_fail(identifier != NULL, NULL);
4051 
4052 #ifdef G_OS_WIN32
4053 	while (strchr(identifier, '/'))
4054 		*strchr(identifier, '/') = '\\';
4055 #endif
4056 
4057 	path = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
4058 			   TAGS_DIR, G_DIR_SEPARATOR_S,
4059 			   identifier, NULL);
4060 
4061 	g_free(identifier);
4062 
4063 	if (!is_dir_exist(path))
4064 		make_dir_hier(path);
4065 
4066 	file = g_strconcat(path, G_DIR_SEPARATOR_S, TAGS_FILE, NULL);
4067 
4068 	g_free(path);
4069 
4070 	return file;
4071 }
4072 
xml_to_folder_item(gpointer nodedata,gpointer data)4073 static gpointer xml_to_folder_item(gpointer nodedata, gpointer data)
4074 {
4075 	XMLNode *xmlnode = (XMLNode *) nodedata;
4076 	Folder *folder = (Folder *) data;
4077 	FolderItem *item;
4078 
4079 	cm_return_val_if_fail(xmlnode != NULL, NULL);
4080 	cm_return_val_if_fail(folder != NULL, NULL);
4081 
4082 	if (g_strcmp0(xmlnode->tag->tag, "folderitem") != 0) {
4083 		g_warning("tag name != \"folderitem\"");
4084 		return NULL;
4085 	}
4086 
4087 	item = folder_item_new(folder, "", "");
4088 	g_node_destroy(item->node);
4089 	if (folder->klass->item_set_xml != NULL)
4090 		folder->klass->item_set_xml(folder, item, xmlnode->tag);
4091 	else
4092 		folder_item_set_xml(folder, item, xmlnode->tag);
4093 
4094 	item->folder = folder;
4095 
4096 	switch (item->stype) {
4097 	case F_INBOX:  folder->inbox  = item; break;
4098 	case F_OUTBOX: folder->outbox = item; break;
4099 	case F_DRAFT:  folder->draft  = item; break;
4100 	case F_QUEUE:  folder->queue  = item; break;
4101 	case F_TRASH:  folder->trash  = item; break;
4102 	default:       break;
4103 	}
4104 	folder_item_prefs_read_config(item);
4105 
4106 	return item;
4107 }
4108 
folder_item_set_node(GNode * node,gpointer data)4109 static gboolean folder_item_set_node(GNode *node, gpointer data)
4110 {
4111 	cm_return_val_if_fail(node->data != NULL, -1);
4112 
4113 	FolderItem *item = (FolderItem *) node->data;
4114 	item->node = node;
4115 
4116 	return FALSE;
4117 }
4118 
folder_get_from_xml(GNode * node)4119 static Folder *folder_get_from_xml(GNode *node)
4120 {
4121 	Folder *folder;
4122 	XMLNode *xmlnode;
4123 	GList *list;
4124 	FolderClass *klass = NULL;
4125 	GNode *cur;
4126 
4127 	cm_return_val_if_fail(node->data != NULL, NULL);
4128 
4129 	xmlnode = node->data;
4130 	if (g_strcmp0(xmlnode->tag->tag, "folder") != 0) {
4131 		g_warning("tag name != \"folder\"");
4132 		return NULL;
4133 	}
4134 	list = xmlnode->tag->attr;
4135 	for (; list != NULL; list = list->next) {
4136 		XMLAttr *attr = list->data;
4137 
4138 		if (!attr || !attr->name || !attr->value) continue;
4139 		if (!strcmp(attr->name, "type"))
4140 			klass = folder_get_class_from_string(attr->value);
4141 	}
4142 	if (klass == NULL)
4143 		return NULL;
4144 
4145 	folder = folder_new(klass, "", "");
4146 	cm_return_val_if_fail(folder != NULL, NULL);
4147 
4148 	if (klass->set_xml)
4149 		klass->set_xml(folder, xmlnode->tag);
4150 	else
4151 		folder_set_xml(folder, xmlnode->tag);
4152 
4153 	cur = node->children;
4154 	while (cur != NULL) {
4155 		GNode *itemnode;
4156 
4157 		itemnode = g_node_map(cur, xml_to_folder_item, (gpointer) folder);
4158 		g_node_append(folder->node, itemnode);
4159 		cur = cur->next;
4160 	}
4161 	g_node_traverse(folder->node, G_IN_ORDER, G_TRAVERSE_ALL, -1, folder_item_set_node, NULL);
4162 
4163 	return folder;
4164 }
4165 
folder_get_list_path(void)4166 gchar *folder_get_list_path(void)
4167 {
4168 	static gchar *filename = NULL;
4169 
4170 	if (!filename)
4171 		filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
4172 					FOLDER_LIST, NULL);
4173 
4174 	return filename;
4175 }
4176 
folder_item_to_xml(gpointer nodedata,gpointer data)4177 static gpointer folder_item_to_xml(gpointer nodedata, gpointer data)
4178 {
4179 	FolderItem *item = (FolderItem *) nodedata;
4180 	XMLTag *tag;
4181 
4182 	cm_return_val_if_fail(item != NULL, NULL);
4183 
4184 	if (item->folder->klass->item_get_xml != NULL)
4185 		tag = item->folder->klass->item_get_xml(item->folder, item);
4186 	else
4187 		tag = folder_item_get_xml(item->folder, item);
4188 
4189 	return xml_node_new(tag, NULL);
4190 }
4191 
folder_get_xml_node(Folder * folder)4192 static GNode *folder_get_xml_node(Folder *folder)
4193 {
4194 	GNode *node;
4195 	XMLNode *xmlnode;
4196 	XMLTag *tag;
4197 
4198 	cm_return_val_if_fail(folder != NULL, NULL);
4199 
4200 	if (folder->klass->get_xml != NULL)
4201 		tag = folder->klass->get_xml(folder);
4202 	else
4203 		tag = folder_get_xml(folder);
4204 
4205 	xml_tag_add_attr(tag, xml_attr_new("type", folder->klass->idstr));
4206 
4207 	xmlnode = xml_node_new(tag, NULL);
4208 
4209 	node = g_node_new(xmlnode);
4210 
4211 	cm_return_val_if_fail (folder->node != NULL, NULL);
4212 
4213 	if (folder->node->children) {
4214 		GNode *cur;
4215 
4216 		cur = folder->node->children;
4217 		while (cur) {
4218 			GNode *xmlnode;
4219 
4220 			xmlnode = g_node_map(cur, folder_item_to_xml, (gpointer) folder);
4221 			g_node_append(node, xmlnode);
4222 			cur = cur->next;
4223 		}
4224 	}
4225 
4226 	return node;
4227 }
4228 
folder_update_op_count_rec(GNode * node)4229 static void folder_update_op_count_rec(GNode *node)
4230 {
4231 	FolderItem *fitem = FOLDER_ITEM(node->data);
4232 
4233 	if (g_node_depth(node) > 0) {
4234 		if (fitem->op_count > 0) {
4235 			fitem->op_count = 0;
4236 			folder_item_update(fitem, F_ITEM_UPDATE_MSGCNT);
4237 		}
4238 		if (node->children) {
4239 			GNode *child;
4240 
4241 			child = node->children;
4242 			while (child) {
4243 				GNode *cur;
4244 
4245 				cur = child;
4246 				child = cur->next;
4247 				folder_update_op_count_rec(cur);
4248 			}
4249 		}
4250 	}
4251 }
4252 
folder_update_op_count(void)4253 void folder_update_op_count(void)
4254 {
4255 	GList *cur;
4256 	Folder *folder;
4257 
4258 	for (cur = folder_list; cur != NULL; cur = cur->next) {
4259 		folder = cur->data;
4260 		folder_update_op_count_rec(folder->node);
4261 	}
4262 }
4263 
4264 typedef struct _type_str {
4265 	gchar * str;
4266 	gint type;
4267 } type_str;
4268 
4269 
4270 /*
4271 static gchar * folder_item_get_tree_identifier(FolderItem * item)
4272 {
4273 	if (item->parent != NULL) {
4274 		gchar * path;
4275 		gchar * id;
4276 
4277 		path = folder_item_get_tree_identifier(item->parent);
4278 		if (path == NULL)
4279 			return NULL;
4280 
4281 		id = g_strconcat(path, "/", item->name, NULL);
4282 		g_free(path);
4283 
4284 		return id;
4285 	}
4286 	else {
4287 		return g_strconcat("/", item->name, NULL);
4288 	}
4289 }
4290 */
4291 
4292 /* CLAWS: temporary local folder for filtering */
4293 #define TEMP_FOLDER "TEMP_FOLDER"
4294 #define PROCESSING_FOLDER_ITEM "processing"
4295 
folder_create_processing_folder(int account_id)4296 static FolderItem *folder_create_processing_folder(int account_id)
4297 {
4298 	static Folder *processing_folder = NULL;
4299 	FolderItem *processing_folder_item;
4300 
4301 	gchar *processing_folder_item_name = NULL;
4302 
4303         processing_folder_item_name = g_strdup_printf("%s-%d", PROCESSING_FOLDER_ITEM, account_id);
4304 
4305 	if (processing_folder == NULL) {
4306 		gchar *tmppath =
4307 		    g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
4308 				"tempfolder", NULL);
4309 		processing_folder =
4310 		    folder_new(mh_get_class(), TEMP_FOLDER, tmppath);
4311 		g_free(tmppath);
4312 		tmppath = NULL;
4313 
4314 		g_assert(processing_folder != NULL);
4315 		processing_folder->klass->scan_tree(processing_folder);
4316 	}
4317 	g_assert(processing_folder != NULL);
4318 
4319 	processing_folder_item = folder_find_child_item_by_name(FOLDER_ITEM(processing_folder->node->data),
4320 					processing_folder_item_name);
4321 	if (processing_folder_item) {
4322 		debug_print("*TMP* already created %s\n", folder_item_get_path(processing_folder_item));
4323 	} else {
4324 		processing_folder_item = processing_folder->klass->create_folder(processing_folder,
4325 								   	         processing_folder->node->data,
4326 										 processing_folder_item_name);
4327 		folder_item_append(FOLDER_ITEM(processing_folder->node->data), processing_folder_item);
4328 		debug_print("*TMP* creating %s\n", folder_item_get_path(processing_folder_item));
4329 	}
4330 	g_free(processing_folder_item_name);
4331 	g_assert(processing_folder_item != NULL);
4332 
4333 	return(processing_folder_item);
4334 }
4335 
folder_get_default_processing(int account_id)4336 FolderItem *folder_get_default_processing(int account_id)
4337 {
4338 	return folder_create_processing_folder(account_id);
4339 }
4340 
4341 /* folder_persist_prefs_new() - return hash table with persistent
4342  * settings (and folder name as key).
4343  * (note that in claws other options are in the folder_item_prefs_RC
4344  * file, so those don't need to be included in PersistPref yet)
4345  */
folder_persist_prefs_new(Folder * folder)4346 static GHashTable *folder_persist_prefs_new(Folder *folder)
4347 {
4348 	GHashTable *pptable;
4349 
4350 	cm_return_val_if_fail(folder, NULL);
4351 	pptable = g_hash_table_new(g_str_hash, g_str_equal);
4352 	folder_get_persist_prefs_recursive(folder->node, pptable);
4353 	return pptable;
4354 }
4355 
folder_persist_prefs_free(GHashTable * pptable)4356 static void folder_persist_prefs_free(GHashTable *pptable)
4357 {
4358 	cm_return_if_fail(pptable);
4359 	g_hash_table_foreach_remove(pptable, persist_prefs_free, NULL);
4360 	g_hash_table_destroy(pptable);
4361 }
4362 
folder_get_persist_prefs(GHashTable * pptable,const char * name)4363 static const PersistPrefs *folder_get_persist_prefs(GHashTable *pptable, const char *name)
4364 {
4365 	if (pptable == NULL || name == NULL) return NULL;
4366 	return g_hash_table_lookup(pptable, name);
4367 }
4368 
folder_item_restore_persist_prefs(FolderItem * item,GHashTable * pptable)4369 static void folder_item_restore_persist_prefs(FolderItem *item, GHashTable *pptable)
4370 {
4371 	const PersistPrefs *pp;
4372 	gchar *id = folder_item_get_identifier(item);
4373 
4374 	pp = folder_get_persist_prefs(pptable, id);
4375 	g_free(id);
4376 
4377 	if (!pp) return;
4378 
4379 	/* CLAWS: since not all folder properties have been migrated to
4380 	 * folderlist.xml, we need to call the old stuff first before
4381 	 * setting things that apply both to Main and Claws. */
4382 	folder_item_prefs_read_config(item);
4383 
4384 	item->collapsed = pp->collapsed;
4385 	item->thread_collapsed = pp->thread_collapsed;
4386 	item->threaded  = pp->threaded;
4387 	item->ret_rcpt  = pp->ret_rcpt;
4388 	item->hide_read_msgs = pp->hide_read_msgs;
4389 	item->hide_del_msgs = pp->hide_del_msgs;
4390 	item->hide_read_threads = pp->hide_read_threads;
4391 	item->sort_key  = pp->sort_key;
4392 	item->sort_type = pp->sort_type;
4393 }
4394 
folder_get_persist_prefs_recursive(GNode * node,GHashTable * pptable)4395 static void folder_get_persist_prefs_recursive(GNode *node, GHashTable *pptable)
4396 {
4397 	FolderItem *item = FOLDER_ITEM(node->data);
4398 	PersistPrefs *pp;
4399 	GNode *child, *cur;
4400 	gchar *id;
4401 
4402 	cm_return_if_fail(node != NULL);
4403 	cm_return_if_fail(item != NULL);
4404 
4405 	/* NOTE: item->path == NULL means top level folder; not interesting
4406 	 * to store preferences of that one.  */
4407 	if (item->path) {
4408 		id = folder_item_get_identifier(item);
4409 		pp = g_new0(PersistPrefs, 1);
4410 		cm_return_if_fail(pp != NULL);
4411 		pp->collapsed = item->collapsed;
4412 		pp->thread_collapsed = item->thread_collapsed;
4413 		pp->threaded  = item->threaded;
4414 		pp->ret_rcpt  = item->ret_rcpt;
4415 		pp->hide_read_msgs = item->hide_read_msgs;
4416 		pp->hide_del_msgs = item->hide_del_msgs;
4417 		pp->hide_read_threads = item->hide_read_threads;
4418 		pp->sort_key  = item->sort_key;
4419 		pp->sort_type = item->sort_type;
4420 		g_hash_table_insert(pptable, id, pp);
4421 	}
4422 
4423 	if (node->children) {
4424 		child = node->children;
4425 		while (child) {
4426 			cur = child;
4427 			child = cur->next;
4428 			folder_get_persist_prefs_recursive(cur, pptable);
4429 		}
4430 	}
4431 }
4432 
persist_prefs_free(gpointer key,gpointer val,gpointer data)4433 static gboolean persist_prefs_free(gpointer key, gpointer val, gpointer data)
4434 {
4435 	g_free(key);
4436 	g_free(val);
4437 	return TRUE;
4438 }
4439 
folder_item_apply_processing(FolderItem * item)4440 void folder_item_apply_processing(FolderItem *item)
4441 {
4442 	GSList *processing_list;
4443 	GSList *mlist, *cur;
4444 	guint total = 0, curmsg = 0;
4445 	gint last_apply_per_account;
4446 
4447 	cm_return_if_fail(item != NULL);
4448 
4449 	if (item->no_select)
4450 	       return;
4451 
4452 	processing_list = item->prefs->processing;
4453 
4454 	if (!processing_enabled(pre_global_processing) &&
4455 	    !processing_enabled(processing_list) &&
4456 	    !processing_enabled(post_global_processing))
4457 		return;
4458 
4459 	debug_print("processing %s\n", item->name);
4460 	folder_item_update_freeze();
4461 
4462 	inc_lock();
4463 
4464 	mlist = folder_item_get_msg_list(item);
4465 	total = g_slist_length(mlist);
4466 	statusbar_print_all(_("Processing messages..."));
4467 
4468 	last_apply_per_account = prefs_common.apply_per_account_filtering_rules;
4469 	prefs_common.apply_per_account_filtering_rules = FILTERING_ACCOUNT_RULES_SKIP;
4470 
4471 	folder_item_set_batch(item, TRUE);
4472 	for (cur = mlist ; cur != NULL ; cur = cur->next) {
4473 		MsgInfo * msginfo;
4474 
4475 		msginfo = (MsgInfo *) cur->data;
4476 
4477                 /* reset parameters that can be modified by processing */
4478                 msginfo->hidden = 0;
4479                 msginfo->score = 0;
4480 
4481 		statusbar_progress_all(curmsg++,total, 10);
4482 
4483                 /* apply pre global rules */
4484 		filter_message_by_msginfo(pre_global_processing, msginfo, NULL,
4485 				FILTERING_PRE_PROCESSING, NULL);
4486 
4487                 /* apply rules of the folder */
4488 		filter_message_by_msginfo(processing_list, msginfo, NULL,
4489 				FILTERING_FOLDER_PROCESSING, item->name);
4490 
4491                 /* apply post global rules */
4492 		filter_message_by_msginfo(post_global_processing, msginfo, NULL,
4493 				FILTERING_POST_PROCESSING, NULL);
4494 		if (curmsg % 1000 == 0)
4495 			GTK_EVENTS_FLUSH();
4496 	}
4497 	folder_item_set_batch(item, FALSE);
4498 
4499 	prefs_common.apply_per_account_filtering_rules = last_apply_per_account;
4500 
4501 	if (pre_global_processing || processing_list
4502 	    || post_global_processing)
4503 		filtering_move_and_copy_msgs(mlist);
4504 	for (cur = mlist ; cur != NULL ; cur = cur->next) {
4505 		procmsg_msginfo_free((MsgInfo **)&(cur->data));
4506 	}
4507 	g_slist_free(mlist);
4508 
4509 	statusbar_progress_all(0,0,0);
4510 	statusbar_pop_all();
4511 
4512 	inc_unlock();
4513 
4514 	folder_item_update_thaw();
4515 }
4516 
4517 /*
4518  *  functions for handling FolderItem content changes
4519  */
4520 static gint folder_item_update_freeze_cnt = 0;
4521 
folder_item_update_with_msg(FolderItem * item,FolderItemUpdateFlags update_flags,MsgInfo * msg)4522 static void folder_item_update_with_msg(FolderItem *item, FolderItemUpdateFlags update_flags, MsgInfo *msg)
4523 {
4524 	if (folder_item_update_freeze_cnt == 0 /* || (msg != NULL && item->opened) */) {
4525 		FolderItemUpdateData source;
4526 
4527 		source.item = item;
4528 		source.update_flags = update_flags;
4529 		source.msg = msg;
4530     		hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
4531 	} else {
4532 		item->update_flags |= update_flags & ~(F_ITEM_UPDATE_ADDMSG | F_ITEM_UPDATE_REMOVEMSG);
4533 	}
4534 }
4535 
4536 /**
4537  * Notify the folder system about changes to a folder. If the
4538  * update system is not frozen the FOLDER_ITEM_UPDATE_HOOKLIST will
4539  * be invoked, otherwise the changes will be remebered until
4540  * the folder system is thawed.
4541  *
4542  * \param item The FolderItem that was changed
4543  * \param update_flags Type of changed that was made
4544  */
folder_item_update(FolderItem * item,FolderItemUpdateFlags update_flags)4545 void folder_item_update(FolderItem *item, FolderItemUpdateFlags update_flags)
4546 {
4547 	folder_item_update_with_msg(item, update_flags, NULL);
4548 }
4549 
folder_item_update_recursive(FolderItem * item,FolderItemUpdateFlags update_flags)4550 void folder_item_update_recursive(FolderItem *item, FolderItemUpdateFlags update_flags)
4551 {
4552 	GNode *node = item->folder->node;
4553 
4554 	node = g_node_find(node, G_PRE_ORDER, G_TRAVERSE_ALL, item);
4555 	node = node->children;
4556 
4557 	folder_item_update(item, update_flags);
4558 	while (node != NULL) {
4559 		if (node && node->data) {
4560 			FolderItem *next_item = (FolderItem*) node->data;
4561 
4562 			folder_item_update(next_item, update_flags);
4563 		}
4564 		node = node->next;
4565 	}
4566 }
4567 
folder_item_update_freeze(void)4568 void folder_item_update_freeze(void)
4569 {
4570 	folder_item_update_freeze_cnt++;
4571 }
4572 
folder_item_update_func(FolderItem * item,gpointer data)4573 static void folder_item_update_func(FolderItem *item, gpointer data)
4574 {
4575 	FolderItemUpdateData source;
4576 
4577 	if (item->update_flags) {
4578 		source.item = item;
4579 		source.update_flags = item->update_flags;
4580 		source.msg = NULL;
4581 		hooks_invoke(FOLDER_ITEM_UPDATE_HOOKLIST, &source);
4582 		item->update_flags = 0;
4583 	}
4584 }
4585 
folder_item_update_thaw(void)4586 void folder_item_update_thaw(void)
4587 {
4588 	if (folder_item_update_freeze_cnt > 0)
4589 		folder_item_update_freeze_cnt--;
4590 	if (folder_item_update_freeze_cnt == 0) {
4591 		/* Update all folders */
4592 		folder_func_to_all_folders(folder_item_update_func, NULL);
4593 	}
4594 }
4595 
folder_item_synchronise(FolderItem * item)4596 void folder_item_synchronise(FolderItem *item)
4597 {
4598 	if (!item)
4599 		return;
4600 	if (item->prefs->offlinesync && item->folder->klass->synchronise) {
4601 		statusbar_print_all(_("Synchronising %s for offline use...\n"), item->path ? item->path : "(null)");
4602 		item->folder->klass->synchronise(item,
4603 			item->prefs->offlinesync_days);
4604 		if (item->prefs->offlinesync_days > 0 &&
4605 		    item->prefs->remove_old_bodies)
4606 			folder_item_clean_local_files(item, item->prefs->offlinesync_days);
4607 		statusbar_pop_all();
4608 	}
4609 }
4610 
folder_item_synchronise_func(FolderItem * item,gpointer data)4611 static void folder_item_synchronise_func(FolderItem *item, gpointer data)
4612 {
4613 	Folder *folder = (Folder *)data;
4614 	if (folder == NULL || item->folder == folder) {
4615 		folder_item_synchronise(item);
4616 	}
4617 }
4618 
folder_synchronise(Folder * folder)4619 void folder_synchronise(Folder *folder)
4620 {
4621 	folder_func_to_all_folders(folder_item_synchronise_func, folder);
4622 }
4623 
4624 typedef struct _WantSyncData {
4625 	Folder *folder;
4626 	gboolean want_sync;
4627 } WantSyncData;
4628 
folder_item_want_synchronise_func(FolderItem * item,gpointer data)4629 static void folder_item_want_synchronise_func(FolderItem *item, gpointer data)
4630 {
4631 	WantSyncData *want_sync_data = (WantSyncData *)data;
4632 
4633 	if (want_sync_data->folder == NULL || item->folder == want_sync_data->folder) {
4634 		if (item->prefs->offlinesync && item->folder->klass->synchronise)
4635 			want_sync_data->want_sync |= TRUE;
4636 	}
4637 }
4638 
folder_want_synchronise(Folder * folder)4639 gboolean folder_want_synchronise(Folder *folder)
4640 {
4641 	WantSyncData *want_sync_data = g_new0(WantSyncData, 1);
4642 	gboolean result;
4643 	want_sync_data->folder = folder;
4644 	want_sync_data->want_sync = FALSE;
4645 
4646 	folder_func_to_all_folders(folder_item_want_synchronise_func, want_sync_data);
4647 	result = want_sync_data->want_sync;
4648 	g_free(want_sync_data);
4649 	if (result > 0)
4650 		debug_print("Folder %s wants sync\n", folder->name);
4651 	return result;
4652 }
4653 
folder_item_set_batch(FolderItem * item,gboolean batch)4654 void folder_item_set_batch (FolderItem *item, gboolean batch)
4655 {
4656 	if (!item || !item->folder)
4657 		return;
4658 	if (item->folder->klass->set_batch) {
4659 		item->folder->klass->set_batch(item->folder, item, batch);
4660 	}
4661 }
4662 
folder_has_parent_of_type(FolderItem * item,SpecialFolderItemType type)4663 gboolean folder_has_parent_of_type(FolderItem *item,
4664 					  SpecialFolderItemType type)
4665 {
4666 	FolderItem *cur = item;
4667 
4668 	if (!item)
4669 		return FALSE;
4670 	/* if we already know it, make it short */
4671 	if (item->parent_stype != -1) {
4672 		return (item->parent_stype == type);
4673 	}
4674 
4675 	/* if we don't, find the type from the first possible parent,
4676 	 * and set our parent type to be faster next time */
4677 	while (cur) {
4678 		if (cur->stype == type || cur->parent_stype == type) {
4679 			item->parent_stype = type;
4680 			return TRUE;
4681 		}
4682 		cur = folder_item_parent(cur);
4683 	}
4684 
4685 	/* if we didn't match what was asked, we didn't return. If our
4686 	 * parent type is unknown, we may as well find it now to be faster
4687 	 * later. */
4688 	if (item->parent_stype == -1) {
4689 		cur = item;
4690 		while (cur) {
4691 			/* here's an exception: Inbox subfolders are normal. */
4692 			if (item->parent_stype == -1 && cur->stype == F_INBOX
4693 			&& item != cur) {
4694 				item->parent_stype = F_NORMAL;
4695 				break;
4696 			}
4697 			/* ah, we know this parent's parent's type, we may as
4698 			 * well copy it instead of going up the full way */
4699 			if (cur->parent_stype != -1) {
4700 				item->parent_stype = cur->parent_stype;
4701 				break;
4702 			}
4703 			/* we found a parent that has a special type. That's
4704 			 * our parent type. */
4705 			if (cur->stype != F_NORMAL) {
4706 				cur->parent_stype = cur->stype;
4707 				item->parent_stype = cur->stype;
4708 				break;
4709 			}
4710 			/* if we didn't find anything, go up once more */
4711 			cur = folder_item_parent(cur);
4712 		}
4713 		/* as we still didn't find anything, our parents must all be
4714 		 * normal. */
4715 		if (item->parent_stype == -1) {
4716 			item->parent_stype = F_NORMAL;
4717 		}
4718 	}
4719 	return FALSE;
4720 }
4721 
folder_is_child_of(FolderItem * item,FolderItem * parent)4722 gboolean folder_is_child_of(FolderItem *item, FolderItem *parent)
4723 {
4724 	if (item == NULL || parent == NULL)
4725 		return FALSE;
4726 
4727 	while (item != NULL) {
4728 		if (parent == item)
4729 			return TRUE;
4730 
4731 		item = folder_item_parent(item);
4732 	}
4733 
4734 	return FALSE;
4735 }
4736 
4737 
folder_subscribe(const gchar * uri)4738 gboolean folder_subscribe (const gchar *uri)
4739 {
4740 	GList *cur;
4741 	for (cur = folder_get_list(); cur != NULL; cur = g_list_next(cur)) {
4742 		Folder *folder = (Folder *) cur->data;
4743 
4744 		if (folder->klass->subscribe
4745 		&&  folder->klass->subscribe(folder, uri)) {
4746 			return TRUE;
4747 		}
4748 	}
4749 	return FALSE;
4750 
4751 }
4752 
folder_get_sort_type(Folder * folder,FolderSortKey * sort_key,FolderSortType * sort_type)4753 gboolean folder_get_sort_type		(Folder		*folder,
4754 					 FolderSortKey	*sort_key,
4755 					 FolderSortType	*sort_type)
4756 {
4757 	if (!folder || !sort_key || !sort_type)
4758 		return FALSE;
4759 	if (folder->klass->get_sort_type == NULL)
4760 		return FALSE;
4761 	folder->klass->get_sort_type(folder, sort_key, sort_type);
4762 	return TRUE;
4763 }
4764 
folder_item_search_msgs(Folder * folder,FolderItem * container,MsgNumberList ** msgs,gboolean * on_server,MatcherList * predicate,SearchProgressNotify progress_cb,gpointer progress_data)4765 gint folder_item_search_msgs	(Folder			*folder,
4766 				 FolderItem		*container,
4767 				 MsgNumberList		**msgs,
4768 				 gboolean		*on_server,
4769 				 MatcherList		*predicate,
4770 				 SearchProgressNotify	progress_cb,
4771 				 gpointer		progress_data)
4772 {
4773 	gint result = -1;
4774 
4775 	folder_item_update_freeze();
4776 
4777 	if (folder->klass->search_msgs)
4778 		result = folder->klass->search_msgs(folder, container,
4779 				msgs, on_server, predicate, progress_cb, progress_data);
4780 	if (result < 0)
4781 		result = folder_item_search_msgs_local(folder, container,
4782 				msgs, on_server, predicate, progress_cb, progress_data);
4783 
4784 	folder_item_update_thaw();
4785 
4786 	return result;
4787 }
4788 
folder_item_get_number_list(FolderItem * item)4789 MsgNumberList *folder_item_get_number_list(FolderItem *item)
4790 {
4791 	GSList *nums = NULL;
4792 	GSList *msglist = folder_item_get_msg_list(item);
4793 
4794 	nums = procmsg_get_number_list_for_msgs(msglist);
4795 	procmsg_msg_list_free(msglist);
4796 
4797 	return nums;
4798 }
4799 
folder_item_search_msgs_local(Folder * folder,FolderItem * container,MsgNumberList ** msgs,gboolean * on_server,MatcherList * predicate,SearchProgressNotify progress_cb,gpointer progress_data)4800 gint folder_item_search_msgs_local	(Folder			*folder,
4801 					 FolderItem		*container,
4802 					 MsgNumberList		**msgs,
4803 					 gboolean		*on_server,
4804 					 MatcherList		*predicate,
4805 					 SearchProgressNotify	progress_cb,
4806 					 gpointer		progress_data)
4807 {
4808 	GSList *result = NULL;
4809 	GSList *cur = NULL;
4810 	gint matched_count = 0;
4811 	guint processed_count = 0;
4812 	gint msgcount;
4813 	GSList *nums = NULL;
4814 
4815 	if (*msgs == NULL) {
4816 		nums = folder_item_get_number_list(container);
4817 	} else {
4818 		nums = *msgs;
4819 	}
4820 
4821 	msgcount = g_slist_length(nums);
4822 
4823 	if (msgcount < 0)
4824 		return -1;
4825 
4826 	for (cur = nums; cur != NULL; cur = cur->next) {
4827 		guint msgnum = GPOINTER_TO_UINT(cur->data);
4828 		MsgInfo *msg = folder_item_get_msginfo(container, msgnum);
4829 
4830 		if (msg == NULL) {
4831 			g_slist_free(result);
4832 			return -1;
4833 		}
4834 
4835 		if (matcherlist_match(predicate, msg)) {
4836 			result = g_slist_prepend(result, GUINT_TO_POINTER(msg->msgnum));
4837 			matched_count++;
4838 		}
4839 		processed_count++;
4840 
4841 		procmsg_msginfo_free(&msg);
4842 
4843 		if (progress_cb != NULL
4844 		    && !progress_cb(progress_data, FALSE, processed_count,
4845 			    matched_count, msgcount))
4846 			break;
4847 	}
4848 
4849 	g_slist_free(nums);
4850 	*msgs = g_slist_reverse(result);
4851 
4852 	return matched_count;
4853 }
4854 
4855 /* Tests if a local (on disk) folder name is acceptable. */
folder_local_name_ok(const gchar * name)4856 gboolean folder_local_name_ok(const gchar *name)
4857 {
4858 #ifdef G_OS_WIN32
4859 	if (name[0] == '.' || name[strlen(name) - 1] == '.') {
4860 		alertpanel_error(_("A folder name cannot begin or end with a dot."));
4861 		return FALSE;
4862 	}
4863 	if (name[strlen(name) - 1] == ' ') {
4864 		alertpanel_error(_("A folder name can not end with a space."));
4865 		return FALSE;
4866 	}
4867 #endif
4868 
4869 	return TRUE;
4870 }
4871