Statistics
| Revision:

root / src / folder.c @ 285

History | View | Annotate | Download (37.1 kB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2005 Hiroyuki Yamamoto
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 "utils.h"
38
#include "xml.h"
39
#include "codeconv.h"
40
#include "prefs.h"
41
#include "account.h"
42
#include "prefs_account.h"
43
44
static GList *folder_list = NULL;
45
46
static void folder_init                (Folder                *folder,
47
                                 const gchar        *name);
48
49
static gboolean folder_read_folder_func        (GNode                *node,
50
                                         gpointer         data);
51
static gchar *folder_get_list_path        (void);
52
static void folder_write_list_recursive        (GNode                *node,
53
                                         gpointer         data);
54
55
56
Folder *folder_new(FolderType type, const gchar *name, const gchar *path)
57
{
58
        Folder *folder = NULL;
59
60
        name = name ? name : path;
61
        switch (type) {
62
        case F_MH:
63
                folder = mh_get_class()->folder_new(name, path);
64
                break;
65
        case F_IMAP:
66
                folder = imap_get_class()->folder_new(name, path);
67
                break;
68
        case F_NEWS:
69
                folder = news_get_class()->folder_new(name, path);
70
                break;
71
        default:
72
                return NULL;
73
        }
74
75
        return folder;
76
}
77
78
static void folder_init(Folder *folder, const gchar *name)
79
{
80
        FolderItem *item;
81
82
        g_return_if_fail(folder != NULL);
83
84
        folder_set_name(folder, name);
85
        folder->account = NULL;
86
        folder->inbox = NULL;
87
        folder->outbox = NULL;
88
        folder->draft = NULL;
89
        folder->queue = NULL;
90
        folder->trash = NULL;
91
        folder->ui_func = NULL;
92
        folder->ui_func_data = NULL;
93
        item = folder_item_new(name, NULL);
94
        item->folder = folder;
95
        folder->node = item->node = g_node_new(item);
96
        folder->data = NULL;
97
}
98
99
void folder_local_folder_init(Folder *folder, const gchar *name,
100
                              const gchar *path)
101
{
102
        folder_init(folder, name);
103
        LOCAL_FOLDER(folder)->rootpath = g_strdup(path);
104
}
105
106
void folder_remote_folder_init(Folder *folder, const gchar *name,
107
                               const gchar *path)
108
{
109
        folder_init(folder, name);
110
        REMOTE_FOLDER(folder)->session = NULL;
111
}
112
113
void folder_destroy(Folder *folder)
114
{
115
        g_return_if_fail(folder != NULL);
116
        g_return_if_fail(folder->klass->destroy != NULL);
117
118
        folder->klass->destroy(folder);
119
120
        folder_list = g_list_remove(folder_list, folder);
121
122
        folder_tree_destroy(folder);
123
        g_free(folder->name);
124
        g_free(folder);
125
}
126
127
void folder_local_folder_destroy(LocalFolder *lfolder)
128
{
129
        g_return_if_fail(lfolder != NULL);
130
131
        g_free(lfolder->rootpath);
132
}
133
134
void folder_remote_folder_destroy(RemoteFolder *rfolder)
135
{
136
        g_return_if_fail(rfolder != NULL);
137
138
        if (rfolder->session)
139
                session_destroy(rfolder->session);
140
}
141
142
#if 0
143
Folder *mbox_folder_new(const gchar *name, const gchar *path)
144
{
145
        /* not yet implemented */
146
        return NULL;
147
}
148
149
Folder *maildir_folder_new(const gchar *name, const gchar *path)
150
{
151
        /* not yet implemented */
152
        return NULL;
153
}
154
#endif
155
156
FolderItem *folder_item_new(const gchar *name, const gchar *path)
157
{
158
        FolderItem *item;
159
160
        item = g_new0(FolderItem, 1);
161
162
        item->stype = F_NORMAL;
163
        item->name = g_strdup(name);
164
        item->path = g_strdup(path);
165
        item->mtime = 0;
166
        item->new = 0;
167
        item->unread = 0;
168
        item->total = 0;
169
        item->unmarked_num = 0;
170
        item->last_num = -1;
171
        item->no_sub = FALSE;
172
        item->no_select = FALSE;
173
        item->collapsed = FALSE;
174
        item->threaded = TRUE;
175
        item->opened = FALSE;
176
        item->updated = FALSE;
177
        item->cache_dirty = FALSE;
178
        item->node = NULL;
179
        item->parent = NULL;
180
        item->folder = NULL;
181
        item->account = NULL;
182
        item->ac_apply_sub = FALSE;
183
        item->auto_to = NULL;
184
        item->use_auto_to_on_reply = FALSE;
185
        item->auto_cc = NULL;
186
        item->auto_bcc = NULL;
187
        item->auto_replyto = NULL;
188
        item->mark_queue = NULL;
189
        item->data = NULL;
190
191
        return item;
192
}
193
194
void folder_item_append(FolderItem *parent, FolderItem *item)
195
{
196
        g_return_if_fail(parent != NULL);
197
        g_return_if_fail(parent->folder != NULL);
198
        g_return_if_fail(parent->node != NULL);
199
        g_return_if_fail(item != NULL);
200
201
        item->parent = parent;
202
        item->folder = parent->folder;
203
        item->node = g_node_append_data(parent->node, item);
204
}
205
206
static gboolean folder_item_remove_func(GNode *node, gpointer data)
207
{
208
        FolderItem *item = FOLDER_ITEM(node->data);
209
210
        folder_item_destroy(item);
211
        return FALSE;
212
}
213
214
void folder_item_remove(FolderItem *item)
215
{
216
        GNode *node;
217
218
        g_return_if_fail(item != NULL);
219
        g_return_if_fail(item->folder != NULL);
220
        g_return_if_fail(item->node != NULL);
221
222
        node = item->node;
223
224
        if (item->folder->node == node)
225
                item->folder->node = NULL;
226
227
        g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
228
                        folder_item_remove_func, NULL);
229
        g_node_destroy(node);
230
}
231
232
void folder_item_remove_children(FolderItem *item)
233
{
234
        GNode *node, *next;
235
236
        g_return_if_fail(item != NULL);
237
        g_return_if_fail(item->folder != NULL);
238
        g_return_if_fail(item->node != NULL);
239
240
        node = item->node->children;
241
        while (node != NULL) {
242
                next = node->next;
243
                folder_item_remove(FOLDER_ITEM(node->data));
244
                node = next;
245
        }
246
}
247
248
void folder_item_destroy(FolderItem *item)
249
{
250
        Folder *folder;
251
252
        g_return_if_fail(item != NULL);
253
254
        folder = item->folder;
255
        if (folder) {
256
                if (folder->inbox == item)
257
                        folder->inbox = NULL;
258
                else if (folder->outbox == item)
259
                        folder->outbox = NULL;
260
                else if (folder->draft == item)
261
                        folder->draft = NULL;
262
                else if (folder->queue == item)
263
                        folder->queue = NULL;
264
                else if (folder->trash == item)
265
                        folder->trash = NULL;
266
        }
267
268
        g_free(item->name);
269
        g_free(item->path);
270
        g_free(item->auto_to);
271
        g_free(item->auto_cc);
272
        g_free(item->auto_bcc);
273
        g_free(item->auto_replyto);
274
        g_free(item);
275
}
276
277
gint folder_item_compare(FolderItem *item_a, FolderItem *item_b)
278
{
279
        gint ret;
280
        gchar *str_a, *str_b;
281
282
        if (!item_a || !item_b)
283
                return 0;
284
        if (!item_a->parent || !item_b->parent)
285
                return 0;
286
        if (!item_a->name || !item_b->name)
287
                return 0;
288
289
        /* if both a and b are special folders, sort them according to
290
         * their types (which is in-order). Note that this assumes that
291
         * there are no multiple folders of a special type. */
292
        if (item_a->stype != F_NORMAL && item_b->stype != F_NORMAL)
293
                return item_a->stype - item_b->stype;
294
295
         /* if b is normal folder, and a is not, b is smaller (ends up
296
          * lower in the list) */
297
        if (item_a->stype != F_NORMAL && item_b->stype == F_NORMAL)
298
                return item_b->stype - item_a->stype;
299
300
        /* if b is special folder, and a is not, b is larger (ends up
301
         * higher in the list) */
302
        if (item_a->stype == F_NORMAL && item_b->stype != F_NORMAL)
303
                return item_b->stype - item_a->stype;
304
305
        /* otherwise just compare the folder names */
306
        str_a = g_utf8_casefold(item_a->name, -1);
307
        str_b = g_utf8_casefold(item_b->name, -1);
308
        ret = g_utf8_collate(str_a, str_b);
309
        g_free(str_b);
310
        g_free(str_a);
311
312
        return ret;
313
}
314
315
void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
316
{
317
        g_return_if_fail(folder != NULL);
318
319
        folder->ui_func = func;
320
        folder->ui_func_data = data;
321
}
322
323
void folder_set_name(Folder *folder, const gchar *name)
324
{
325
        g_return_if_fail(folder != NULL);
326
327
        g_free(folder->name);
328
        folder->name = name ? g_strdup(name) : NULL;
329
        if (folder->node && folder->node->data) {
330
                FolderItem *item = (FolderItem *)folder->node->data;
331
332
                g_free(item->name);
333
                item->name = name ? g_strdup(name) : NULL;
334
        }
335
}
336
337
void folder_tree_destroy(Folder *folder)
338
{
339
        g_return_if_fail(folder != NULL);
340
341
        if (folder->node)
342
                folder_item_remove(FOLDER_ITEM(folder->node->data));
343
}
344
345
void folder_add(Folder *folder)
346
{
347
        Folder *cur_folder;
348
        GList *cur;
349
        gint i;
350
351
        g_return_if_fail(folder != NULL);
352
353
        for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
354
                cur_folder = FOLDER(cur->data);
355
                if (FOLDER_TYPE(folder) == F_MH) {
356
                        if (FOLDER_TYPE(cur_folder) != F_MH) break;
357
                } else if (FOLDER_TYPE(folder) == F_IMAP) {
358
                        if (FOLDER_TYPE(cur_folder) != F_MH &&
359
                            FOLDER_TYPE(cur_folder) != F_IMAP) break;
360
                } else if (FOLDER_TYPE(folder) == F_NEWS) {
361
                        if (FOLDER_TYPE(cur_folder) != F_MH &&
362
                            FOLDER_TYPE(cur_folder) != F_IMAP &&
363
                            FOLDER_TYPE(cur_folder) != F_NEWS) break;
364
                }
365
        }
