Statistics
| Revision:

root / libsylph / folder.c @ 1491

History | View | Annotate | Download (38.5 kB)

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