1 /*
2  * LibSylph -- E-Mail client library
3  * Copyright (C) 1999-2012 Hiroyuki Yamamoto
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23 
24 #include "defs.h"
25 
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 
32 #include "folder.h"
33 #include "session.h"
34 #include "imap.h"
35 #include "news.h"
36 #include "mh.h"
37 #include "virtual.h"
38 #include "utils.h"
39 #include "xml.h"
40 #include "codeconv.h"
41 #include "prefs.h"
42 #include "account.h"
43 #include "prefs_account.h"
44 #include "sylmain.h"
45 
46 typedef struct _FolderPrivData FolderPrivData;
47 
48 struct _FolderPrivData {
49 	Folder *folder;
50 	FolderItem *junk;
51 
52 	FolderUIFunc2 ui_func2;
53 	gpointer ui_func2_data;
54 
55 	gpointer data;
56 };
57 
58 static GList *folder_list = NULL;
59 static GList *folder_priv_list = NULL;
60 
61 static void folder_init		(Folder		*folder,
62 				 const gchar	*name);
63 
64 static FolderPrivData *folder_get_priv	(Folder		*folder);
65 
66 static gboolean folder_read_folder_func	(GNode		*node,
67 					 gpointer	 data);
68 static gchar *folder_get_list_path	(void);
69 static void folder_write_list_recursive	(GNode		*node,
70 					 gpointer	 data);
71 
72 
folder_new(FolderType type,const gchar * name,const gchar * path)73 Folder *folder_new(FolderType type, const gchar *name, const gchar *path)
74 {
75 	Folder *folder = NULL;
76 
77 	name = name ? name : path;
78 	switch (type) {
79 	case F_MH:
80 		folder = mh_get_class()->folder_new(name, path);
81 		break;
82 	case F_IMAP:
83 		folder = imap_get_class()->folder_new(name, path);
84 		break;
85 	case F_NEWS:
86 		folder = news_get_class()->folder_new(name, path);
87 		break;
88 	default:
89 		return NULL;
90 	}
91 
92 	return folder;
93 }
94 
folder_init(Folder * folder,const gchar * name)95 static void folder_init(Folder *folder, const gchar *name)
96 {
97 	FolderItem *item;
98 
99 	g_return_if_fail(folder != NULL);
100 
101 	folder_set_name(folder, name);
102 	folder->account = NULL;
103 	folder->inbox = NULL;
104 	folder->outbox = NULL;
105 	folder->draft = NULL;
106 	folder->queue = NULL;
107 	folder->trash = NULL;
108 	folder->ui_func = NULL;
109 	folder->ui_func_data = NULL;
110 	item = folder_item_new(name, NULL);
111 	item->folder = folder;
112 	folder->node = item->node = g_node_new(item);
113 	folder->data = NULL;
114 }
115 
folder_local_folder_init(Folder * folder,const gchar * name,const gchar * path)116 void folder_local_folder_init(Folder *folder, const gchar *name,
117 			      const gchar *path)
118 {
119 	folder_init(folder, name);
120 	LOCAL_FOLDER(folder)->rootpath = g_strdup(path);
121 }
122 
folder_remote_folder_init(Folder * folder,const gchar * name,const gchar * path)123 void folder_remote_folder_init(Folder *folder, const gchar *name,
124 			       const gchar *path)
125 {
126 	folder_init(folder, name);
127 	REMOTE_FOLDER(folder)->session = NULL;
128 	REMOTE_FOLDER(folder)->remove_cache_on_destroy = TRUE;
129 }
130 
folder_destroy(Folder * folder)131 void folder_destroy(Folder *folder)
132 {
133 	FolderPrivData *priv;
134 
135 	g_return_if_fail(folder != NULL);
136 	g_return_if_fail(folder->klass->destroy != NULL);
137 
138 	debug_print("folder_destroy: destroying Folder (%p)\n", folder);
139 
140 	folder->klass->destroy(folder);
141 
142 	folder_list = g_list_remove(folder_list, folder);
143 
144 	folder_tree_destroy(folder);
145 
146 	priv = folder_get_priv(folder);
147 	folder_priv_list = g_list_remove(folder_priv_list, priv);
148 	g_free(priv);
149 
150 	g_free(folder->name);
151 	g_free(folder);
152 }
153 
folder_local_folder_destroy(LocalFolder * lfolder)154 void folder_local_folder_destroy(LocalFolder *lfolder)
155 {
156 	g_return_if_fail(lfolder != NULL);
157 
158 	g_free(lfolder->rootpath);
159 }
160 
folder_remote_folder_destroy(RemoteFolder * rfolder)161 void folder_remote_folder_destroy(RemoteFolder *rfolder)
162 {
163 	g_return_if_fail(rfolder != NULL);
164 
165 	if (rfolder->session)
166 		session_destroy(rfolder->session);
167 }
168 
folder_remote_folder_destroy_all_sessions(void)169 gint folder_remote_folder_destroy_all_sessions(void)
170 {
171 	GList *list;
172 	Folder *folder;
173 	RemoteFolder *rfolder;
174 
175 	for (list = folder_list; list != NULL; list = list->next) {
176 		folder = FOLDER(list->data);
177 		if (FOLDER_IS_REMOTE(folder)) {
178 			rfolder = REMOTE_FOLDER(folder);
179 			if (rfolder->session &&
180 			    !folder_remote_folder_is_session_active(rfolder)) {
181 				session_destroy(rfolder->session);
182 				rfolder->session = NULL;
183 			}
184 		}
185 	}
186 
187 	return 0;
188 }
189 
folder_remote_folder_is_session_active(RemoteFolder * rfolder)190 gboolean folder_remote_folder_is_session_active(RemoteFolder *rfolder)
191 {
192 	g_return_val_if_fail(rfolder != NULL, FALSE);
193 
194 	if (FOLDER_TYPE(rfolder) == F_IMAP)
195 		return imap_is_session_active(IMAP_FOLDER(rfolder));
196 
197 	return FALSE;
198 }
199 
folder_remote_folder_active_session_exist(void)200 gboolean folder_remote_folder_active_session_exist(void)
201 {
202 	GList *list;
203 	Folder *folder;
204 	RemoteFolder *rfolder;
205 
206 	for (list = folder_list; list != NULL; list = list->next) {
207 		folder = FOLDER(list->data);
208 		if (FOLDER_IS_REMOTE(folder)) {
209 			rfolder = REMOTE_FOLDER(folder);
210 			if (folder_remote_folder_is_session_active(rfolder))
211 				return TRUE;
212 		}
213 	}
214 
215 	return FALSE;
216 }
217 
folder_scan_tree(Folder * folder)218 gint folder_scan_tree(Folder *folder)
219 {
220 	g_return_val_if_fail(folder != NULL, -1);
221 	g_return_val_if_fail(folder->klass->scan_tree != NULL, -1);
222 
223 	return folder->klass->scan_tree(folder);
224 }
225 
folder_create_tree(Folder * folder)226 gint folder_create_tree(Folder *folder)
227 {
228 	g_return_val_if_fail(folder != NULL, -1);
229 	g_return_val_if_fail(folder->klass->create_tree != NULL, -1);
230 
231 	return folder->klass->create_tree(folder);
232 }
233 
folder_item_new(const gchar * name,const gchar * path)234 FolderItem *folder_item_new(const gchar *name, const gchar *path)
235 {
236 	FolderItem *item;
237 
238 	item = g_new0(FolderItem, 1);
239 
240 	item->stype = F_NORMAL;
241 	item->name = g_strdup(name);
242 	item->path = g_strdup(path);
243 	item->mtime = 0;
244 	item->new = 0;
245 	item->unread = 0;
246 	item->total = 0;
247 	item->unmarked_num = 0;
248 	item->last_num = -1;
249 	item->no_sub = FALSE;
250 	item->no_select = FALSE;
251 	item->collapsed = FALSE;
252 	item->threaded = TRUE;
253 	item->opened = FALSE;
254 	item->updated = FALSE;
255 	item->cache_dirty = FALSE;
256 	item->mark_dirty = FALSE;
257 	item->node = NULL;
258 	item->parent = NULL;
259 	item->folder = NULL;
260 	item->account = NULL;
261 	item->ac_apply_sub = FALSE;
262 	item->auto_to = NULL;
263 	item->use_auto_to_on_reply = FALSE;
264 	item->auto_cc = NULL;
265 	item->auto_bcc = NULL;
266 	item->auto_replyto = NULL;
267 	item->mark_queue = NULL;
268 	item->last_selected = 0;
269 	item->qsearch_cond_type = 0;
270 	item->data = NULL;
271 
272 	return item;
273 }
274 
folder_item_append(FolderItem * parent,FolderItem * item)275 void folder_item_append(FolderItem *parent, FolderItem *item)
276 {
277 	g_return_if_fail(parent != NULL);
278 	g_return_if_fail(parent->folder != NULL);
279 	g_return_if_fail(parent->node != NULL);
280 	g_return_if_fail(item != NULL);
281 
282 	item->parent = parent;
283 	item->folder = parent->folder;
284 	item->node = g_node_append_data(parent->node, item);
285 }
286 
folder_item_copy(FolderItem * item)287 FolderItem *folder_item_copy(FolderItem *item)
288 {
289 	FolderItem *new_item;
290 
291 	new_item = g_new0(FolderItem, 1);
292 
293 	new_item->stype = item->stype;
294 	new_item->name = g_strdup(item->name);
295 	new_item->path = g_strdup(item->path);
296 	new_item->mtime = item->mtime;
297 	new_item->new = item->new;
298 	new_item->unread = item->unread;
299 	new_item->total = item->total;
300 	new_item->unmarked_num = item->unmarked_num;
301 	new_item->last_num = item->last_num;
302 	new_item->no_sub = item->no_sub;
303 	new_item->no_select = item->no_select;
304 	new_item->collapsed = item->collapsed;
305 	new_item->threaded = item->threaded;
306 	new_item->opened = item->opened;
307 	new_item->updated = item->updated;
308 	new_item->cache_dirty = item->cache_dirty;
309 	new_item->mark_dirty = item->mark_dirty;
310 	new_item->node = item->node;
311 	new_item->parent = item->parent;
312 	new_item->folder = item->folder;
313 	new_item->account = item->account;
314 	new_item->ac_apply_sub = item->ac_apply_sub;
315 	new_item->auto_to = g_strdup(item->auto_to);
316 	new_item->use_auto_to_on_reply = item->use_auto_to_on_reply;
317 	new_item->auto_cc = g_strdup(item->auto_cc);
318 	new_item->auto_bcc = g_strdup(item->auto_bcc);
319 	new_item->auto_replyto = g_strdup(item->auto_replyto);
320 	new_item->mark_queue = item->mark_queue;
321 	new_item->last_selected = item->last_selected;
322 	new_item->qsearch_cond_type = item->qsearch_cond_type;
323 	new_item->data = item->data;
324 
325 	return new_item;
326 }
327 
folder_item_remove_func(GNode * node,gpointer data)328 static gboolean folder_item_remove_func(GNode *node, gpointer data)
329 {
330 	FolderItem *item = FOLDER_ITEM(node->data);
331 
332 	folder_item_destroy(item);
333 	return FALSE;
334 }
335 
folder_item_remove(FolderItem * item)336 void folder_item_remove(FolderItem *item)
337 {
338 	GNode *node;
339 
340 	g_return_if_fail(item != NULL);
341 	g_return_if_fail(item->folder != NULL);
342 	g_return_if_fail(item->node != NULL);
343 
344 	node = item->node;
345 
346 	if (item->folder->node == node)
347 		item->folder->node = NULL;
348 
349 	g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
350 			folder_item_remove_func, NULL);
351 	g_node_destroy(node);
352 }
353 
folder_item_remove_children(FolderItem * item)354 void folder_item_remove_children(FolderItem *item)
355 {
356 	GNode *node, *next;
357 
358 	g_return_if_fail(item != NULL);
359 	g_return_if_fail(item->folder != NULL);
360 	g_return_if_fail(item->node != NULL);
361 
362 	node = item->node->children;
363 	while (node != NULL) {
364 		next = node->next;
365 		folder_item_remove(FOLDER_ITEM(node->data));
366 		node = next;
367 	}
368 }
369 
folder_item_destroy(FolderItem * item)370 void folder_item_destroy(FolderItem *item)
371 {
372 	Folder *folder;
373 
374 	g_return_if_fail(item != NULL);
375 
376 	folder = item->folder;
377 	if (folder) {
378 		if (folder->inbox == item)
379 			folder->inbox = NULL;
380 		else if (folder->outbox == item)
381 			folder->outbox = NULL;
382 		else if (folder->draft == item)
383 			folder->draft = NULL;
384 		else if (folder->queue == item)
385 			folder->queue = NULL;
386 		else if (folder->trash == item)
387 			folder->trash = NULL;
388 		else if (folder_get_junk(folder) == item)
389 			folder_set_junk(folder, NULL);
390 	}
391 
392 	g_free(item->name);
393 	g_free(item->path);
394 	g_free(item->auto_to);
395 	g_free(item->auto_cc);
396 	g_free(item->auto_bcc);
397 	g_free(item->auto_replyto);
398 	g_free(item);
399 }
400 
folder_item_compare(FolderItem * item_a,FolderItem * item_b)401 gint folder_item_compare(FolderItem *item_a, FolderItem *item_b)
402 {
403 	gint ret;
404 	gchar *str_a, *str_b;
405 
406 	if (!item_a || !item_b)
407 		return 0;
408 	if (!item_a->parent || !item_b->parent)
409 		return 0;
410 	if (!item_a->name || !item_b->name)
411 		return 0;
412 
413 	/* if both a and b are special folders, sort them according to
414 	 * their types (which is in-order). Note that this assumes that
415 	 * there are no multiple folders of a special type. As a special
416 	 * case, two virtual folders are compared like normal ones. */
417 	if (item_a->stype != F_NORMAL && item_b->stype != F_NORMAL &&
418 	    (item_a->stype != F_VIRTUAL || item_b->stype != F_VIRTUAL))
419 		return item_a->stype - item_b->stype;
420 
421 	 /* if b is normal folder, and a is not, b is smaller (ends up
422 	  * lower in the list) */
423 	if (item_a->stype != F_NORMAL && item_b->stype == F_NORMAL)
424 		return item_b->stype - item_a->stype;
425 
426 	/* if b is special folder, and a is not, b is larger (ends up
427 	 * higher in the list) */
428 	if (item_a->stype == F_NORMAL && item_b->stype != F_NORMAL)
429 		return item_b->stype - item_a->stype;
430 
431 	/* otherwise just compare the folder names */
432 	str_a = g_utf8_casefold(item_a->name, -1);
433 	str_b = g_utf8_casefold(item_b->name, -1);
434 	ret = g_utf8_collate(str_a, str_b);
435 	g_free(str_b);
436 	g_free(str_a);
437 
438 	return ret;
439 }
440 
folder_set_ui_func(Folder * folder,FolderUIFunc func,gpointer data)441 void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
442 {
443 	g_return_if_fail(folder != NULL);
444 
445 	folder->ui_func = func;
446 	folder->ui_func_data = data;
447 }
448 
folder_set_ui_func2(Folder * folder,FolderUIFunc2 func,gpointer data)449 void folder_set_ui_func2(Folder *folder, FolderUIFunc2 func, gpointer data)
450 {
451 	FolderPrivData *priv;
452 
453 	priv = folder_get_priv(folder);
454 	if (priv) {
455 		priv->ui_func2 = func;
456 		priv->ui_func2_data = data;
457 	}
458 }
459 
folder_get_ui_func2(Folder * folder)460 FolderUIFunc2 folder_get_ui_func2(Folder *folder)
461 {
462 	FolderPrivData *priv;
463 
464 	priv = folder_get_priv(folder);
465 	if (priv)
466 		return priv->ui_func2;
467 
468 	return NULL;
469 }
470 
folder_call_ui_func2(Folder * folder,FolderItem * item,guint count,guint total)471 gboolean folder_call_ui_func2(Folder *folder, FolderItem *item, guint count,
472 			      guint total)
473 {
474 	FolderPrivData *priv;
475 
476 	priv = folder_get_priv(folder);
477 	if (priv && priv->ui_func2) {
478 		return priv->ui_func2(folder, item, count, total,
479 				      priv->ui_func2_data);
480 	}
481 
482 	return TRUE;
483 }
484 
folder_set_name(Folder * folder,const gchar * name)485 void folder_set_name(Folder *folder, const gchar *name)
486 {
487 	g_return_if_fail(folder != NULL);
488 
489 	g_free(folder->name);
490 	folder->name = name ? g_strdup(name) : NULL;
491 	if (folder->node && folder->node->data) {
492 		FolderItem *item = (FolderItem *)folder->node->data;
493 
494 		g_free(item->name);
495 		item->name = name ? g_strdup(name) : NULL;
496 	}
497 }
498 
folder_tree_destroy(Folder * folder)499 void folder_tree_destroy(Folder *folder)
500 {
501 	g_return_if_fail(folder != NULL);
502 
503 	if (folder->node)
504 		folder_item_remove(FOLDER_ITEM(folder->node->data));
505 }
506 
folder_add(Folder * folder)507 void folder_add(Folder *folder)
508 {
509 	Folder *cur_folder;
510 	GList *cur;
511 	gint i;
512 	FolderPrivData *priv;
513 
514 	debug_print("Adding Folder (%p) to folder list\n", folder);
515 
516 	g_return_if_fail(folder != NULL);
517 
518 	for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
519 		cur_folder = FOLDER(cur->data);
520 		if (FOLDER_TYPE(folder) == F_MH) {
521 			if (FOLDER_TYPE(cur_folder) != F_MH) break;
522 		} else if (FOLDER_TYPE(folder) == F_IMAP) {
523 			if (FOLDER_TYPE(cur_folder) != F_MH &&
524 			    FOLDER_TYPE(cur_folder) != F_IMAP) break;
525 		} else if (FOLDER_TYPE(folder) == F_NEWS) {
526 			if (FOLDER_TYPE(cur_folder) != F_MH &&
527 			    FOLDER_TYPE(cur_folder) != F_IMAP &&
528 			    FOLDER_TYPE(cur_folder) != F_NEWS) break;
529 		}
530 	}
531 
532 	folder_list = g_list_insert(folder_list, folder, i);
533 	priv = g_new0(FolderPrivData, 1);
534 	priv->folder = folder;
535 	folder_priv_list = g_list_insert(folder_priv_list, priv, i);
536 }
537 
folder_get_list(void)538 GList *folder_get_list(void)
539 {
540 	return folder_list;
541 }
542 
folder_read_list(void)543 gint folder_read_list(void)
544 {
545 	GNode *node;
546 	XMLNode *xmlnode;
547 	gchar *path;
548 
549 	path = folder_get_list_path();
550 	if (!is_file_exist(path)) return -1;
551 	node = xml_parse_file(path);
552 	if (!node) return -1;
553 
554 	xmlnode = node->data;
555 	if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
556 		g_warning("wrong folder list\n");
557 		xml_free_tree(node);
558 		return -1;
559 	}
560 
561 	g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
562 			folder_read_folder_func, NULL);
563 
564 	xml_free_tree(node);
565 	if (folder_list)
566 		return 0;
567 	else
568 		return -1;
569 }
570 
folder_write_list(void)571 void folder_write_list(void)
572 {
573 	GList *list;
574 	Folder *folder;
575 	gchar *path;
576 	PrefFile *pfile;
577 
578 	path = folder_get_list_path();
579 	if ((pfile = prefs_file_open(path)) == NULL) return;
580 
581 	fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
582 		CS_INTERNAL);
583 	fputs("\n<folderlist>\n", pfile->fp);
584 
585 	for (list = folder_list; list != NULL; list = list->next) {
586 		folder = list->data;
587 		folder_write_list_recursive(folder->node, pfile->fp);
588 	}
589 
590 	fputs("</folderlist>\n", pfile->fp);
591 
592 	if (prefs_file_close(pfile) < 0)
593 		g_warning("failed to write folder list.\n");
594 
595 	if (syl_app_get())
596 		g_signal_emit_by_name(syl_app_get(), "folderlist-updated");
597 }
598 
599 struct TotalMsgStatus
600 {
601 	guint new;
602 	guint unread;
603 	guint total;
604 	GString *str;
605 };
606 
folder_get_status_full_all_func(GNode * node,gpointer data)607 static gboolean folder_get_status_full_all_func(GNode *node, gpointer data)
608 {
609 	FolderItem *item;
610 	struct TotalMsgStatus *status = (struct TotalMsgStatus *)data;
611 	gchar *id;
612 
613 	g_return_val_if_fail(node->data != NULL, FALSE);
614 
615 	item = FOLDER_ITEM(node->data);
616 
617 	if (!item->path) return FALSE;
618 
619 	status->new += item->new;
620 	status->unread += item->unread;
621 	status->total += item->total;
622 
623 	if (status->str) {
624 		id = folder_item_get_identifier(item);
625 		g_string_sprintfa(status->str, "%5d %5d %5d %s\n",
626 				  item->new, item->unread,
627 				  item->total, id);
628 		g_free(id);
629 	}
630 
631 	return FALSE;
632 }
633 
folder_get_status_full_all(GString * str,guint * new,guint * unread,guint * total)634 static void folder_get_status_full_all(GString *str, guint *new, guint *unread,
635 				       guint *total)
636 {
637 	GList *list;
638 	Folder *folder;
639 	struct TotalMsgStatus status;
640 
641 	status.new = status.unread = status.total = 0;
642 	status.str = str;
643 
644 	debug_print("Counting total number of messages...\n");
645 
646 	for (list = folder_list; list != NULL; list = list->next) {
647 		folder = FOLDER(list->data);
648 		if (folder->node)
649 			g_node_traverse(folder->node, G_PRE_ORDER,
650 					G_TRAVERSE_ALL, -1,
651 					folder_get_status_full_all_func,
652 					&status);
653 	}
654 
655 	*new = status.new;
656 	*unread = status.unread;
657 	*total = status.total;
658 }
659 
folder_get_status(GPtrArray * folders,gboolean full)660 gchar *folder_get_status(GPtrArray *folders, gboolean full)
661 {
662 	guint new, unread, total;
663 	GString *str;
664 	gint i;
665 	gchar *ret;
666 
667 	new = unread = total = 0;
668 
669 	str = g_string_new(NULL);
670 
671 	if (folders) {
672 		for (i = 0; i < folders->len; i++) {
673 			FolderItem *item;
674 
675 			item = g_ptr_array_index(folders, i);
676 			new += item->new;
677 			unread += item->unread;
678 			total += item->total;
679 
680 			if (full) {
681 				gchar *id;
682 
683 				id = folder_item_get_identifier(item);
684 				g_string_sprintfa(str, "%5d %5d %5d %s\n",
685 						  item->new, item->unread,
686 						  item->total, id);
687 				g_free(id);
688 			}
689 		}
690 	} else {
691 		folder_get_status_full_all(full ? str : NULL,
692 					   &new, &unread, &total);
693 	}
694 
695 	if (full)
696 		g_string_sprintfa(str, "%5d %5d %5d\n", new, unread, total);
697 	else
698 		g_string_sprintfa(str, "%d %d %d\n", new, unread, total);
699 
700 	ret = str->str;
701 	g_string_free(str, FALSE);
702 
703 	return ret;
704 }
705 
folder_find_from_path(const gchar * path)706 Folder *folder_find_from_path(const gchar *path)
707 {
708 	GList *list;
709 	Folder *folder;
710 
711 	for (list = folder_list; list != NULL; list = list->next) {
712 		folder = list->data;
713 		if (FOLDER_TYPE(folder) == F_MH &&
714 		    !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
715 			return folder;
716 	}
717 
718 	return NULL;
719 }
720 
folder_find_from_name(const gchar * name,FolderType type)721 Folder *folder_find_from_name(const gchar *name, FolderType type)
722 {
723 	GList *list;
724 	Folder *folder;
725 
726 	for (list = folder_list; list != NULL; list = list->next) {
727 		folder = list->data;
728 		if (FOLDER_TYPE(folder) == type &&
729 		    strcmp2(name, folder->name) == 0)
730 			return folder;
731 	}
732 
733 	return NULL;
734 }
735 
folder_item_find_func(GNode * node,gpointer data)736 static gboolean folder_item_find_func(GNode *node, gpointer data)
737 {
738 	FolderItem *item = node->data;
739 	gpointer *d = data;
740 	const gchar *path = d[0];
741 
742 	if (path_cmp(path, item->path) != 0)
743 		return FALSE;
744 
745 	d[1] = item;
746 
747 	return TRUE;
748 }
749 
folder_find_item_from_path(const gchar * path)750 FolderItem *folder_find_item_from_path(const gchar *path)
751 {
752 	Folder *folder;
753 	gpointer d[2];
754 
755 	folder = folder_get_default_folder();
756 	g_return_val_if_fail(folder != NULL, NULL);
757 
758 	d[0] = (gpointer)path;
759 	d[1] = NULL;
760 	g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
761 			folder_item_find_func, d);
762 	return d[1];
763 }
764 
folder_find_child_item_by_name(FolderItem * item,const gchar * name)765 FolderItem *folder_find_child_item_by_name(FolderItem *item, const gchar *name)
766 {
767 	GNode *node;
768 	FolderItem *child;
769 	const gchar *base;
770 
771 	if (!name)
772 		return NULL;
773 
774 	for (node = item->node->children; node != NULL; node = node->next) {
775 		child = FOLDER_ITEM(node->data);
776 		base = g_basename(child->path);
777 #ifdef G_OS_WIN32
778 		if (base && g_ascii_strcasecmp(base, name) == 0)
779 #else
780 		if (strcmp2(base, name) == 0)
781 #endif
782 			return child;
783 	}
784 
785 	return NULL;
786 }
787 
788 static const struct {
789 	gchar *str;
790 	FolderType type;
791 } type_str_table[] = {
792 	{"#mh"     , F_MH},
793 	{"#mbox"   , F_MBOX},
794 	{"#maildir", F_MAILDIR},
795 	{"#imap"   , F_IMAP},
796 	{"#news"   , F_NEWS}
797 };
798 
folder_get_type_string(FolderType type)799 static gchar *folder_get_type_string(FolderType type)
800 {
801 	gint i;
802 
803 	for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
804 	     i++) {
805 		if (type_str_table[i].type == type)
806 			return type_str_table[i].str;
807 	}
808 
809 	return NULL;
810 }
811 
folder_get_type_from_string(const gchar * str)812 static FolderType folder_get_type_from_string(const gchar *str)
813 {
814 	gint i;
815 
816 	for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
817 	     i++) {
818 		if (g_ascii_strcasecmp(type_str_table[i].str, str) == 0)
819 			return type_str_table[i].type;
820 	}
821 
822 	return F_UNKNOWN;
823 }
824 
folder_get_identifier(Folder * folder)825 gchar *folder_get_identifier(Folder *folder)
826 {
827 	gchar *type_str;
828 
829 	g_return_val_if_fail(folder != NULL, NULL);
830 
831 	type_str = folder_get_type_string(FOLDER_TYPE(folder));
832 	return g_strconcat(type_str, "/", folder->name, NULL);
833 }
834 
folder_item_get_identifier(FolderItem * item)835 gchar *folder_item_get_identifier(FolderItem *item)
836 {
837 	gchar *id;
838 	gchar *folder_id;
839 
840 	g_return_val_if_fail(item != NULL, NULL);
841 
842 	if (!item->path) {
843 		if (!item->parent)
844 			return folder_get_identifier(item->folder);
845 		else
846 			return NULL;
847 	}
848 
849 	folder_id = folder_get_identifier(item->folder);
850 	id = g_strconcat(folder_id, "/", item->path, NULL);
851 	g_free(folder_id);
852 
853 	return id;
854 }
855 
folder_find_item_from_identifier(const gchar * identifier)856 FolderItem *folder_find_item_from_identifier(const gchar *identifier)
857 {
858 	Folder *folder;
859 	gpointer d[2];
860 	gchar *str;
861 	gchar *p;
862 	gchar *name;
863 	gchar *path;
864 	FolderType type;
865 
866 	g_return_val_if_fail(identifier != NULL, NULL);
867 
868 	if (*identifier != '#')
869 		return folder_find_item_from_path(identifier);
870 
871 	Xstrdup_a(str, identifier, return NULL);
872 
873 	p = strchr(str, '/');
874 	if (!p)
875 		return folder_find_item_from_path(identifier);
876 	*p = '\0';
877 	p++;
878 	type = folder_get_type_from_string(str);
879 	if (type == F_UNKNOWN)
880 		return folder_find_item_from_path(identifier);
881 
882 	name = p;
883 	p = strchr(p, '/');
884 	if (p) {
885 		*p = '\0';
886 		p++;
887 	}
888 
889 	folder = folder_find_from_name(name, type);
890 	if (!folder)
891 		return folder_find_item_from_path(identifier);
892 
893 	if (!p)
894 		return FOLDER_ITEM(folder->node->data);
895 
896 	path = p;
897 
898 	d[0] = (gpointer)path;
899 	d[1] = NULL;
900 	g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
901 			folder_item_find_func, d);
902 	return d[1];
903 }
904 
folder_find_item_and_num_from_id(const gchar * identifier,gint * num)905 FolderItem *folder_find_item_and_num_from_id(const gchar *identifier, gint *num)
906 {
907 	gchar *id;
908 	gchar *msg;
909 	FolderItem *item;
910 
911 	g_return_val_if_fail(identifier != NULL, NULL);
912 
913 	id = g_path_get_dirname(identifier);
914 	msg = g_path_get_basename(identifier);
915 	item = folder_find_item_from_identifier(id);
916 	*num = to_number(msg);
917 	g_free(msg);
918 	g_free(id);
919 
920 	return item;
921 }
922 
folder_get_default_folder(void)923 Folder *folder_get_default_folder(void)
924 {
925 	return folder_list ? FOLDER(folder_list->data) : NULL;
926 }
927 
folder_get_default_inbox(void)928 FolderItem *folder_get_default_inbox(void)
929 {
930 	Folder *folder;
931 
932 	if (!folder_list) return NULL;
933 	folder = FOLDER(folder_list->data);
934 	g_return_val_if_fail(folder != NULL, NULL);
935 	return folder->inbox;
936 }
937 
folder_get_default_outbox(void)938 FolderItem *folder_get_default_outbox(void)
939 {
940 	Folder *folder;
941 
942 	if (!folder_list) return NULL;
943 	folder = FOLDER(folder_list->data);
944 	g_return_val_if_fail(folder != NULL, NULL);
945 	return folder->outbox;
946 }
947 
folder_get_default_draft(void)948 FolderItem *folder_get_default_draft(void)
949 {
950 	Folder *folder;
951 
952 	if (!folder_list) return NULL;
953 	folder = FOLDER(folder_list->data);
954 	g_return_val_if_fail(folder != NULL, NULL);
955 	return folder->draft;
956 }
957 
folder_get_default_queue(void)958 FolderItem *folder_get_default_queue(void)
959 {
960 	Folder *folder;
961 
962 	if (!folder_list) return NULL;
963 	folder = FOLDER(folder_list->data);
964 	g_return_val_if_fail(folder != NULL, NULL);
965 	return folder->queue;
966 }
967 
folder_get_default_trash(void)968 FolderItem *folder_get_default_trash(void)
969 {
970 	Folder *folder;
971 
972 	if (!folder_list) return NULL;
973 	folder = FOLDER(folder_list->data);
974 	g_return_val_if_fail(folder != NULL, NULL);
975 	return folder->trash;
976 }
977 
folder_get_default_junk(void)978 FolderItem *folder_get_default_junk(void)
979 {
980 	FolderPrivData *priv;
981 
982 	if (!folder_list) return NULL;
983 	if (!folder_priv_list) return NULL;
984 	priv = (FolderPrivData *)folder_priv_list->data;
985 	g_return_val_if_fail(priv != NULL, NULL);
986 	g_return_val_if_fail(priv->folder != NULL, NULL);
987 	return priv->junk;
988 }
989 
folder_get_priv(Folder * folder)990 static FolderPrivData *folder_get_priv(Folder *folder)
991 {
992 	FolderPrivData *priv;
993 	GList *cur;
994 
995 	g_return_val_if_fail(folder != NULL, NULL);
996 
997 	for (cur = folder_priv_list; cur != NULL; cur = cur->next) {
998 		priv = (FolderPrivData *)cur->data;
999 		if (priv->folder == folder)
1000 			return priv;
1001 	}
1002 
1003 	g_warning("folder_get_priv: private data for Folder (%p) not found.",
1004 		  folder);
1005 
1006 	return NULL;
1007 }
1008 
folder_get_junk(Folder * folder)1009 FolderItem *folder_get_junk(Folder *folder)
1010 {
1011 	FolderPrivData *priv;
1012 
1013 	priv = folder_get_priv(folder);
1014 	if (priv)
1015 		return priv->junk;
1016 
1017 	return NULL;
1018 }
1019 
folder_set_junk(Folder * folder,FolderItem * item)1020 void folder_set_junk(Folder *folder, FolderItem *item)
1021 {
1022 	FolderPrivData *priv;
1023 
1024 	priv = folder_get_priv(folder);
1025 	if (priv)
1026 		priv->junk = item;
1027 }
1028 
folder_item_is_trash(FolderItem * item)1029 gboolean folder_item_is_trash(FolderItem *item)
1030 {
1031 	PrefsAccount *ac;
1032 	FolderItem *trash;
1033 
1034 	g_return_val_if_fail(item != NULL, FALSE);
1035 
1036 	if (item->stype == F_TRASH)
1037 		return TRUE;
1038 
1039 	ac = account_find_from_item_property(item);
1040 	if (ac && ac->set_trash_folder && ac->trash_folder) {
1041 		trash = folder_find_item_from_identifier(ac->trash_folder);
1042 		if (trash == item)
1043 			return TRUE;
1044 	}
1045 
1046 	return FALSE;
1047 }
1048 
1049 #define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type)	\
1050 {							\
1051 	if (!folder->member) {				\
1052 		item = folder_item_new(dir, dir);	\
1053 		item->stype = type;			\
1054 		folder_item_append(rootitem, item);	\
1055 		folder->member = item;			\
1056 	}						\
1057 }
1058 
folder_set_missing_folders(void)1059 void folder_set_missing_folders(void)
1060 {
1061 	Folder *folder;
1062 	FolderItem *rootitem;
1063 	FolderItem *item;
1064 	GList *list;
1065 
1066 	for (list = folder_list; list != NULL; list = list->next) {
1067 		folder = list->data;
1068 		if (FOLDER_TYPE(folder) != F_MH) continue;
1069 		rootitem = FOLDER_ITEM(folder->node->data);
1070 		g_return_if_fail(rootitem != NULL);
1071 
1072 		if (folder->inbox && folder->outbox && folder->draft &&
1073 		    folder->queue && folder->trash && folder_get_junk(folder))
1074 			continue;
1075 
1076 		if (folder_create_tree(folder) < 0) {
1077 			g_warning("%s: can't create the folder tree.\n",
1078 				  LOCAL_FOLDER(folder)->rootpath);
1079 			continue;
1080 		}
1081 
1082 		CREATE_FOLDER_IF_NOT_EXIST(inbox,  INBOX_DIR,  F_INBOX);
1083 		CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX);
1084 		CREATE_FOLDER_IF_NOT_EXIST(draft,  DRAFT_DIR,  F_DRAFT);
1085 		CREATE_FOLDER_IF_NOT_EXIST(queue,  QUEUE_DIR,  F_QUEUE);
1086 		CREATE_FOLDER_IF_NOT_EXIST(trash,  TRASH_DIR,  F_TRASH);
1087 
1088 		if (!folder_get_junk(folder)) {
1089 			item = folder_item_new(JUNK_DIR, JUNK_DIR);
1090 			item->stype = F_JUNK;
1091 			folder_item_append(rootitem, item);
1092 			folder_set_junk(folder, item);
1093 		}
1094 	}
1095 }
1096 
folder_unref_account_func(GNode * node,gpointer data)1097 static gboolean folder_unref_account_func(GNode *node, gpointer data)
1098 {
1099 	FolderItem *item = node->data;
1100 	PrefsAccount *account = data;
1101 
1102 	if (item->account == account)
1103 		item->account = NULL;
1104 
1105 	return FALSE;
1106 }
1107 
folder_unref_account_all(PrefsAccount * account)1108 void folder_unref_account_all(PrefsAccount *account)
1109 {
1110 	Folder *folder;
1111 	GList *list;
1112 
1113 	if (!account) return;
1114 
1115 	for (list = folder_list; list != NULL; list = list->next) {
1116 		folder = list->data;
1117 		if (folder->account == account)
1118 			folder->account = NULL;
1119 		g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1120 				folder_unref_account_func, account);
1121 	}
1122 }
1123 
1124 #undef CREATE_FOLDER_IF_NOT_EXIST
1125 
folder_get_path(Folder * folder)1126 gchar *folder_get_path(Folder *folder)
1127 {
1128 	gchar *path;
1129 
1130 	g_return_val_if_fail(folder != NULL, NULL);
1131 
1132 	if (FOLDER_TYPE(folder) == F_MH) {
1133 		path = g_filename_from_utf8(LOCAL_FOLDER(folder)->rootpath,
1134 					    -1, NULL, NULL, NULL);
1135 		if (!path) {
1136 			g_warning("folder_get_path: failed to convert character set\n");
1137 			path = g_strdup(LOCAL_FOLDER(folder)->rootpath);
1138 		}
1139 		if (!g_path_is_absolute(path)) {
1140 			gchar *path_;
1141 
1142 			path_ = g_strconcat(get_mail_base_dir(),
1143 					    G_DIR_SEPARATOR_S, path, NULL);
1144 			g_free(path);
1145 			path = path_;
1146 		}
1147 	} else if (FOLDER_TYPE(folder) == F_IMAP) {
1148 		gchar *server;
1149 		gchar *uid;
1150 
1151 		g_return_val_if_fail(folder->account != NULL, NULL);
1152 		server = uriencode_for_filename(folder->account->recv_server);
1153 		uid = uriencode_for_filename(folder->account->userid);
1154 		path = g_strconcat(get_imap_cache_dir(),
1155 				   G_DIR_SEPARATOR_S, server,
1156 				   G_DIR_SEPARATOR_S, uid, NULL);
1157 		g_free(uid);
1158 		g_free(server);
1159 	} else if (FOLDER_TYPE(folder) == F_NEWS) {
1160 		gchar *server;
1161 
1162 		g_return_val_if_fail(folder->account != NULL, NULL);
1163 		server = uriencode_for_filename(folder->account->nntp_server);
1164 		path = g_strconcat(get_news_cache_dir(),
1165 				   G_DIR_SEPARATOR_S, server, NULL);
1166 		g_free(server);
1167 	} else
1168 		path = NULL;
1169 
1170 	return path;
1171 }
1172 
folder_item_get_path(FolderItem * item)1173 gchar *folder_item_get_path(FolderItem *item)
1174 {
1175 	gchar *folder_path;
1176 	gchar *item_path = NULL, *path;
1177 
1178 	g_return_val_if_fail(item != NULL, NULL);
1179 
1180 	folder_path = folder_get_path(item->folder);
1181 	g_return_val_if_fail(folder_path != NULL, NULL);
1182 
1183 	if (item->path) {
1184 		item_path = g_filename_from_utf8(item->path, -1,
1185 						 NULL, NULL, NULL);
1186 		if (!item_path) {
1187 			g_warning("folder_item_get_path: failed to convert character set\n");
1188 			item_path = g_strdup(item->path);
1189 		}
1190 #ifdef G_OS_WIN32
1191 		subst_char(item_path, '/', G_DIR_SEPARATOR);
1192 #endif
1193 	}
1194 
1195 	if (item_path)
1196 		path = g_strconcat(folder_path, G_DIR_SEPARATOR_S, item_path,
1197 				   NULL);
1198 	else
1199 		path = g_strdup(folder_path);
1200 
1201 	g_free(item_path);
1202 	g_free(folder_path);
1203 	return path;
1204 }
1205 
folder_item_scan(FolderItem * item)1206 gint folder_item_scan(FolderItem *item)
1207 {
1208 	Folder *folder;
1209 
1210 	g_return_val_if_fail(item != NULL, -1);
1211 
1212 	folder = item->folder;
1213 	return folder->klass->scan(folder, item);
1214 }
1215 
folder_item_scan_foreach_func(gpointer key,gpointer val,gpointer data)1216 static void folder_item_scan_foreach_func(gpointer key, gpointer val,
1217 					  gpointer data)
1218 {
1219 	folder_item_scan(FOLDER_ITEM(key));
1220 }
1221 
folder_item_scan_foreach(GHashTable * table)1222 void folder_item_scan_foreach(GHashTable *table)
1223 {
1224 	g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
1225 }
1226 
folder_item_get_msg_list(FolderItem * item,gboolean use_cache)1227 GSList *folder_item_get_msg_list(FolderItem *item, gboolean use_cache)
1228 {
1229 	Folder *folder;
1230 
1231 	g_return_val_if_fail(item != NULL, NULL);
1232 
1233 	folder = item->folder;
1234 
1235 	if (item->stype == F_VIRTUAL)
1236 		return virtual_get_class()->get_msg_list(folder, item,
1237 							 use_cache);
1238 
1239 	return folder->klass->get_msg_list(folder, item, use_cache);
1240 }
1241 
folder_item_get_uncached_msg_list(FolderItem * item)1242 GSList *folder_item_get_uncached_msg_list(FolderItem *item)
1243 {
1244 	Folder *folder;
1245 
1246 	g_return_val_if_fail(item != NULL, NULL);
1247 	g_return_val_if_fail(item->folder->klass->get_uncached_msg_list != NULL,
1248 			     NULL);
1249 
1250 	folder = item->folder;
1251 
1252 	if (item->stype == F_VIRTUAL)
1253 		return NULL;
1254 
1255 	return folder->klass->get_uncached_msg_list(folder, item);
1256 }
1257 
folder_item_fetch_msg(FolderItem * item,gint num)1258 gchar *folder_item_fetch_msg(FolderItem *item, gint num)
1259 {
1260 	Folder *folder;
1261 
1262 	g_return_val_if_fail(item != NULL, NULL);
1263 
1264 	folder = item->folder;
1265 
1266 	return folder->klass->fetch_msg(folder, item, num);
1267 }
1268 
folder_item_fetch_all_msg(FolderItem * item)1269 gint folder_item_fetch_all_msg(FolderItem *item)
1270 {
1271 	Folder *folder;
1272 	GSList *mlist;
1273 	GSList *cur;
1274 	gint num = 0;
1275 	gint ret = 0;
1276 
1277 	g_return_val_if_fail(item != NULL, -1);
1278 
1279 	debug_print("fetching all messages in %s ...\n", item->path);
1280 
1281 	folder = item->folder;
1282 
1283 	if (folder->ui_func)
1284 		folder->ui_func(folder, item, folder->ui_func_data ?
1285 				folder->ui_func_data : GINT_TO_POINTER(num));
1286 
1287 	mlist = folder_item_get_msg_list(item, TRUE);
1288 
1289 	for (cur = mlist; cur != NULL; cur = cur->next) {
1290 		MsgInfo *msginfo = (MsgInfo *)cur->data;
1291 		gchar *msg;
1292 
1293 		num++;
1294 		if (folder->ui_func)
1295 			folder->ui_func(folder, item,
1296 					folder->ui_func_data ?
1297 					folder->ui_func_data :
1298 					GINT_TO_POINTER(num));
1299 
1300 		msg = folder_item_fetch_msg(item, msginfo->msgnum);
1301 		if (!msg) {
1302 			g_warning("Can't fetch message %d. Aborting.\n",
1303 				  msginfo->msgnum);
1304 			ret = -1;
1305 			break;
1306 		}
1307 		g_free(msg);
1308 	}
1309 
1310 	procmsg_msg_list_free(mlist);
1311 
1312 	return ret;
1313 }
1314 
folder_item_get_msginfo(FolderItem * item,gint num)1315 MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num)
1316 {
1317 	Folder *folder;
1318 
1319 	g_return_val_if_fail(item != NULL, NULL);
1320 
1321 	folder = item->folder;
1322 
1323 	return folder->klass->get_msginfo(folder, item, num);
1324 }
1325 
folder_item_add_msg(FolderItem * dest,const gchar * file,MsgFlags * flags,gboolean remove_source)1326 gint folder_item_add_msg(FolderItem *dest, const gchar *file, MsgFlags *flags,
1327 			 gboolean remove_source)
1328 {
1329 	Folder *folder;
1330 
1331 	g_return_val_if_fail(dest != NULL, -1);
1332 	g_return_val_if_fail(file != NULL, -1);
1333 	g_return_val_if_fail(dest->folder->klass->add_msg != NULL, -1);
1334 
1335 	folder = dest->folder;
1336 
1337 	return folder->klass->add_msg(folder, dest, file, flags, remove_source);
1338 }
1339 
folder_item_add_msgs(FolderItem * dest,GSList * file_list,gboolean remove_source,gint * first)1340 gint folder_item_add_msgs(FolderItem *dest, GSList *file_list,
1341 			  gboolean remove_source, gint *first)
1342 {
1343 	Folder *folder;
1344 
1345 	g_return_val_if_fail(dest != NULL, -1);
1346 	g_return_val_if_fail(file_list != NULL, -1);
1347 	g_return_val_if_fail(dest->folder->klass->add_msgs != NULL, -1);
1348 
1349 	folder = dest->folder;
1350 
1351 	return folder->klass->add_msgs(folder, dest, file_list, remove_source,
1352 				       first);
1353 }
1354 
folder_item_add_msg_msginfo(FolderItem * dest,MsgInfo * msginfo,gboolean remove_source)1355 gint folder_item_add_msg_msginfo(FolderItem *dest, MsgInfo *msginfo,
1356 				 gboolean remove_source)
1357 {
1358 	Folder *folder;
1359 
1360 	g_return_val_if_fail(dest != NULL, -1);
1361 	g_return_val_if_fail(msginfo != NULL, -1);
1362 	g_return_val_if_fail(msginfo->file_path != NULL, -1);
1363 	g_return_val_if_fail(dest->folder->klass->add_msg_msginfo != NULL, -1);
1364 
1365 	folder = dest->folder;
1366 
1367 	return folder->klass->add_msg_msginfo(folder, dest, msginfo,
1368 					      remove_source);
1369 }
1370 
folder_item_add_msgs_msginfo(FolderItem * dest,GSList * msglist,gboolean remove_source,gint * first)1371 gint folder_item_add_msgs_msginfo(FolderItem *dest, GSList *msglist,
1372 				  gboolean remove_source, gint *first)
1373 {
1374 	Folder *folder;
1375 
1376 	g_return_val_if_fail(dest != NULL, -1);
1377 	g_return_val_if_fail(msglist != NULL, -1);
1378 	g_return_val_if_fail(dest->folder->klass->add_msgs_msginfo != NULL, -1);
1379 
1380 	folder = dest->folder;
1381 
1382 	return folder->klass->add_msgs_msginfo(folder, dest, msglist,
1383 					       remove_source, first);
1384 }
1385 
1386 #define IS_FROM_QUEUE(m, d) \
1387 	(m->folder && m->folder->stype == F_QUEUE && \
1388 	 MSG_IS_QUEUED(m->flags) && d->stype != F_QUEUE)
1389 
folder_item_move_msg(FolderItem * dest,MsgInfo * msginfo)1390 gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
1391 {
1392 	Folder *folder;
1393 
1394 	g_return_val_if_fail(dest != NULL, -1);
1395 	g_return_val_if_fail(msginfo != NULL, -1);
1396 	g_return_val_if_fail(dest->folder->klass->move_msg != NULL, -1);
1397 
1398 	folder = dest->folder;
1399 
1400 	if (IS_FROM_QUEUE(msginfo, dest)) {
1401 		GSList msglist;
1402 
1403 		msglist.data = msginfo;
1404 		msglist.next = NULL;
1405 		return procmsg_add_messages_from_queue(dest, &msglist, TRUE);
1406 	}
1407 
1408 	return folder->klass->move_msg(folder, dest, msginfo);
1409 }
1410 
folder_item_move_msgs(FolderItem * dest,GSList * msglist)1411 gint folder_item_move_msgs(FolderItem *dest, GSList *msglist)
1412 {
1413 	Folder *folder;
1414 	MsgInfo *msginfo;
1415 
1416 	g_return_val_if_fail(dest != NULL, -1);
1417 	g_return_val_if_fail(msglist != NULL, -1);
1418 	g_return_val_if_fail(dest->folder->klass->move_msgs != NULL, -1);
1419 
1420 	folder = dest->folder;
1421 
1422 	msginfo = (MsgInfo *)msglist->data;
1423 	if (IS_FROM_QUEUE(msginfo, dest))
1424 		return procmsg_add_messages_from_queue(dest, msglist, TRUE);
1425 
1426 	return folder->klass->move_msgs(folder, dest, msglist);
1427 }
1428 
folder_item_copy_msg(FolderItem * dest,MsgInfo * msginfo)1429 gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
1430 {
1431 	Folder *folder;
1432 
1433 	g_return_val_if_fail(dest != NULL, -1);
1434 	g_return_val_if_fail(msginfo != NULL, -1);
1435 	g_return_val_if_fail(dest->folder->klass->copy_msg != NULL, -1);
1436 
1437 	folder = dest->folder;
1438 
1439 	if (IS_FROM_QUEUE(msginfo, dest)) {
1440 		GSList msglist;
1441 
1442 		msglist.data = msginfo;
1443 		msglist.next = NULL;
1444 		return procmsg_add_messages_from_queue(dest, &msglist, FALSE);
1445 	}
1446 
1447 	return folder->klass->copy_msg(folder, dest, msginfo);
1448 }
1449 
folder_item_copy_msgs(FolderItem * dest,GSList * msglist)1450 gint folder_item_copy_msgs(FolderItem *dest, GSList *msglist)
1451 {
1452 	Folder *folder;
1453 	MsgInfo *msginfo;
1454 
1455 	g_return_val_if_fail(dest != NULL, -1);
1456 	g_return_val_if_fail(msglist != NULL, -1);
1457 	g_return_val_if_fail(dest->folder->klass->copy_msgs != NULL, -1);
1458 
1459 	folder = dest->folder;
1460 
1461 	msginfo = (MsgInfo *)msglist->data;
1462 	if (IS_FROM_QUEUE(msginfo, dest))
1463 		return procmsg_add_messages_from_queue(dest, msglist, FALSE);
1464 
1465 	return folder->klass->copy_msgs(folder, dest, msglist);
1466 }
1467 
1468 #undef IS_FROM_QUEUE
1469 
folder_item_remove_msg(FolderItem * item,MsgInfo * msginfo)1470 gint folder_item_remove_msg(FolderItem *item, MsgInfo *msginfo)
1471 {
1472 	Folder *folder;
1473 
1474 	g_return_val_if_fail(item != NULL, -1);
1475 	g_return_val_if_fail(item->folder->klass->remove_msg != NULL, -1);
1476 
1477 	folder = item->folder;
1478 
1479 	return folder->klass->remove_msg(folder, item, msginfo);
1480 }
1481 
folder_item_remove_msgs(FolderItem * item,GSList * msglist)1482 gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
1483 {
1484 	Folder *folder;
1485 	gint ret = 0;
1486 
1487 	g_return_val_if_fail(item != NULL, -1);
1488 
1489 	folder = item->folder;
1490 	if (folder->klass->remove_msgs) {
1491 		return folder->klass->remove_msgs(folder, item, msglist);
1492 	}
1493 
1494 	while (msglist != NULL) {
1495 		MsgInfo *msginfo = (MsgInfo *)msglist->data;
1496 
1497 		ret = folder_item_remove_msg(item, msginfo);
1498 		if (ret != 0) break;
1499 		msglist = msglist->next;
1500 	}
1501 
1502 	return ret;
1503 }
1504 
folder_item_remove_all_msg(FolderItem * item)1505 gint folder_item_remove_all_msg(FolderItem *item)
1506 {
1507 	Folder *folder;
1508 
1509 	g_return_val_if_fail(item != NULL, -1);
1510 	g_return_val_if_fail(item->folder->klass->remove_all_msg != NULL, -1);
1511 
1512 	folder = item->folder;
1513 
1514 	return folder->klass->remove_all_msg(folder, item);
1515 }
1516 
folder_item_is_msg_changed(FolderItem * item,MsgInfo * msginfo)1517 gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
1518 {
1519 	Folder *folder;
1520 
1521 	g_return_val_if_fail(item != NULL, FALSE);
1522 	g_return_val_if_fail(item->folder->klass->is_msg_changed != NULL,
1523 			     FALSE);
1524 
1525 	folder = item->folder;
1526 	return folder->klass->is_msg_changed(folder, item, msginfo);
1527 }
1528 
folder_item_close(FolderItem * item)1529 gint folder_item_close(FolderItem *item)
1530 {
1531 	Folder *folder;
1532 
1533 	g_return_val_if_fail(item != NULL, -1);
1534 
1535 	item->opened = FALSE;
1536 	folder = item->folder;
1537 	return folder->klass->close(folder, item);
1538 }
1539 
folder_item_get_cache_file(FolderItem * item)1540 gchar *folder_item_get_cache_file(FolderItem *item)
1541 {
1542 	gchar *path;
1543 	gchar *file;
1544 
1545 	g_return_val_if_fail(item != NULL, NULL);
1546 	g_return_val_if_fail(item->path != NULL, NULL);
1547 
1548 	path = folder_item_get_path(item);
1549 	g_return_val_if_fail(path != NULL, NULL);
1550 	if (!is_dir_exist(path))
1551 		make_dir_hier(path);
1552 	file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
1553 	g_free(path);
1554 
1555 	return file;
1556 }
1557 
folder_item_get_mark_file(FolderItem * item)1558 gchar *folder_item_get_mark_file(FolderItem *item)
1559 {
1560 	gchar *path;
1561 	gchar *file;
1562 
1563 	g_return_val_if_fail(item != NULL, NULL);
1564 	g_return_val_if_fail(item->path != NULL, NULL);
1565 
1566 	path = folder_item_get_path(item);
1567 	g_return_val_if_fail(path != NULL, NULL);
1568 	if (!is_dir_exist(path))
1569 		make_dir_hier(path);
1570 	file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
1571 	g_free(path);
1572 
1573 	return file;
1574 }
1575 
folder_build_tree(GNode * node,gpointer data)1576 static gboolean folder_build_tree(GNode *node, gpointer data)
1577 {
1578 	Folder *folder = FOLDER(data);
1579 	FolderItem *item;
1580 	XMLNode *xmlnode;
1581 	GList *list;
1582 	SpecialFolderItemType stype = F_NORMAL;
1583 	const gchar *name = NULL;
1584 	const gchar *path = NULL;
1585 	PrefsAccount *account = NULL;
1586 	gboolean no_sub = FALSE, no_select = FALSE, collapsed = FALSE,
1587 		 threaded = TRUE, ac_apply_sub = FALSE;
1588 	FolderSortKey sort_key = SORT_BY_NONE;
1589 	FolderSortType sort_type = SORT_ASCENDING;
1590 	gboolean qsearch_cond_type = 0;
1591 	gint new = 0, unread = 0, total = 0;
1592 	time_t mtime = 0;
1593 	gboolean use_auto_to_on_reply = FALSE;
1594 	gchar *auto_to = NULL, *auto_cc = NULL, *auto_bcc = NULL,
1595 	      *auto_replyto = NULL;
1596 	gboolean trim_summary_subject = FALSE, trim_compose_subject = FALSE;
1597 
1598 	g_return_val_if_fail(node->data != NULL, FALSE);
1599 	if (!node->parent) return FALSE;
1600 
1601 	xmlnode = node->data;
1602 	if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
1603 		g_warning("tag name != \"folderitem\"\n");
1604 		return FALSE;
1605 	}
1606 
1607 	list = xmlnode->tag->attr;
1608 	for (; list != NULL; list = list->next) {
1609 		XMLAttr *attr = list->data;
1610 
1611 		if (!attr || !attr->name || !attr->value) continue;
1612 		if (!strcmp(attr->name, "type")) {
1613 			if (!g_ascii_strcasecmp(attr->value, "normal"))
1614 				stype = F_NORMAL;
1615 			else if (!g_ascii_strcasecmp(attr->value, "inbox"))
1616 				stype = F_INBOX;
1617 			else if (!g_ascii_strcasecmp(attr->value, "outbox"))
1618 				stype = F_OUTBOX;
1619 			else if (!g_ascii_strcasecmp(attr->value, "draft"))
1620 				stype = F_DRAFT;
1621 			else if (!g_ascii_strcasecmp(attr->value, "queue"))
1622 				stype = F_QUEUE;
1623 			else if (!g_ascii_strcasecmp(attr->value, "trash"))
1624 				stype = F_TRASH;
1625 			else if (!g_ascii_strcasecmp(attr->value, "junk"))
1626 				stype = F_JUNK;
1627 			else if (!g_ascii_strcasecmp(attr->value, "virtual"))
1628 				stype = F_VIRTUAL;
1629 		} else if (!strcmp(attr->name, "name"))
1630 			name = attr->value;
1631 		else if (!strcmp(attr->name, "path")) {
1632 #ifdef G_OS_WIN32
1633 			subst_char(attr->value, G_DIR_SEPARATOR, '/');
1634 #endif
1635 			path = attr->value;
1636 		} else if (!strcmp(attr->name, "mtime"))
1637 			mtime = strtoll(attr->value, NULL, 10);
1638 		else if (!strcmp(attr->name, "new"))
1639 			new = atoi(attr->value);
1640 		else if (!strcmp(attr->name, "unread"))
1641 			unread = atoi(attr->value);
1642 		else if (!strcmp(attr->name, "total"))
1643 			total = atoi(attr->value);
1644 		else if (!strcmp(attr->name, "no_sub"))
1645 			no_sub = *attr->value == '1' ? TRUE : FALSE;
1646 		else if (!strcmp(attr->name, "no_select"))
1647 			no_select = *attr->value == '1' ? TRUE : FALSE;
1648 		else if (!strcmp(attr->name, "collapsed"))
1649 			collapsed = *attr->value == '1' ? TRUE : FALSE;
1650 		else if (!strcmp(attr->name, "threaded"))
1651 			threaded =  *attr->value == '1' ? TRUE : FALSE;
1652 		else if (!strcmp(attr->name, "sort_key")) {
1653 			if (!strcmp(attr->value, "none"))
1654 				sort_key = SORT_BY_NONE;
1655 			else if (!strcmp(attr->value, "number"))
1656 				sort_key = SORT_BY_NUMBER;
1657 			else if (!strcmp(attr->value, "size"))
1658 				sort_key = SORT_BY_SIZE;
1659 			else if (!strcmp(attr->value, "date"))
1660 				sort_key = SORT_BY_DATE;
1661 			else if (!strcmp(attr->value, "thread-date"))
1662 				sort_key = SORT_BY_TDATE;
1663 			else if (!strcmp(attr->value, "from"))
1664 				sort_key = SORT_BY_FROM;
1665 			else if (!strcmp(attr->value, "subject"))
1666 				sort_key = SORT_BY_SUBJECT;
1667 			else if (!strcmp(attr->value, "score"))
1668 				sort_key = SORT_BY_SCORE;
1669 			else if (!strcmp(attr->value, "label"))
1670 				sort_key = SORT_BY_LABEL;
1671 			else if (!strcmp(attr->value, "mark"))
1672 				sort_key = SORT_BY_MARK;
1673 			else if (!strcmp(attr->value, "unread"))
1674 				sort_key = SORT_BY_UNREAD;
1675 			else if (!strcmp(attr->value, "mime"))
1676 				sort_key = SORT_BY_MIME;
1677 			else if (!strcmp(attr->value, "to"))
1678 				sort_key = SORT_BY_TO;
1679 		} else if (!strcmp(attr->name, "sort_type")) {
1680 			if (!strcmp(attr->value, "ascending"))
1681 				sort_type = SORT_ASCENDING;
1682 			else
1683 				sort_type = SORT_DESCENDING;
1684 		} else if (!strcmp(attr->name, "qsearch_cond")) {
1685 			if (!strcmp(attr->value, "all"))
1686 				qsearch_cond_type = 0;
1687 			else if (!strcmp(attr->value, "unread"))
1688 				qsearch_cond_type = 1;
1689 			else if (!strcmp(attr->value, "mark"))
1690 				qsearch_cond_type = 2;
1691 			else if (!strcmp(attr->value, "clabel"))
1692 				qsearch_cond_type = 3;
1693 			else if (!strcmp(attr->value, "mime"))
1694 				qsearch_cond_type = 4;
1695 			else if (!strcmp(attr->value, "w1day"))
1696 				qsearch_cond_type = 5;
1697 			else if (!strcmp(attr->value, "last5"))
1698 				qsearch_cond_type = 6;
1699 			else if (!strcmp(attr->value, "last7"))
1700 				qsearch_cond_type = 7;
1701 			else if (!strcmp(attr->value, "in-addressbook"))
1702 				qsearch_cond_type = 8;
1703 			else if (!strcmp(attr->value, "last30"))
1704 				qsearch_cond_type = 9;
1705 		} else if (!strcmp(attr->name, "account_id")) {
1706 			account = account_find_from_id(atoi(attr->value));
1707 			if (!account) g_warning("account_id: %s not found\n",
1708 						attr->value);
1709 		} else if (!strcmp(attr->name, "account_apply_sub"))
1710 			ac_apply_sub = *attr->value == '1' ? TRUE : FALSE;
1711 		else if (!strcmp(attr->name, "to"))
1712 			auto_to = g_strdup(attr->value);
1713 		else if (!strcmp(attr->name, "use_auto_to_on_reply"))
1714 			use_auto_to_on_reply =
1715 				*attr->value == '1' ? TRUE : FALSE;
1716 		else if (!strcmp(attr->name, "cc"))
1717 			auto_cc = g_strdup(attr->value);
1718 		else if (!strcmp(attr->name, "bcc"))
1719 			auto_bcc = g_strdup(attr->value);
1720 		else if (!strcmp(attr->name, "replyto"))
1721 			auto_replyto = g_strdup(attr->value);
1722 		else if (!strcmp(attr->name, "trim_summary_subject")) {
1723 			trim_summary_subject =
1724 				*attr->value == '1' ? TRUE : FALSE;
1725 		} else if (!strcmp(attr->name, "trim_compose_subject")) {
1726 			trim_compose_subject =
1727 				*attr->value = '1' ? TRUE : FALSE;
1728 		}
1729 	}
1730 
1731 	item = folder_item_new(name, path);
1732 	item->stype = stype;
1733 	item->mtime = mtime;
1734 	item->new = new;
1735 	item->unread = unread;
1736 	item->total = total;
1737 	item->no_sub = no_sub;
1738 	item->no_select = no_select;
1739 	item->collapsed = collapsed;
1740 	item->threaded  = threaded;
1741 	item->sort_key  = sort_key;
1742 	item->sort_type = sort_type;
1743 	item->node = node;
1744 	item->parent = FOLDER_ITEM(node->parent->data);
1745 	item->folder = folder;
1746 	switch (stype) {
1747 	case F_INBOX:  folder->inbox  = item; break;
1748 	case F_OUTBOX: folder->outbox = item; break;
1749 	case F_DRAFT:  folder->draft  = item; break;
1750 	case F_QUEUE:  folder->queue  = item; break;
1751 	case F_TRASH:  folder->trash  = item; break;
1752 	case F_JUNK:   folder_set_junk(folder, item); break;
1753 	default:       break;
1754 	}
1755 	item->account = account;
1756 	item->ac_apply_sub = ac_apply_sub;
1757 	item->auto_to = auto_to;
1758 	item->use_auto_to_on_reply = use_auto_to_on_reply;
1759 	item->auto_cc = auto_cc;
1760 	item->auto_bcc = auto_bcc;
1761 	item->auto_replyto = auto_replyto;
1762 	item->trim_summary_subject = trim_summary_subject;
1763 	item->trim_compose_subject = trim_compose_subject;
1764 	item->qsearch_cond_type = qsearch_cond_type;
1765 	node->data = item;
1766 	xml_free_node(xmlnode);
1767 
1768 	return FALSE;
1769 }
1770 
folder_read_folder_func(GNode * node,gpointer data)1771 static gboolean folder_read_folder_func(GNode *node, gpointer data)
1772 {
1773 	Folder *folder;
1774 	FolderItem *item;
1775 	XMLNode *xmlnode;
1776 	GList *list;
1777 	FolderType type = F_UNKNOWN;
1778 	const gchar *name = NULL;
1779 	const gchar *path = NULL;
1780 	PrefsAccount *account = NULL;
1781 	gboolean collapsed = FALSE, threaded = TRUE, ac_apply_sub = FALSE;
1782 
1783 	if (g_node_depth(node) != 2) return FALSE;
1784 	g_return_val_if_fail(node->data != NULL, FALSE);
1785 
1786 	xmlnode = node->data;
1787 	if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
1788 		g_warning("tag name != \"folder\"\n");
1789 		return TRUE;
1790 	}
1791 	g_node_unlink(node);
1792 	list = xmlnode->tag->attr;
1793 	for (; list != NULL; list = list->next) {
1794 		XMLAttr *attr = list->data;
1795 
1796 		if (!attr || !attr->name || !attr->value) continue;
1797 		if (!strcmp(attr->name, "type")) {
1798 			if (!g_ascii_strcasecmp(attr->value, "mh"))
1799 				type = F_MH;
1800 			else if (!g_ascii_strcasecmp(attr->value, "mbox"))
1801 				type = F_MBOX;
1802 			else if (!g_ascii_strcasecmp(attr->value, "maildir"))
1803 				type = F_MAILDIR;
1804 			else if (!g_ascii_strcasecmp(attr->value, "imap"))
1805 				type = F_IMAP;
1806 			else if (!g_ascii_strcasecmp(attr->value, "news"))
1807 				type = F_NEWS;
1808 		} else if (!strcmp(attr->name, "name"))
1809 			name = attr->value;
1810 		else if (!strcmp(attr->name, "path"))
1811 			path = attr->value;
1812 		else if (!strcmp(attr->name, "collapsed"))
1813 			collapsed = *attr->value == '1' ? TRUE : FALSE;
1814 		else if (!strcmp(attr->name, "threaded"))
1815 			threaded = *attr->value == '1' ? TRUE : FALSE;
1816 		else if (!strcmp(attr->name, "account_id")) {
1817 			account = account_find_from_id(atoi(attr->value));
1818 			if (!account) g_warning("account_id: %s not found\n",
1819 						attr->value);
1820 		} else if (!strcmp(attr->name, "account_apply_sub"))
1821 			ac_apply_sub = *attr->value == '1' ? TRUE : FALSE;
1822 	}
1823 
1824 	folder = folder_new(type, name, path);
1825 	g_return_val_if_fail(folder != NULL, FALSE);
1826 	if (account && FOLDER_IS_REMOTE(folder)) {
1827 		folder->account = account;
1828 		account->folder = REMOTE_FOLDER(folder);
1829 	}
1830 	if (account && FOLDER_IS_LOCAL(folder))
1831 		ac_apply_sub = TRUE;
1832 	item = FOLDER_ITEM(folder->node->data);
1833 	node->data = item;
1834 	item->node = node;
1835 	g_node_destroy(folder->node);
1836 	folder->node = node;
1837 	folder_add(folder);
1838 	item->collapsed = collapsed;
1839 	item->threaded  = threaded;
1840 	item->account   = account;
1841 	item->ac_apply_sub = ac_apply_sub;
1842 
1843 	g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1844 			folder_build_tree, folder);
1845 
1846 	return FALSE;
1847 }
1848 
folder_get_list_path(void)1849 static gchar *folder_get_list_path(void)
1850 {
1851 	static gchar *filename = NULL;
1852 
1853 	if (!filename)
1854 		filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1855 					FOLDER_LIST, NULL);
1856 
1857 	return filename;
1858 }
1859 
1860 #define PUT_ESCAPE_STR(fp, attr, str)			\
1861 {							\
1862 	fputs(" " attr "=\"", fp);			\
1863 	xml_file_put_escape_str(fp, str);		\
1864 	fputs("\"", fp);				\
1865 }
1866 
folder_write_list_recursive(GNode * node,gpointer data)1867 static void folder_write_list_recursive(GNode *node, gpointer data)
1868 {
1869 	FILE *fp = (FILE *)data;
1870 	FolderItem *item;
1871 	gint i, depth;
1872 	static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1873 					   "news", "unknown"};
1874 	static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1875 						 "draft", "queue", "trash",
1876 						 "junk", "virtual"};
1877 	static gchar *sort_key_str[] = {"none", "number", "size", "date",
1878 					"thread-date",
1879 					"from", "subject", "score", "label",
1880 					"mark", "unread", "mime", "to"};
1881 	static gchar *qsearch_cond_str[] = {"all", "unread", "mark", "clabel",
1882 					    "mime", "w1day", "last5", "last7",
1883 					    "in-addressbook", "last30"};
1884 
1885 	g_return_if_fail(node != NULL);
1886 	g_return_if_fail(fp != NULL);
1887 
1888 	item = FOLDER_ITEM(node->data);
1889 	g_return_if_fail(item != NULL);
1890 
1891 	depth = g_node_depth(node);
1892 	for (i = 0; i < depth; i++)
1893 		fputs("    ", fp);
1894 	if (depth == 1) {
1895 		Folder *folder = item->folder;
1896 
1897 		fprintf(fp, "<folder type=\"%s\"",
1898 			folder_type_str[FOLDER_TYPE(folder)]);
1899 		if (folder->name)
1900 			PUT_ESCAPE_STR(fp, "name", folder->name);
1901 		if (FOLDER_TYPE(folder) == F_MH)
1902 			PUT_ESCAPE_STR(fp, "path",
1903 				       LOCAL_FOLDER(folder)->rootpath);
1904 		if (item->collapsed && node->children)
1905 			fputs(" collapsed=\"1\"", fp);
1906 		if (folder->account)
1907 			fprintf(fp, " account_id=\"%d\"",
1908 				folder->account->account_id);
1909 		else if (item->account)
1910 			fprintf(fp, " account_id=\"%d\"",
1911 				item->account->account_id);
1912 		if (item->ac_apply_sub)
1913 			fputs(" account_apply_sub=\"1\"", fp);
1914 	} else {
1915 		fprintf(fp, "<folderitem type=\"%s\"",
1916 			folder_item_stype_str[item->stype]);
1917 		if (item->name)
1918 			PUT_ESCAPE_STR(fp, "name", item->name);
1919 		if (item->path)
1920 			PUT_ESCAPE_STR(fp, "path", item->path);
1921 
1922 		if (item->no_sub)
1923 			fputs(" no_sub=\"1\"", fp);
1924 		if (item->no_select)
1925 			fputs(" no_select=\"1\"", fp);
1926 		if (item->collapsed && node->children)
1927 			fputs(" collapsed=\"1\"", fp);
1928 		if (item->threaded)
1929 			fputs(" threaded=\"1\"", fp);
1930 		else
1931 			fputs(" threaded=\"0\"", fp);
1932 
1933 		if (item->sort_key != SORT_BY_NONE) {
1934 			fprintf(fp, " sort_key=\"%s\"",
1935 				sort_key_str[item->sort_key]);
1936 			if (item->sort_type == SORT_ASCENDING)
1937 				fprintf(fp, " sort_type=\"ascending\"");
1938 			else
1939 				fprintf(fp, " sort_type=\"descending\"");
1940 		}
1941 		if (item->qsearch_cond_type > 0 &&
1942 		    item->qsearch_cond_type < 10) {
1943 			fprintf(fp, " qsearch_cond=\"%s\"",
1944 				qsearch_cond_str[item->qsearch_cond_type]);
1945 		}
1946 
1947 		fprintf(fp,
1948 			" mtime=\"%lld\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1949 			(gint64)item->mtime, item->new, item->unread, item->total);
1950 
1951 		if (item->account)
1952 			fprintf(fp, " account_id=\"%d\"",
1953 				item->account->account_id);
1954 		if (item->ac_apply_sub)
1955 			fputs(" account_apply_sub=\"1\"", fp);
1956 
1957 		if (item->auto_to)
1958 			PUT_ESCAPE_STR(fp, "to", item->auto_to);
1959 		if (item->use_auto_to_on_reply)
1960 			fputs(" use_auto_to_on_reply=\"1\"", fp);
1961 		if (item->auto_cc)
1962 			PUT_ESCAPE_STR(fp, "cc", item->auto_cc);
1963 		if (item->auto_bcc)
1964 			PUT_ESCAPE_STR(fp, "bcc", item->auto_bcc);
1965 		if (item->auto_replyto)
1966 			PUT_ESCAPE_STR(fp, "replyto", item->auto_replyto);
1967 
1968 		if (item->trim_summary_subject)
1969 			fputs(" trim_summary_subject=\"1\"", fp);
1970 		if (item->trim_compose_subject)
1971 			fputs(" trim_compose_subject=\"1\"", fp);
1972 	}
1973 
1974 	if (node->children) {
1975 		GNode *child;
1976 		fputs(">\n", fp);
1977 
1978 		child = node->children;
1979 		while (child) {
1980 			GNode *cur;
1981 
1982 			cur = child;
1983 			child = cur->next;
1984 			folder_write_list_recursive(cur, data);
1985 		}
1986 
1987 		for (i = 0; i < depth; i++)
1988 			fputs("    ", fp);
1989 		fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1990 	} else
1991 		fputs(" />\n", fp);
1992 }
1993 
1994 #undef PUT_ESCAPE_STR
1995