366
367
        folder_list = g_list_insert(folder_list, folder, i);
368
}
369
370
GList *folder_get_list(void)
371
{
372
        return folder_list;
373
}
374
375
gint folder_read_list(void)
376
{
377
        GNode *node;
378
        XMLNode *xmlnode;
379
        gchar *path;
380
381
        path = folder_get_list_path();
382
        if (!is_file_exist(path)) return -1;
383
        node = xml_parse_file(path);
384
        if (!node) return -1;
385
386
        xmlnode = node->data;
387
        if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
388
                g_warning("wrong folder list\n");
389
                xml_free_tree(node);
390
                return -1;
391
        }
392
393
        g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
394
                        folder_read_folder_func, NULL);
395
396
        xml_free_tree(node);
397
        if (folder_list)
398
                return 0;
399
        else
400
                return -1;
401
}
402
403
void folder_write_list(void)
404
{
405
        GList *list;
406
        Folder *folder;
407
        gchar *path;
408
        PrefFile *pfile;
409
410
        path = folder_get_list_path();
411
        if ((pfile = prefs_file_open(path)) == NULL) return;
412
413
        fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
414
                CS_INTERNAL);
415
        fputs("\n<folderlist>\n", pfile->fp);
416
417
        for (list = folder_list; list != NULL; list = list->next) {
418
                folder = list->data;
419
                folder_write_list_recursive(folder->node, pfile->fp);
420
        }
421
422
        fputs("</folderlist>\n", pfile->fp);
423
424
        if (prefs_file_close(pfile) < 0)
425
                g_warning("failed to write folder list.\n");
426
}
427
428
struct TotalMsgStatus
429
{
430
        guint new;
431
        guint unread;
432
        guint total;
433
        GString *str;
434
};
435
436
static gboolean folder_get_status_full_all_func(GNode *node, gpointer data)
437
{
438
        FolderItem *item;
439
        struct TotalMsgStatus *status = (struct TotalMsgStatus *)data;
440
        gchar *id;
441
442
        g_return_val_if_fail(node->data != NULL, FALSE);
443
444
        item = FOLDER_ITEM(node->data);
445
446
        if (!item->path) return FALSE;
447
448
        status->new += item->new;
449
        status->unread += item->unread;
450
        status->total += item->total;
451
452
        if (status->str) {
453
                id = folder_item_get_identifier(item);
454
                g_string_sprintfa(status->str, "%5d %5d %5d %s\n",
455
                                  item->new, item->unread,
456
                                  item->total, id);
457
                g_free(id);
458
        }
459
460
        return FALSE;
461
}
462
463
static void folder_get_status_full_all(GString *str, guint *new, guint *unread,
464
                                       guint *total)
