Statistics
| Revision:

root / libsylph / folder.c @ 1900

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