Statistics
| Revision:

root / src / folder.c @ 333

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