465
{
466
        GList *list;
467
        Folder *folder;
468
        struct TotalMsgStatus status;
469
470
        status.new = status.unread = status.total = 0;
471
        status.str = str;
472
473
        debug_print("Counting total number of messages...\n");
474
475
        for (list = folder_list; list != NULL; list = list->next) {
476
                folder = FOLDER(list->data);
477
                if (folder->node)
478
                        g_node_traverse(folder->node, G_PRE_ORDER,
479
                                        G_TRAVERSE_ALL, -1,
480
                                        folder_get_status_full_all_func,
481
                                        &status);
482
        }
483
484
        *new = status.new;
485
        *unread = status.unread;
486
        *total = status.total;
487
}
488
489
gchar *folder_get_status(GPtrArray *folders, gboolean full)
490
{
491
        guint new, unread, total;
492
        GString *str;
493
        gint i;
494
        gchar *ret;
495
496
        new = unread = total = 0;
497
498
        str = g_string_new(NULL);
499
500
        if (folders) {
501
                for (i = 0; i < folders->len; i++) {
502
                        FolderItem *item;
503
504
                        item = g_ptr_array_index(folders, i);
505
                        new += item->new;
506
                        unread += item->unread;
507
                        total += item->total;
508
509
                        if (full) {
510
                                gchar *id;
511
512
                                id = folder_item_get_identifier(item);
513
                                g_string_sprintfa(str, "%5d %5d %5d %s\n",
514
                                                  item->new, item->unread,
515
                                                  item->total, id);
516
                                g_free(id);
517
                        }
518
                }
519
        } else {
520
                folder_get_status_full_all(full ? str : NULL,
521
                                           &new, &unread, &total);
522
        }
523
524
        if (full)
525
                g_string_sprintfa(str, "%5d %5d %5d\n", new, unread, total);
526
        else
527
                g_string_sprintfa(str, "%d %d %d\n", new, unread, total);
528
529
        ret = str->str;
530
        g_string_free(str, FALSE);
531
532
        return ret;
533
}
534
535
Folder *folder_find_from_path(const gchar *path)
536
{
537
        GList *list;
538
        Folder *folder;
539
540
        for (list = folder_list; list != NULL; list = list->next) {
541
                folder = list->data;
542
                if (FOLDER_TYPE(folder) == F_MH &&
543
                    !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
544
                        return folder;
545
        }
546
547
        return NULL;
548
}
549
550
Folder *folder_find_from_name(const gchar *name, FolderType type)
551
{
552
        GList *list;
553
        Folder *folder;
554
555
        for (list = folder_list; list != NULL; list = list->next) {
556
                folder = list->data;
557
                if (FOLDER_TYPE(folder) == type &&
558
                    strcmp2(name, folder->name) == 0)
559
                        return folder;
560
        }
561
562
        return NULL;
563
}
564
565
static gboolean folder_item_find_func(GNode *node, gpointer data)
566
{
567
        FolderItem *item = node->data;
568
        gpointer *d = data;
569
        const gchar *path = d[0];
570
571
        if (path_cmp(path, item->path) != 0)
572
                return FALSE;
573
574
        d[1] = item;
575
576
        return TRUE;
577
}
578
579
FolderItem *folder_find_item_from_path(const gchar *path)
580
{
581
        Folder *folder;
582
        gpointer d[2];
583
584
        folder = folder_get_default_folder();
585
        g_return_val_if_fail(folder != NULL, NULL);
586
587
        d[0] = (gpointer)path;
588
        d[1] = NULL;
589
        g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
590
                        folder_item_find_func, d);
591
        return d[1];
592
}
593
594
FolderItem *folder_find_child_item_by_name(FolderItem *item, const gchar *name)
595
{
596
        GNode *node;
597
        FolderItem *child;
598
599
        for (node = item->node->children; node != NULL; node = node->next) {
600
                child = FOLDER_ITEM(node->data);
601
                if (strcmp2(g_basename(child->path), name) == 0)
602
                        return child;
603
        }
604
605
        return NULL;
606
}
607
608
static const struct {
609
        gchar *str;
610
        FolderType type;
611
} type_str_table[] = {
612
        {"#mh"     , F_MH},
613
        {"#mbox"   , F_MBOX},
614
        {"#maildir", F_MAILDIR},
615
        {"#imap"   , F_IMAP},
616
        {"#news"   , F_NEWS}
617
};
618
619
static gchar *folder_get_type_string(FolderType type)
620
{
621
        gint i;
622
623
        for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
624
             i++) {
625
                if (type_str_table[i].type == type)
626
                        return type_str_table[i].str;
627
        }
628
629
        return NULL;
630
}
631
632
static FolderType folder_get_type_from_string(const gchar *str)
633
{
634
        gint i;
635
636
        for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
637
             i++) {
638
                if (g_strcasecmp(type_str_table[i].str, str) == 0)
639
                        return type_str_table[i].type;
640
        }
641
642
        return F_UNKNOWN;
643
}
644
645
gchar *folder_get_identifier(Folder *folder)
646
{
647
        gchar *type_str;
648
649
        g_return_val_if_fail(folder != NULL, NULL);
650
651
        type_str = folder_get_type_string(FOLDER_TYPE(folder));
652
        return g_strconcat(type_str, "/", folder->name, NULL);
653
}
654
655
gchar *folder_item_get_identifier(FolderItem *item)
656
{
657
        gchar *id;
658
        gchar *folder_id;
659
660
        g_return_val_if_fail(item != NULL, NULL);
661
        g_return_val_if_fail(item->path != NULL, NULL);
662
663
        folder_id = folder_get_identifier(item->folder);
664
        id = g_strconcat(folder_id, "/", item->path, NULL);
665
        g_free(folder_id);
666
667
        return id;
668
}
669
670
FolderItem *folder_find_item_from_identifier(const gchar *identifier)
671
{
672
        Folder *folder;
673
        gpointer d[2];
674
        gchar *str;
675
        gchar *p;
676
        gchar *name;
677
        gchar *path;
678
        FolderType type;
679
680
        g_return_val_if_fail(identifier != NULL, NULL);
681
682
        if (*identifier != '#')
683
                return folder_find_item_from_path(identifier);
684
685
        Xstrdup_a(str, identifier, return NULL);
686
687
        p = strchr(str, '/');
688
        if (!p)
689
                return folder_find_item_from_path(identifier);
690
        *p = '\0';
691
        p++;
692
        type = folder_get_type_from_string(str);
693
        if (type == F_UNKNOWN)
694
                return folder_find_item_from_path(identifier);
695
696
        name = p;
697
        p = strchr(p, '/');
698
        if (!p)
699
                return folder_find_item_from_path(identifier);
700
        *p = '\0';
701
        p++;
702
703
        folder = folder_find_from_name(name, type);
704
        if (!folder)
705
                return folder_find_item_from_path(identifier);
706
707
        path = p;
708
709
        d[0] = (gpointer)path;
710
        d[1] = NULL;
711
        g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
712
                        folder_item_find_func, d);
