talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

folder.c (111552B)


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