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