713
        return d[1];
714
}
715
716
Folder *folder_get_default_folder(void)
717
{
718
        return folder_list ? FOLDER(folder_list->data) : NULL;
719
}
720
721
FolderItem *folder_get_default_inbox(void)
722
{
723
        Folder *folder;
724
725
        if (!folder_list) return NULL;
726
        folder = FOLDER(folder_list->data);
727
        g_return_val_if_fail(folder != NULL, NULL);
728
        return folder->inbox;
729
}
730
731
FolderItem *folder_get_default_outbox(void)
732
{
733
        Folder *folder;
734
735
        if (!folder_list) return NULL;
736
        folder = FOLDER(folder_list->data);
737
        g_return_val_if_fail(folder != NULL, NULL);
738
        return folder->outbox;
739
}
740
741
FolderItem *folder_get_default_draft(void)
742
{
743
        Folder *folder;
744
745
        if (!folder_list) return NULL;
746
        folder = FOLDER(folder_list->data);
747
        g_return_val_if_fail(folder != NULL, NULL);
748
        return folder->draft;
749
}
750
751
FolderItem *folder_get_default_queue(void)
752
{
753
        Folder *folder;
754
755
        if (!folder_list) return NULL;
756
        folder = FOLDER(folder_list->data);
757
        g_return_val_if_fail(folder != NULL, NULL);
758
        return folder->queue;
759
}
760
761
FolderItem *folder_get_default_trash(void)
762
{
763
        Folder *folder;
764
765
        if (!folder_list) return NULL;
766
        folder = FOLDER(folder_list->data);
767
        g_return_val_if_fail(folder != NULL, NULL);
768
        return folder->trash;
769
}
770
771
#define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type)        \
772
{                                                        \
773
        if (!folder->member) {                                \
774
                item = folder_item_new(dir, dir);        \
775
                item->stype = type;                        \
776
                folder_item_append(rootitem, item);        \
777
                folder->member = item;                        \
778
        }                                                \
779
}
780
781
void folder_set_missing_folders(void)
782
{
783
        Folder *folder;
784
        FolderItem *rootitem;
785
        FolderItem *item;
786
        GList *list;
787
788
        for (list = folder_list; list != NULL; list = list->next) {
789
                folder = list->data;
790
                if (FOLDER_TYPE(folder) != F_MH) continue;
791
                rootitem = FOLDER_ITEM(folder->node->data);
792
                g_return_if_fail(rootitem != NULL);
793
794
                if (folder->inbox && folder->outbox && folder->draft &&
795
                    folder->queue && folder->trash)
796
                        continue;
797
798
                if (folder->klass->create_tree(folder) < 0) {
799
                        g_warning("%s: can't create the folder tree.\n",
800
                                  LOCAL_FOLDER(folder)->rootpath);
801
                        continue;
802
                }
803
804
                CREATE_FOLDER_IF_NOT_EXIST(inbox,  INBOX_DIR,  F_INBOX);
805
                CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX);
806
                CREATE_FOLDER_IF_NOT_EXIST(draft,  DRAFT_DIR,  F_DRAFT);
807
                CREATE_FOLDER_IF_NOT_EXIST(queue,  QUEUE_DIR,  F_QUEUE);
808
                CREATE_FOLDER_IF_NOT_EXIST(trash,  TRASH_DIR,  F_TRASH);
809
        }
810
}
811
812
static gboolean folder_unref_account_func(GNode *node, gpointer data)
813
{
814
        FolderItem *item = node->data;
815
        PrefsAccount *account = data;
816
817
        if (item->account == account)
818
                item->account = NULL;
819
820
        return FALSE;
821
}
822
823
void folder_unref_account_all(PrefsAccount *account)
824
{
825
        Folder *folder;
826
        GList *list;
827
828
        if (!account) return;
829
830
        for (list = folder_list; list != NULL; list = list->next) {
831
                folder = list->data;
832
                if (folder->account == account)
833
                        folder->account = NULL;
834
                g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
835
                                folder_unref_account_func, account);
836
        }
837
}
838
839
#undef CREATE_FOLDER_IF_NOT_EXIST
840
841
gchar *folder_get_path(Folder *folder)
842
{
843
        gchar *path;
844
845
        g_return_val_if_fail(folder != NULL, NULL);
846
847
        if (FOLDER_TYPE(folder) == F_MH) {
848
                path = g_filename_from_utf8(LOCAL_FOLDER(folder)->rootpath,
849
                                            -1, NULL, NULL, NULL);
850
                if (!path) {
851
                        g_warning("folder_get_path: faild to convert character set\n");
852
                        path = g_strdup(LOCAL_FOLDER(folder)->rootpath);
853
                }
854
        } else if (FOLDER_TYPE(folder) == F_IMAP) {
855
                g_return_val_if_fail(folder->account != NULL, NULL);
856
                path = g_strconcat(get_imap_cache_dir(),
857
                                   G_DIR_SEPARATOR_S,
858
                                   folder->account->recv_server,
859
                                   G_DIR_SEPARATOR_S,
860
                                   folder->account->userid,
861
                                   NULL);
862
        } else if (FOLDER_TYPE(folder) == F_NEWS) {
863
                g_return_val_if_fail(folder->account != NULL, NULL);
864
                path = g_strconcat(get_news_cache_dir(),
865
                                   G_DIR_SEPARATOR_S,
866
                                   folder->account->nntp_server,
867
                                   NULL);
868
        } else
869
                path = NULL;
870
871
        return path;
872
}
873
874
gchar *folder_item_get_path(FolderItem *item)
875
{
876
        gchar *folder_path;
877
        gchar *item_path = NULL, *path;
878
879
        g_return_val_if_fail(item != NULL, NULL);
880
881
        folder_path = folder_get_path(item->folder);
882
        g_return_val_if_fail(folder_path != NULL, NULL);
883
884
        if (item->path) {
885
                item_path = g_filename_from_utf8(item->path, -1,
886
                                                 NULL, NULL, NULL);
887
                if (!item_path) {
888
                        g_warning("folder_item_get_path: faild to convert character set\n");
889
                        item_path = g_strdup(item->path);
890
                }
891
        }
892
893
        if (folder_path[0] == G_DIR_SEPARATOR) {
894
                if (item_path)
895
                        path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
896
                                           item_path, NULL);
897
                else
898
                        path = g_strdup(folder_path);
899
        } else {
900
                if (item_path)
901
                        path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
902
                                           folder_path, G_DIR_SEPARATOR_S,
903
                                           item_path, NULL);
904
                else
905
                        path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
906
                                           folder_path, NULL);
907
        }
908
909
        g_free(item_path);
910
        g_free(folder_path);
911
        return path;
912
}
913
914
gint folder_item_scan(FolderItem *item)
915
{
916
        Folder *folder;
917
918
        g_return_val_if_fail(item != NULL, -1);
919
920
        folder = item->folder;
921
        return folder->klass->scan(folder, item);
922
}
923
924
static void folder_item_scan_foreach_func(gpointer key, gpointer val,
925
                                          gpointer data)
926
{
927
        folder_item_scan(FOLDER_ITEM(key));
928
}
929
930
void folder_item_scan_foreach(GHashTable *table)
931
{
932
        g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
933
}
934
935
GSList *folder_item_get_msg_list(FolderItem *item, gboolean use_cache)
936
{
937
        Folder *folder;
938
939
        g_return_val_if_fail(item != NULL, NULL);
940
941
        folder = item->folder;
942
        return folder->klass->get_msg_list(folder, item, use_cache);
943
}
944
945
gchar *folder_item_fetch_msg(FolderItem *item, gint num)
946
{
947
        Folder *folder;
948
949
        g_return_val_if_fail(item != NULL, NULL);
950
951
        folder = item->folder;
952
953
        return folder->klass->fetch_msg(folder, item, num);
954
}
955
956
gint folder_item_fetch_all_msg(FolderItem *item)
957
{
958
        Folder *folder;
959
        GSList *mlist;
960
        GSList *cur;
961
        gint num = 0;
962
        gint ret = 0;
963
964
        g_return_val_if_fail(item != NULL, -1);
965
966
        debug_print("fetching all messages in %s ...\n", item->path);
967
968
        folder = item->folder;
969
970
        if (folder->ui_func)
971
                folder->ui_func(folder, item, folder->ui_func_data ?
972
                                folder->ui_func_data : GINT_TO_POINTER(num));
973
974
        mlist = folder_item_get_msg_list(item, TRUE);
975
976
        for (cur = mlist; cur != NULL; cur = cur->next) {
977
                MsgInfo *msginfo = (MsgInfo *)cur->data;
978
                gchar *msg;
979
980
                num++;
981
                if (folder->ui_func)
982
                        folder->ui_func(folder, item,
983
                                        folder->ui_func_data ?
984
                                        folder->ui_func_data :
985
                                        GINT_TO_POINTER(num));
986
987
                msg = folder_item_fetch_msg(item, msginfo->msgnum);
988
                if (!msg) {
989
                        g_warning("Can't fetch message %d. Aborting.\n",
990
                                  msginfo->msgnum);
991
                        ret = -1;
992
                        break;
993
                }
994
                g_free(msg);
995
        }
996
997
        procmsg_msg_list_free(mlist);
998
999
        return ret;
1000
}
1001
1002
MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num)
1003
{
1004
        Folder *folder;
1005
1006
        g_return_val_if_fail(item != NULL, NULL);
1007
1008
        folder = item->folder;
1009
1010
        return folder->klass->get_msginfo(folder, item, num);
1011
}
1012
1013
gint folder_item_add_msg(FolderItem *dest, const gchar *file, MsgFlags *flags,
1014
                         gboolean remove_source)
1015
{
1016
        Folder *folder;
1017
1018
        g_return_val_if_fail(dest != NULL, -1);
1019
        g_return_val_if_fail(file != NULL, -1);
1020
        g_return_val_if_fail(dest->folder->klass->add_msg != NULL, -1);
1021
1022
        folder = dest->folder;
1023
1024
        return folder->klass->add_msg(folder, dest, file, flags, remove_source);
1025
}
1026
1027
gint folder_item_add_msgs(FolderItem *dest, GSList *file_list,
1028
                          gboolean remove_source, gint *first)
1029
{
1030
        Folder *folder;
1031
1032
        g_return_val_if_fail(dest != NULL, -1);
1033
        g_return_val_if_fail(file_list != NULL, -1);
1034
        g_return_val_if_fail(dest->folder->klass->add_msgs != NULL, -1);
1035
1036
        folder = dest->folder;
1037
1038
        return folder->klass->add_msgs(folder, dest, file_list, remove_source,
1039
                                       first);
1040
}
1041
1042
gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
1043
{
1044
        Folder *folder;
1045
1046
        g_return_val_if_fail(dest != NULL, -1);
1047
        g_return_val_if_fail(msginfo != NULL, -1);
1048
        g_return_val_if_fail(dest->folder->klass->move_msg != NULL, -1);
1049
1050
        folder = dest->folder;
1051
1052
        return folder->klass->move_msg(folder, dest, msginfo);
1053
}
1054
1055
gint folder_item_move_msgs(FolderItem *dest, GSList *msglist)
1056
{
1057
        Folder *folder;
1058
1059
        g_return_val_if_fail(dest != NULL, -1);
1060
        g_return_val_if_fail(msglist != NULL, -1);
1061
        g_return_val_if_fail(dest->folder->klass->move_msgs != NULL, -1);
1062
1063
        folder = dest->folder;
1064
1065
        return folder->klass->move_msgs(folder, dest, msglist);
1066
}
1067
1068
gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
1069
{
1070
        Folder *folder;
1071
1072
        g_return_val_if_fail(dest != NULL, -1);
1073
        g_return_val_if_fail(msginfo != NULL, -1);
1074
        g_return_val_if_fail(dest->folder->klass->copy_msg != NULL, -1);
1075
1076
        folder = dest->folder;
1077
1078
        return folder->klass->copy_msg(folder, dest, msginfo);
1079
}
1080
1081
gint folder_item_copy_msgs(FolderItem *dest, GSList *msglist)
1082
{
1083
        Folder *folder;
1084
1085
        g_return_val_if_fail(dest != NULL, -1);
1086
        g_return_val_if_fail(msglist != NULL, -1);
1087
        g_return_val_if_fail(dest->folder->klass->copy_msgs != NULL, -1);
1088
1089
        folder = dest->folder;
1090
1091
        return folder->klass->copy_msgs(folder, dest, msglist);
1092
}
1093
1094
gint folder_item_remove_msg(FolderItem *item, MsgInfo *msginfo)
1095
{
1096
        Folder *folder;
1097
1098
        g_return_val_if_fail(item != NULL, -1);
1099
        g_return_val_if_fail(item->folder->klass->remove_msg != NULL, -1);
1100
1101
        folder = item->folder;
1102
1103
        return folder->klass->remove_msg(folder, item, msginfo);
1104
}
1105
1106
gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
1107
{
1108
        Folder *folder;
1109
        gint ret = 0;
1110
1111
        g_return_val_if_fail(item != NULL, -1);
1112
1113
        folder = item->folder;
1114
        if (folder->klass->remove_msgs) {
1115
                return folder->klass->remove_msgs(folder, item, msglist);
1116
        }
1117
1118
        while (msglist != NULL) {
1119
                MsgInfo *msginfo = (MsgInfo *)msglist->data;
1120
1121
                ret = folder_item_remove_msg(item, msginfo);
1122
                if (ret != 0) break;
1123
                msglist = msglist->next;
1124
        }
1125
1126
        return ret;
1127
}
1128
1129
gint folder_item_remove_all_msg(FolderItem *item)
1130
{
1131
        Folder *folder;
1132
1133
        g_return_val_if_fail(item != NULL, -1);
1134
        g_return_val_if_fail(item->folder->klass->remove_all_msg != NULL, -1);
1135
1136
        folder = item->folder;
1137
1138
        return folder->klass->remove_all_msg(folder, item);
1139
}
1140
1141
gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
1142
{
1143
        Folder *folder;
1144
1145
        g_return_val_if_fail(item != NULL, FALSE);
1146
        g_return_val_if_fail(item->folder->klass->is_msg_changed != NULL,
1147
                             FALSE);
1148
1149
        folder = item->folder;
1150
        return folder->klass->is_msg_changed(folder, item, msginfo);
1151
}
1152
1153
gint folder_item_close(FolderItem *item)
1154
{
1155
        Folder *folder;
1156
1157
        g_return_val_if_fail(item != NULL, -1);
1158
1159
        item->opened = FALSE;
1160
        folder = item->folder;
1161
        return folder->klass->close(folder, item);
1162
}
1163
1164
gchar *folder_item_get_cache_file(FolderItem *item)
1165
{
1166
        gchar *path;
1167
        gchar *file;
1168
1169
        g_return_val_if_fail(item != NULL, NULL);
1170
        g_return_val_if_fail(item->path != NULL, NULL);
1171
1172
        path = folder_item_get_path(item);
1173
        g_return_val_if_fail(path != NULL, NULL);
1174
        if (!is_dir_exist(path))
1175
                make_dir_hier(path);
1176
        file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
1177
        g_free(path);
1178
1179
        return file;
1180
}
1181
1182
gchar *folder_item_get_mark_file(FolderItem *item)
1183
{
1184
        gchar *path;
1185
        gchar *file;
1186
1187
        g_return_val_if_fail(item != NULL, NULL);
1188
        g_return_val_if_fail(item->path != NULL, NULL);
1189
1190
        path = folder_item_get_path(item);
1191
        g_return_val_if_fail(path != NULL, NULL);
1192
        if (!is_dir_exist(path))
1193
                make_dir_hier(path);
1194
        file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
1195
        g_free(path);
1196
1197
        return file;
1198
}
1199
1200
static gboolean folder_build_tree(GNode *node, gpointer data)
1201
{
1202
        Folder *folder = FOLDER(data);
1203
        FolderItem *item;
1204
        XMLNode *xmlnode;
1205
        GList *list;
1206
        SpecialFolderItemType stype = F_NORMAL;
1207
        const gchar *name = NULL;
1208
        const gchar *path = NULL;
1209
        PrefsAccount *account = NULL;
1210
        gboolean no_sub = FALSE, no_select = FALSE, collapsed = FALSE,
1211
                 threaded = TRUE, ac_apply_sub = FALSE;
1212
        FolderSortKey sort_key = SORT_BY_NONE;
1213
        FolderSortType sort_type = SORT_ASCENDING;
1214
        gint new = 0, unread = 0, total = 0;
1215
        time_t mtime = 0;
1216
        gboolean use_auto_to_on_reply = FALSE;
1217
        gchar *auto_to = NULL, *auto_cc = NULL, *auto_bcc = NULL,
1218
              *auto_replyto = NULL;
1219
        gboolean trim_summary_subject = FALSE, trim_compose_subject = FALSE;
1220
1221
        g_return_val_if_fail(node->data != NULL, FALSE);
1222
        if (!node->parent) return FALSE;
1223
1224
        xmlnode = node->data;
1225
        if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
1226
                g_warning("tag name != \"folderitem\"\n");
1227
                return FALSE;
1228
        }
1229
1230
        list = xmlnode->tag->attr;
1231
        for (; list != NULL; list = list->next) {
1232
                XMLAttr *attr = list->data;
1233
1234
                if (!attr || !attr->name || !attr->value) continue;
1235
                if (!strcmp(attr->name, "type")) {
1236
                        if (!strcasecmp(attr->value, "normal"))
1237
                                stype = F_NORMAL;
1238
                        else if (!strcasecmp(attr->value, "inbox"))
1239
                                stype = F_INBOX;
1240
                        else if (!strcasecmp(attr->value, "outbox"))
1241
                                stype = F_OUTBOX;
1242
                        else if (!strcasecmp(attr->value, "draft"))
1243
                                stype = F_DRAFT;
1244
                        else if (!strcasecmp(attr->value, "queue"))
1245
                                stype = F_QUEUE;
1246
                        else if (!strcasecmp(attr->value, "trash"))
1247
                                stype = F_TRASH;
1248
                } else if (!strcmp(attr->name, "name"))
1249
                        name = attr->value;
1250
                else if (!strcmp(attr->name, "path"))
1251
                        path = attr->value;
1252
                else if (!strcmp(attr->name, "mtime"))
1253
                        mtime = strtoul(attr->value, NULL, 10);
1254
                else if (!strcmp(attr->name, "new"))
1255
                        new = atoi(attr->value);
1256
                else if (!strcmp(attr->name, "unread"))
1257
                        unread = atoi(attr->value);
1258
                else if (!strcmp(attr->name, "total"))
1259
                        total = atoi(attr->value);
1260
                else if (!strcmp(attr->name, "no_sub"))
1261
                        no_sub = *attr->value == '1' ? TRUE : FALSE;
1262
                else if (!strcmp(attr->name, "no_select"))
1263
                        no_select = *attr->value == '1' ? TRUE : FALSE;
1264
                else if (!strcmp(attr->name, "collapsed"))
1265
                        collapsed = *attr->value == '1' ? TRUE : FALSE;
1266
                else if (!strcmp(attr->name, "threaded"))
1267
                        threaded =  *attr->value == '1' ? TRUE : FALSE;
1268
                else if (!strcmp(attr->name, "sort_key")) {
1269
                        if (!strcmp(attr->value, "none"))
1270
                                sort_key = SORT_BY_NONE;
1271
                        else if (!strcmp(attr->value, "number"))
1272
                                sort_key = SORT_BY_NUMBER;
1273
                        else if (!strcmp(attr->value, "size"))
1274
                                sort_key = SORT_BY_SIZE;
1275
                        else if (!strcmp(attr->value, "date"))
1276
                                sort_key = SORT_BY_DATE;
1277
                        else if (!strcmp(attr->value, "from"))
1278
                                sort_key = SORT_BY_FROM;
1279
                        else if (!strcmp(attr->value, "subject"))
1280
                                sort_key = SORT_BY_SUBJECT;
1281
                        else if (!strcmp(attr->value, "score"))
1282
                                sort_key = SORT_BY_SCORE;
1283
                        else if (!strcmp(attr->value, "label"))
1284
                                sort_key = SORT_BY_LABEL;
1285
                        else if (!strcmp(attr->value, "mark"))
1286
                                sort_key = SORT_BY_MARK;
1287
                        else if (!strcmp(attr->value, "unread"))
1288
                                sort_key = SORT_BY_UNREAD;
1289
                        else if (!strcmp(attr->value, "mime"))
1290
                                sort_key = SORT_BY_MIME;
1291
                        else if (!strcmp(attr->value, "to"))
1292
                                sort_key = SORT_BY_TO;
1293
                } else if (!strcmp(attr->name, "sort_type")) {
1294
                        if (!strcmp(attr->value, "ascending"))
1295
                                sort_type = SORT_ASCENDING;
1296
                        else
1297
                                sort_type = SORT_DESCENDING;
1298
                } else if (!strcmp(attr->name, "account_id")) {
1299
                        account = account_find_from_id(atoi(attr->value));
1300
                        if (!account) g_warning("account_id: %s not found\n",
1301
                                                attr->value);
1302
                } else if (!strcmp(attr->name, "account_apply_sub"))
1303
                        ac_apply_sub = *attr->value == '1' ? TRUE : FALSE;
1304
                else if (!strcmp(attr->name, "to"))
1305
                        auto_to = g_strdup(attr->value);
1306
                else if (!strcmp(attr->name, "use_auto_to_on_reply"))
1307
                        use_auto_to_on_reply =
1308
                                *attr->value == '1' ? TRUE : FALSE;
1309
                else if (!strcmp(attr->name, "cc"))
1310
                        auto_cc = g_strdup(attr->value);
1311
                else if (!strcmp(attr->name, "bcc"))
1312
                        auto_bcc = g_strdup(attr->value);
1313
                else if (!strcmp(attr->name, "replyto"))
1314
                        auto_replyto = g_strdup(attr->value);
1315
                else if (!strcmp(attr->name, "trim_summary_subject")) {
1316
                        trim_summary_subject =
1317
                                *attr->value == '1' ? TRUE : FALSE;
1318
                } else if (!strcmp(attr->name, "trim_compose_subject")) {
1319
                        trim_compose_subject =
1320
                                *attr->value = '1' ? TRUE : FALSE;
1321
                }
1322
        }
1323
1324
        item = folder_item_new(name, path);
1325
        item->stype = stype;
1326
        item->mtime = mtime;
1327
        item->new = new;
1328
        item->unread = unread;
1329
        item->total = total;
1330
        item->no_sub = no_sub;
1331
        item->no_select = no_select;
1332
        item->collapsed = collapsed;
1333
        item->threaded  = threaded;
1334
        item->sort_key  = sort_key;
1335
        item->sort_type = sort_type;
1336
        item->node = node;
1337
        item->parent = FOLDER_ITEM(node->parent->data);
1338
        item->folder = folder;
1339
        switch (stype) {
1340
        case F_INBOX:  folder->inbox  = item; break;
1341
        case F_OUTBOX: folder->outbox = item; break;
1342
        case F_DRAFT:  folder->draft  = item; break;
1343
        case F_QUEUE:  folder->queue  = item; break;
1344
        case F_TRASH:  folder->trash  = item; break;
1345
        default:       break;
1346
        }
1347
        item->account = account;
1348
        item->ac_apply_sub = ac_apply_sub;
1349
        item->auto_to = auto_to;
1350
        item->use_auto_to_on_reply = use_auto_to_on_reply;
1351
        item->auto_cc = auto_cc;
1352
        item->auto_bcc = auto_bcc;
1353
        item->auto_replyto = auto_replyto;
1354
        item->trim_summary_subject = trim_summary_subject;
1355
        item->trim_compose_subject = trim_compose_subject;
1356
        node->data = item;
1357
        xml_free_node(xmlnode);
1358
1359
        return FALSE;
1360
}
1361
1362
static gboolean folder_read_folder_func(GNode *node, gpointer data)
1363
{
1364
        Folder *folder;
1365
        FolderItem *item;
1366
        XMLNode *xmlnode;
1367
        GList *list;
1368
        FolderType type = F_UNKNOWN;
1369
        const gchar *name = NULL;
1370
        const gchar *path = NULL;
1371
        PrefsAccount *account = NULL;
1372
        gboolean collapsed = FALSE, threaded = TRUE, ac_apply_sub = FALSE;
1373
1374
        if (g_node_depth(node) != 2) return FALSE;
1375
        g_return_val_if_fail(node->data != NULL, FALSE);
1376
1377
        xmlnode = node->data;
1378
        if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
1379
                g_warning("tag name != \"folder\"\n");
1380
                return TRUE;
1381
        }
1382
        g_node_unlink(node);
1383
        list = xmlnode->tag->attr;
1384
        for (; list != NULL; list = list->next) {
1385
                XMLAttr *attr = list->data;
1386
1387
                if (!attr || !attr->name || !attr->value) continue;
1388
                if (!strcmp(attr->name, "type")) {
1389
                        if (!strcasecmp(attr->value, "mh"))
1390
                                type = F_MH;
1391
                        else if (!strcasecmp(attr->value, "mbox"))
1392
                                type = F_MBOX;
1393
                        else if (!strcasecmp(attr->value, "maildir"))
1394
                                type = F_MAILDIR;
1395
                        else if (!strcasecmp(attr->value, "imap"))
1396
                                type = F_IMAP;
1397
                        else if (!strcasecmp(attr->value, "news"))
1398
                                type = F_NEWS;
1399
                } else if (!strcmp(attr->name, "name"))
1400
                        name = attr->value;
1401
                else if (!strcmp(attr->name, "path"))
1402
                        path = attr->value;
1403
                else if (!strcmp(attr->name, "collapsed"))
1404
                        collapsed = *attr->value == '1' ? TRUE : FALSE;
1405
                else if (!strcmp(attr->name, "threaded"))
1406
                        threaded = *attr->value == '1' ? TRUE : FALSE;
1407
                else if (!strcmp(attr->name, "account_id")) {
1408
                        account = account_find_from_id(atoi(attr->value));
1409
                        if (!account) g_warning("account_id: %s not found\n",
1410
                                                attr->value);
1411
                } else if (!strcmp(attr->name, "account_apply_sub"))
1412
                        ac_apply_sub = *attr->value == '1' ? TRUE : FALSE;
1413
        }
1414
1415
        folder = folder_new(type, name, path);
1416
        g_return_val_if_fail(folder != NULL, FALSE);
1417
        folder->account = account;
1418
        if (account && (type == F_IMAP || type == F_NEWS))
1419
                account->folder = REMOTE_FOLDER(folder);
1420
        item = FOLDER_ITEM(folder->node->data);
1421
        node->data = item;
1422
        item->node = node;
1423
        g_node_destroy(folder->node);
1424
        folder->node = node;
1425
        folder_add(folder);
1426
        item->collapsed = collapsed;
1427
        item->threaded  = threaded;
1428
        item->account   = account;
1429
        item->ac_apply_sub = ac_apply_sub;
1430
1431
        g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1432
                        folder_build_tree, folder);
1433
1434
        return FALSE;
1435
}
1436
1437
static gchar *folder_get_list_path(void)
1438
{
1439
        static gchar *filename = NULL;
1440
1441
        if (!filename)
1442
                filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1443
                                        FOLDER_LIST, NULL);
1444
1445
        return filename;
1446
}
1447
1448
#define PUT_ESCAPE_STR(fp, attr, str)                        \
1449
{                                                        \
1450
        fputs(" " attr "=\"", fp);                        \
1451
        xml_file_put_escape_str(fp, str);                \
1452
        fputs("\"", fp);                                \
1453
}
1454
1455
static void folder_write_list_recursive(GNode *node, gpointer data)
1456
{
1457
        FILE *fp = (FILE *)data;
1458
        FolderItem *item;
1459
        gint i, depth;
1460
        static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1461
                                           "news", "unknown"};
1462
        static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1463
                                                 "draft", "queue", "trash"};
1464
        static gchar *sort_key_str[] = {"none", "number", "size", "date",
1465
                                        "from", "subject", "score", "label",
1466
                                        "mark", "unread", "mime", "to"};
1467
1468
        g_return_if_fail(node != NULL);
1469
        g_return_if_fail(fp != NULL);
1470
1471
        item = FOLDER_ITEM(node->data);
1472
        g_return_if_fail(item != NULL);
1473
1474
        depth = g_node_depth(node);
1475
        for (i = 0; i < depth; i++)
1476
                fputs("    ", fp);
1477
        if (depth == 1) {
1478
                Folder *folder = item->folder;
1479
1480
                fprintf(fp, "<folder type=\"%s\"",
1481
                        folder_type_str[FOLDER_TYPE(folder)]);
1482
                if (folder->name)
1483
                        PUT_ESCAPE_STR(fp, "name", folder->name);
1484
                if (FOLDER_TYPE(folder) == F_MH)
1485
                        PUT_ESCAPE_STR(fp, "path",
1486
                                       LOCAL_FOLDER(folder)->rootpath);
1487
                if (item->collapsed && node->children)
1488
                        fputs(" collapsed=\"1\"", fp);
1489
                if (folder->account)
1490
                        fprintf(fp, " account_id=\"%d\"",
1491
                                folder->account->account_id);
1492
                if (item->ac_apply_sub)
1493
                        fputs(" account_apply_sub=\"1\"", fp);
1494
        } else {
1495
                fprintf(fp, "<folderitem type=\"%s\"",
1496
                        folder_item_stype_str[item->stype]);
1497
                if (item->name)
1498
                        PUT_ESCAPE_STR(fp, "name", item->name);
1499
                if (item->path)
1500
                        PUT_ESCAPE_STR(fp, "path", item->path);
1501
1502
                if (item->no_sub)
1503
                        fputs(" no_sub=\"1\"", fp);
1504
                if (item->no_select)
1505
                        fputs(" no_select=\"1\"", fp);
1506
                if (item->collapsed && node->children)
1507
                        fputs(" collapsed=\"1\"", fp);
1508
                if (item->threaded)
1509
                        fputs(" threaded=\"1\"", fp);
1510
                else
1511
                        fputs(" threaded=\"0\"", fp);
1512
1513
                if (item->sort_key != SORT_BY_NONE) {
1514
                        fprintf(fp, " sort_key=\"%s\"",
1515
                                sort_key_str[item->sort_key]);
1516
                        if (item->sort_type == SORT_ASCENDING)
1517
                                fprintf(fp, " sort_type=\"ascending\"");
1518
                        else
1519
                                fprintf(fp, " sort_type=\"descending\"");
1520
                }
1521
1522
                fprintf(fp,
1523
                        " mtime=\"%lu\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1524
                        item->mtime, item->new, item->unread, item->total);
1525
1526
                if (item->account)
1527
                        fprintf(fp, " account_id=\"%d\"",
1528
                                item->account->account_id);
1529
                if (item->ac_apply_sub)
1530
                        fputs(" account_apply_sub=\"1\"", fp);
1531
1532
                if (item->auto_to)
1533
                        PUT_ESCAPE_STR(fp, "to", item->auto_to);
1534
                if (item->use_auto_to_on_reply)
1535
                        fputs(" use_auto_to_on_reply=\"1\"", fp);
1536
                if (item->auto_cc)
1537
                        PUT_ESCAPE_STR(fp, "cc", item->auto_cc);
1538
                if (item->auto_bcc)
1539
                        PUT_ESCAPE_STR(fp, "bcc", item->auto_bcc);
1540
                if (item->auto_replyto)
1541
                        PUT_ESCAPE_STR(fp, "replyto", item->auto_replyto);
1542
1543
                if (item->trim_summary_subject)
1544
                        fputs(" trim_summary_subject=\"1\"", fp);
1545
                if (item->trim_compose_subject)
1546
                        fputs(" trim_compose_subject=\"1\"", fp);
1547
        }
1548
1549
        if (node->children) {
1550
                GNode *child;
1551
                fputs(">\n", fp);
1552
1553
                child = node->children;
1554
                while (child) {
1555
                        GNode *cur;
1556
1557
                        cur = child;
1558
                        child = cur->next;
1559
                        folder_write_list_recursive(cur, data);
1560
                }
1561
1562
                for (i = 0; i < depth; i++)
1563
                        fputs("    ", fp);
1564
                fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1565
        } else
1566
                fputs(" />\n", fp);
1567
}
1568
1569
#undef PUT_ESCAPE_STR