Statistics
| Revision:

root / libsylph / folder.c @ 2164

History | View | Annotate | Download (41.4 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
#include "sylmain.h"
45

    
46
static GList *folder_list = NULL;
47

    
48
static void folder_init                (Folder                *folder,
49
                                 const gchar        *name);
50

    
51
static gboolean folder_read_folder_func        (GNode                *node,
52
                                         gpointer         data);
53
static gchar *folder_get_list_path        (void);
54
static void folder_write_list_recursive        (GNode                *node,
55
                                         gpointer         data);
56

    
57

    
58
Folder *folder_new(FolderType type, const gchar *name, const gchar *path)
59
{
60
        Folder *folder = NULL;
61

    
62
        name = name ? name : path;
63
        switch (type) {
64
        case F_MH:
65
                folder = mh_get_class()->folder_new(name, path);
66
                break;
67
        case F_IMAP:
68
                folder = imap_get_class()->folder_new(name, path);
69
                break;
70
        case F_NEWS:
71
                folder = news_get_class()->folder_new(name, path);
72
                break;
73
        default:
74
                return NULL;
75
        }
76

    
77
        return folder;
78
}
79

    
80
static void folder_init(Folder *folder, const gchar *name)
81
{
82
        FolderItem *item;
83

    
84
        g_return_if_fail(folder != NULL);
85

    
86
        folder_set_name(folder, name);
87
        folder->account = NULL;
88
        folder->inbox = NULL;
89
        folder->outbox = NULL;
90
        folder->draft = NULL;
91
        folder->queue = NULL;
92
        folder->trash = NULL;
93
        folder->ui_func = NULL;
94
        folder->ui_func_data = NULL;
95
        item = folder_item_new(name, NULL);
96
        item->folder = folder;
97
        folder->node = item->node = g_node_new(item);
98
        folder->data = NULL;
99
}
100

    
101
void folder_local_folder_init(Folder *folder, const gchar *name,
102
                              const gchar *path)
103
{
104
        folder_init(folder, name);
105
        LOCAL_FOLDER(folder)->rootpath = g_strdup(path);
106
}
107

    
108
void folder_remote_folder_init(Folder *folder, const gchar *name,
109
                               const gchar *path)
110
{
111
        folder_init(folder, name);
112
        REMOTE_FOLDER(folder)->session = NULL;
113
}
114

    
115
void folder_destroy(Folder *folder)
116
{
117
        g_return_if_fail(folder != NULL);
118
        g_return_if_fail(folder->klass->destroy != NULL);
119

    
120
        folder->klass->destroy(folder);
121

    
122
        folder_list = g_list_remove(folder_list, folder);
123

    
124
        folder_tree_destroy(folder);
125
        g_free(folder->name);
126
        g_free(folder);
127
}
128

    
129
void folder_local_folder_destroy(LocalFolder *lfolder)
130
{
131
        g_return_if_fail(lfolder != NULL);
132

    
133
        g_free(lfolder->rootpath);
134
}
135

    
136
void folder_remote_folder_destroy(RemoteFolder *rfolder)
137
{
138
        g_return_if_fail(rfolder != NULL);
139

    
140
        if (rfolder->session)
141
                session_destroy(rfolder->session);
142
}
143

    
144
gint folder_scan_tree(Folder *folder)
145
{
146
        g_return_val_if_fail(folder != NULL, -1);
147
        g_return_val_if_fail(folder->klass->scan_tree != NULL, -1);
148

    
149
        return folder->klass->scan_tree(folder);
150
}
151

    
152
gint folder_create_tree(Folder *folder)
153
{
154
        g_return_val_if_fail(folder != NULL, -1);
155
        g_return_val_if_fail(folder->klass->create_tree != NULL, -1);
156

    
157
        return folder->klass->create_tree(folder);
158
}
159

    
160
FolderItem *folder_item_new(const gchar *name, const gchar *path)
161
{
162
        FolderItem *item;
163

    
164
        item = g_new0(FolderItem, 1);
165

    
166
        item->stype = F_NORMAL;
167
        item->name = g_strdup(name);
168
        item->path = g_strdup(path);
169
        item->mtime = 0;
170
        item->new = 0;
171
        item->unread = 0;
172
        item->total = 0;
173
        item->unmarked_num = 0;
174
        item->last_num = -1;
175
        item->no_sub = FALSE;
176
        item->no_select = FALSE;
177
        item->collapsed = FALSE;
178
        item->threaded = TRUE;
179
        item->opened = FALSE;
180
        item->updated = FALSE;
181
        item->cache_dirty = FALSE;
182
        item->mark_dirty = FALSE;
183
        item->node = NULL;
184
        item->parent = NULL;
185
        item->folder = NULL;
186
        item->account = NULL;
187
        item->ac_apply_sub = FALSE;
188
        item->auto_to = NULL;
189
        item->use_auto_to_on_reply = FALSE;
190
        item->auto_cc = NULL;
191
        item->auto_bcc = NULL;
192
        item->auto_replyto = NULL;
193
        item->mark_queue = NULL;
194
        item->last_selected = 0;
195
        item->qsearch_cond_type = 0;
196
        item->data = NULL;
197

    
198
        return item;
199
}
200

    
201
void folder_item_append(FolderItem *parent, FolderItem *item)
202
{
203
        g_return_if_fail(parent != NULL);
204
        g_return_if_fail(parent->folder != NULL);
205
        g_return_if_fail(parent->node != NULL);
206
        g_return_if_fail(item != NULL);
207

    
208
        item->parent = parent;
209
        item->folder = parent->folder;
210
        item->node = g_node_append_data(parent->node, item);
211
}
212

    
213
FolderItem *folder_item_copy(FolderItem *item)
214
{
215
        FolderItem *new_item;
216

    
217
        new_item = g_new0(FolderItem, 1);
218

    
219
        new_item->stype = item->stype;
220
        new_item->name = g_strdup(item->name);
221
        new_item->path = g_strdup(item->path);
222
        new_item->mtime = item->mtime;
223
        new_item->new = item->new;
224
        new_item->unread = item->unread;
225
        new_item->total = item->total;
226
        new_item->unmarked_num = item->unmarked_num;
227
        new_item->last_num = item->last_num;
228
        new_item->no_sub = item->no_sub;
229
        new_item->no_select = item->no_select;
230
        new_item->collapsed = item->collapsed;
231
        new_item->threaded = item->threaded;
232
        new_item->opened = item->opened;
233
        new_item->updated = item->updated;
234
        new_item->cache_dirty = item->cache_dirty;
235
        new_item->mark_dirty = item->mark_dirty;
236
        new_item->node = item->node;
237
        new_item->parent = item->parent;
238
        new_item->folder = item->folder;
239
        new_item->account = item->account;
240
        new_item->ac_apply_sub = item->ac_apply_sub;
241
        new_item->auto_to = g_strdup(item->auto_to);
242
        new_item->use_auto_to_on_reply = item->use_auto_to_on_reply;
243
        new_item->auto_cc = g_strdup(item->auto_cc);
244
        new_item->auto_bcc = g_strdup(item->auto_bcc);
245
        new_item->auto_replyto = g_strdup(item->auto_replyto);
246
        new_item->mark_queue = item->mark_queue;
247
        new_item->last_selected = item->last_selected;
248
        new_item->qsearch_cond_type = item->qsearch_cond_type;
249
        new_item->data = item->data;
250

    
251
        return new_item;
252
}
253

    
254
static gboolean folder_item_remove_func(GNode *node, gpointer data)
255
{
256
        FolderItem *item = FOLDER_ITEM(node->data);
257

    
258
        folder_item_destroy(item);
259
        return FALSE;
260
}
261

    
262
void folder_item_remove(FolderItem *item)
263
{
264
        GNode *node;
265

    
266
        g_return_if_fail(item != NULL);
267
        g_return_if_fail(item->folder != NULL);
268
        g_return_if_fail(item->node != NULL);
269

    
270
        node = item->node;
271

    
272
        if (item->folder->node == node)
273
                item->folder->node = NULL;
274

    
275
        g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
276
                        folder_item_remove_func, NULL);
277
        g_node_destroy(node);
278
}
279

    
280
void folder_item_remove_children(FolderItem *item)
281
{
282
        GNode *node, *next;
283

    
284
        g_return_if_fail(item != NULL);
285
        g_return_if_fail(item->folder != NULL);
286
        g_return_if_fail(item->node != NULL);
287

    
288
        node = item->node->children;
289
        while (node != NULL) {
290
                next = node->next;
291
                folder_item_remove(FOLDER_ITEM(node->data));
292
                node = next;
293
        }
294
}
295

    
296
void folder_item_destroy(FolderItem *item)
297
{
298
        Folder *folder;
299

    
300
        g_return_if_fail(item != NULL);
301

    
302
        folder = item->folder;
303
        if (folder) {
304
                if (folder->inbox == item)
305
                        folder->inbox = NULL;
306
                else if (folder->outbox == item)
307
                        folder->outbox = NULL;
308
                else if (folder->draft == item)
309
                        folder->draft = NULL;
310
                else if (folder->queue == item)
311
                        folder->queue = NULL;
312
                else if (folder->trash == item)
313
                        folder->trash = NULL;
314
        }
315

    
316
        g_free(item->name);
317
        g_free(item->path);
318
        g_free(item->auto_to);
319
        g_free(item->auto_cc);
320
        g_free(item->auto_bcc);
321
        g_free(item->auto_replyto);
322
        g_free(item);
323
}
324

    
325
gint folder_item_compare(FolderItem *item_a, FolderItem *item_b)
326
{
327
        gint ret;
328
        gchar *str_a, *str_b;
329

    
330
        if (!item_a || !item_b)
331
                return 0;
332
        if (!item_a->parent || !item_b->parent)
333
                return 0;
334
        if (!item_a->name || !item_b->name)
335
                return 0;
336

    
337
        /* if both a and b are special folders, sort them according to
338
         * their types (which is in-order). Note that this assumes that
339
         * there are no multiple folders of a special type. As a special
340
         * case, two virtual folders are compared like normal ones. */
341
        if (item_a->stype != F_NORMAL && item_b->stype != F_NORMAL &&
342
            (item_a->stype != F_VIRTUAL || item_b->stype != F_VIRTUAL))
343
                return item_a->stype - item_b->stype;
344

    
345
         /* if b is normal folder, and a is not, b is smaller (ends up
346
          * lower in the list) */
347
        if (item_a->stype != F_NORMAL && item_b->stype == F_NORMAL)
348
                return item_b->stype - item_a->stype;
349

    
350
        /* if b is special folder, and a is not, b is larger (ends up
351
         * higher in the list) */
352
        if (item_a->stype == F_NORMAL && item_b->stype != F_NORMAL)
353
                return item_b->stype - item_a->stype;
354

    
355
        /* otherwise just compare the folder names */
356
        str_a = g_utf8_casefold(item_a->name, -1);
357
        str_b = g_utf8_casefold(item_b->name, -1);
358
        ret = g_utf8_collate(str_a, str_b);
359
        g_free(str_b);
360
        g_free(str_a);
361

    
362
        return ret;
363
}
364

    
365
void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
366
{
367
        g_return_if_fail(folder != NULL);
368

    
369
        folder->ui_func = func;
370
        folder->ui_func_data = data;
371
}
372

    
373
void folder_set_name(Folder *folder, const gchar *name)
374
{
375
        g_return_if_fail(folder != NULL);
376

    
377
        g_free(folder->name);
378
        folder->name = name ? g_strdup(name) : NULL;
379
        if (folder->node && folder->node->data) {
380
                FolderItem *item = (FolderItem *)folder->node->data;
381

    
382
                g_free(item->name);
383
                item->name = name ? g_strdup(name) : NULL;
384
        }
385
}
386

    
387
void folder_tree_destroy(Folder *folder)
388
{
389
        g_return_if_fail(folder != NULL);
390

    
391
        if (folder->node)
392
                folder_item_remove(FOLDER_ITEM(folder->node->data));
393
}
394

    
395
void folder_add(Folder *folder)
396
{
397
        Folder *cur_folder;
398
        GList *cur;
399
        gint i;
400

    
401
        g_return_if_fail(folder != NULL);
402

    
403
        for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
404
                cur_folder = FOLDER(cur->data);
405
                if (FOLDER_TYPE(folder) == F_MH) {
406
                        if (FOLDER_TYPE(cur_folder) != F_MH) break;
407
                } else if (FOLDER_TYPE(folder) == F_IMAP) {
408
                        if (FOLDER_TYPE(cur_folder) != F_MH &&
409
                            FOLDER_TYPE(cur_folder) != F_IMAP) break;
410
                } else if (FOLDER_TYPE(folder) == F_NEWS) {
411
                        if (FOLDER_TYPE(cur_folder) != F_MH &&
412
                            FOLDER_TYPE(cur_folder) != F_IMAP &&
413
                            FOLDER_TYPE(cur_folder) != F_NEWS) break;
414
                }
415
        }
416

    
417
        folder_list = g_list_insert(folder_list, folder, i);
418
}
419

    
420
GList *folder_get_list(void)
421
{
422
        return folder_list;
423
}
424

    
425
gint folder_read_list(void)
426
{
427
        GNode *node;
428
        XMLNode *xmlnode;
429
        gchar *path;
430

    
431
        path = folder_get_list_path();
432
        if (!is_file_exist(path)) return -1;
433
        node = xml_parse_file(path);
434
        if (!node) return -1;
435

    
436
        xmlnode = node->data;
437
        if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
438
                g_warning("wrong folder list\n");
439
                xml_free_tree(node);
440
                return -1;
441
        }
442

    
443
        g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
444
                        folder_read_folder_func, NULL);
445

    
446
        xml_free_tree(node);
447
        if (folder_list)
448
                return 0;
449
        else
450
                return -1;
451
}
452

    
453
void folder_write_list(void)
454
{
455
        GList *list;
456
        Folder *folder;
457
        gchar *path;
458
        PrefFile *pfile;
459

    
460
        path = folder_get_list_path();
461
        if ((pfile = prefs_file_open(path)) == NULL) return;
462

    
463
        fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
464
                CS_INTERNAL);
465
        fputs("\n<folderlist>\n", pfile->fp);
466

    
467
        for (list = folder_list; list != NULL; list = list->next) {
468
                folder = list->data;
469
                folder_write_list_recursive(folder->node, pfile->fp);
470
        }
471

    
472
        fputs("</folderlist>\n", pfile->fp);
473

    
474
        if (prefs_file_close(pfile) < 0)
475
                g_warning("failed to write folder list.\n");
476

    
477
        g_signal_emit_by_name(syl_app_get(), "folderlist-updated");
478
}
479

    
480
struct TotalMsgStatus
481
{
482
        guint new;
483
        guint unread;
484
        guint total;
485
        GString *str;
486
};
487

    
488
static gboolean folder_get_status_full_all_func(GNode *node, gpointer data)
489
{
490
        FolderItem *item;
491
        struct TotalMsgStatus *status = (struct TotalMsgStatus *)data;
492
        gchar *id;
493

    
494
        g_return_val_if_fail(node->data != NULL, FALSE);
495

    
496
        item = FOLDER_ITEM(node->data);
497

    
498
        if (!item->path) return FALSE;
499

    
500
        status->new += item->new;
501
        status->unread += item->unread;
502
        status->total += item->total;
503

    
504
        if (status->str) {
505
                id = folder_item_get_identifier(item);
506
                g_string_sprintfa(status->str, "%5d %5d %5d %s\n",
507
                                  item->new, item->unread,
508
                                  item->total, id);
509
                g_free(id);
510
        }
511

    
512
        return FALSE;
513
}
514

    
515
static void folder_get_status_full_all(GString *str, guint *new, guint *unread,
516
                                       guint *total)
517
{
518
        GList *list;
519
        Folder *folder;
520
        struct TotalMsgStatus status;
521

    
522
        status.new = status.unread = status.total = 0;
523
        status.str = str;
524

    
525
        debug_print("Counting total number of messages...\n");
526

    
527
        for (list = folder_list; list != NULL; list = list->next) {
528
                folder = FOLDER(list->data);
529
                if (folder->node)
530
                        g_node_traverse(folder->node, G_PRE_ORDER,
531
                                        G_TRAVERSE_ALL, -1,
532
                                        folder_get_status_full_all_func,
533
                                        &status);
534
        }
535

    
536
        *new = status.new;
537
        *unread = status.unread;
538
        *total = status.total;
539
}
540

    
541
gchar *folder_get_status(GPtrArray *folders, gboolean full)
542
{
543
        guint new, unread, total;
544
        GString *str;
545
        gint i;
546
        gchar *ret;
547

    
548
        new = unread = total = 0;
549

    
550
        str = g_string_new(NULL);
551

    
552
        if (folders) {
553
                for (i = 0; i < folders->len; i++) {
554
                        FolderItem *item;
555

    
556
                        item = g_ptr_array_index(folders, i);
557
                        new += item->new;
558
                        unread += item->unread;
559
                        total += item->total;
560

    
561
                        if (full) {
562
                                gchar *id;
563

    
564
                                id = folder_item_get_identifier(item);
565
                                g_string_sprintfa(str, "%5d %5d %5d %s\n",
566
                                                  item->new, item->unread,
567
                                                  item->total, id);
568
                                g_free(id);
569
                        }
570
                }
571
        } else {
572
                folder_get_status_full_all(full ? str : NULL,
573
                                           &new, &unread, &total);
574
        }
575

    
576
        if (full)
577
                g_string_sprintfa(str, "%5d %5d %5d\n", new, unread, total);
578
        else
579
                g_string_sprintfa(str, "%d %d %d\n", new, unread, total);
580

    
581
        ret = str->str;
582
        g_string_free(str, FALSE);
583

    
584
        return ret;
585
}
586

    
587
Folder *folder_find_from_path(const gchar *path)
588
{
589
        GList *list;
590
        Folder *folder;
591

    
592
        for (list = folder_list; list != NULL; list = list->next) {
593
                folder = list->data;
594
                if (FOLDER_TYPE(folder) == F_MH &&
595
                    !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
596
                        return folder;
597
        }
598

    
599
        return NULL;
600
}
601

    
602
Folder *folder_find_from_name(const gchar *name, FolderType type)
603
{
604
        GList *list;
605
        Folder *folder;
606

    
607
        for (list = folder_list; list != NULL; list = list->next) {
608
                folder = list->data;
609
                if (FOLDER_TYPE(folder) == type &&
610
                    strcmp2(name, folder->name) == 0)
611
                        return folder;
612
        }
613

    
614
        return NULL;
615
}
616

    
617
static gboolean folder_item_find_func(GNode *node, gpointer data)
618
{
619
        FolderItem *item = node->data;
620
        gpointer *d = data;
621
        const gchar *path = d[0];
622

    
623
        if (path_cmp(path, item->path) != 0)
624
                return FALSE;
625

    
626
        d[1] = item;
627

    
628
        return TRUE;
629
}
630

    
631
FolderItem *folder_find_item_from_path(const gchar *path)
632
{
633
        Folder *folder;
634
        gpointer d[2];
635

    
636
        folder = folder_get_default_folder();
637
        g_return_val_if_fail(folder != NULL, NULL);
638

    
639
        d[0] = (gpointer)path;
640
        d[1] = NULL;
641
        g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
642
                        folder_item_find_func, d);
643
        return d[1];
644
}
645

    
646
FolderItem *folder_find_child_item_by_name(FolderItem *item, const gchar *name)
647
{
648
        GNode *node;
649
        FolderItem *child;
650

    
651
        for (node = item->node->children; node != NULL; node = node->next) {
652
                child = FOLDER_ITEM(node->data);
653
                if (strcmp2(g_basename(child->path), name) == 0)
654
                        return child;
655
        }
656

    
657
        return NULL;
658
}
659

    
660
static const struct {
661
        gchar *str;
662
        FolderType type;
663
} type_str_table[] = {
664
        {"#mh"     , F_MH},
665
        {"#mbox"   , F_MBOX},
666
        {"#maildir", F_MAILDIR},
667
        {"#imap"   , F_IMAP},
668
        {"#news"   , F_NEWS}
669
};
670

    
671
static gchar *folder_get_type_string(FolderType type)
672
{
673
        gint i;
674

    
675
        for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
676
             i++) {
677
                if (type_str_table[i].type == type)
678
                        return type_str_table[i].str;
679
        }
680

    
681
        return NULL;
682
}
683

    
684
static FolderType folder_get_type_from_string(const gchar *str)
685
{
686
        gint i;
687

    
688
        for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
689
             i++) {
690
                if (g_ascii_strcasecmp(type_str_table[i].str, str) == 0)
691
                        return type_str_table[i].type;
692
        }
693

    
694
        return F_UNKNOWN;
695
}
696

    
697
gchar *folder_get_identifier(Folder *folder)
698
{
699
        gchar *type_str;
700

    
701
        g_return_val_if_fail(folder != NULL, NULL);
702

    
703
        type_str = folder_get_type_string(FOLDER_TYPE(folder));
704
        return g_strconcat(type_str, "/", folder->name, NULL);
705
}
706

    
707
gchar *folder_item_get_identifier(FolderItem *item)
708
{
709
        gchar *id;
710
        gchar *folder_id;
711

    
712
        g_return_val_if_fail(item != NULL, NULL);
713

    
714
        if (!item->path) {
715
                if (!item->parent)
716
                        return folder_get_identifier(item->folder);
717
                else
718
                        return NULL;
719
        }
720

    
721
        folder_id = folder_get_identifier(item->folder);
722
        id = g_strconcat(folder_id, "/", item->path, NULL);
723
        g_free(folder_id);
724

    
725
        return id;
726
}
727

    
728
FolderItem *folder_find_item_from_identifier(const gchar *identifier)
729
{
730
        Folder *folder;
731
        gpointer d[2];
732
        gchar *str;
733
        gchar *p;
734
        gchar *name;
735
        gchar *path;
736
        FolderType type;
737

    
738
        g_return_val_if_fail(identifier != NULL, NULL);
739

    
740
        if (*identifier != '#')
741
                return folder_find_item_from_path(identifier);
742

    
743
        Xstrdup_a(str, identifier, return NULL);
744

    
745
        p = strchr(str, '/');
746
        if (!p)
747
                return folder_find_item_from_path(identifier);
748
        *p = '\0';
749
        p++;
750
        type = folder_get_type_from_string(str);
751
        if (type == F_UNKNOWN)
752
                return folder_find_item_from_path(identifier);
753

    
754
        name = p;
755
        p = strchr(p, '/');
756
        if (p) {
757
                *p = '\0';
758
                p++;
759
        }
760

    
761
        folder = folder_find_from_name(name, type);
762
        if (!folder)
763
                return folder_find_item_from_path(identifier);
764

    
765
        if (!p)
766
                return FOLDER_ITEM(folder->node->data);
767

    
768
        path = p;
769

    
770
        d[0] = (gpointer)path;
771
        d[1] = NULL;
772
        g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
773
                        folder_item_find_func, d);
774
        return d[1];
775
}
776

    
777
FolderItem *folder_find_item_and_num_from_id(const gchar *identifier, gint *num)
778
{
779
        gchar *id;
780
        gchar *msg;
781
        FolderItem *item;
782

    
783
        g_return_val_if_fail(identifier != NULL, NULL);
784

    
785
        id = g_path_get_dirname(identifier);
786
        msg = g_path_get_basename(identifier);
787
        item = folder_find_item_from_identifier(id);
788
        *num = to_number(msg);
789
        g_free(msg);
790
        g_free(id);
791

    
792
        return item;
793
}
794

    
795
Folder *folder_get_default_folder(void)
796
{
797
        return folder_list ? FOLDER(folder_list->data) : NULL;
798
}
799

    
800
FolderItem *folder_get_default_inbox(void)
801
{
802
        Folder *folder;
803

    
804
        if (!folder_list) return NULL;
805
        folder = FOLDER(folder_list->data);
806
        g_return_val_if_fail(folder != NULL, NULL);
807
        return folder->inbox;
808
}
809

    
810
FolderItem *folder_get_default_outbox(void)
811
{
812
        Folder *folder;
813

    
814
        if (!folder_list) return NULL;
815
        folder = FOLDER(folder_list->data);
816
        g_return_val_if_fail(folder != NULL, NULL);
817
        return folder->outbox;
818
}
819

    
820
FolderItem *folder_get_default_draft(void)
821
{
822
        Folder *folder;
823

    
824
        if (!folder_list) return NULL;
825
        folder = FOLDER(folder_list->data);
826
        g_return_val_if_fail(folder != NULL, NULL);
827
        return folder->draft;
828
}
829

    
830
FolderItem *folder_get_default_queue(void)
831
{
832
        Folder *folder;
833

    
834
        if (!folder_list) return NULL;
835
        folder = FOLDER(folder_list->data);
836
        g_return_val_if_fail(folder != NULL, NULL);
837
        return folder->queue;
838
}
839

    
840
FolderItem *folder_get_default_trash(void)
841
{
842
        Folder *folder;
843

    
844
        if (!folder_list) return NULL;
845
        folder = FOLDER(folder_list->data);
846
        g_return_val_if_fail(folder != NULL, NULL);
847
        return folder->trash;
848
}
849

    
850
#define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type)        \
851
{                                                        \
852
        if (!folder->member) {                                \
853
                item = folder_item_new(dir, dir);        \
854
                item->stype = type;                        \
855
                folder_item_append(rootitem, item);        \
856
                folder->member = item;                        \
857
        }                                                \
858
}
859

    
860
void folder_set_missing_folders(void)
861
{
862
        Folder *folder;
863
        FolderItem *rootitem;
864
        FolderItem *item;
865
        GList *list;
866

    
867
        for (list = folder_list; list != NULL; list = list->next) {
868
                folder = list->data;
869
                if (FOLDER_TYPE(folder) != F_MH) continue;
870
                rootitem = FOLDER_ITEM(folder->node->data);
871
                g_return_if_fail(rootitem != NULL);
872

    
873
                if (folder->inbox && folder->outbox && folder->draft &&
874
                    folder->queue && folder->trash)
875
                        continue;
876

    
877
                if (folder_create_tree(folder) < 0) {
878
                        g_warning("%s: can't create the folder tree.\n",
879
                                  LOCAL_FOLDER(folder)->rootpath);
880
                        continue;
881
                }
882

    
883
                CREATE_FOLDER_IF_NOT_EXIST(inbox,  INBOX_DIR,  F_INBOX);
884
                CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX);
885
                CREATE_FOLDER_IF_NOT_EXIST(draft,  DRAFT_DIR,  F_DRAFT);
886
                CREATE_FOLDER_IF_NOT_EXIST(queue,  QUEUE_DIR,  F_QUEUE);
887
                CREATE_FOLDER_IF_NOT_EXIST(trash,  TRASH_DIR,  F_TRASH);
888
                /* CREATE_FOLDER_IF_NOT_EXIST(junk,   JUNK_DIR,  F_JUNK); */
889
        }
890
}
891

    
892
static gboolean folder_unref_account_func(GNode *node, gpointer data)
893
{
894
        FolderItem *item = node->data;
895
        PrefsAccount *account = data;
896

    
897
        if (item->account == account)
898
                item->account = NULL;
899

    
900
        return FALSE;
901
}
902

    
903
void folder_unref_account_all(PrefsAccount *account)
904
{
905
        Folder *folder;
906
        GList *list;
907

    
908
        if (!account) return;
909

    
910
        for (list = folder_list; list != NULL; list = list->next) {
911
                folder = list->data;
912
                if (folder->account == account)
913
                        folder->account = NULL;
914
                g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
915
                                folder_unref_account_func, account);
916
        }
917
}
918

    
919
#undef CREATE_FOLDER_IF_NOT_EXIST
920

    
921
gchar *folder_get_path(Folder *folder)
922
{
923
        gchar *path;
924

    
925
        g_return_val_if_fail(folder != NULL, NULL);
926

    
927
        if (FOLDER_TYPE(folder) == F_MH) {
928
                path = g_filename_from_utf8(LOCAL_FOLDER(folder)->rootpath,
929
                                            -1, NULL, NULL, NULL);
930
                if (!path) {
931
                        g_warning("folder_get_path: faild to convert character set\n");
932
                        path = g_strdup(LOCAL_FOLDER(folder)->rootpath);
933
                }
934
                if (!g_path_is_absolute(path)) {
935
                        gchar *path_;
936

    
937
                        path_ = g_strconcat(get_mail_base_dir(),
938
                                            G_DIR_SEPARATOR_S, path, NULL);
939
                        g_free(path);
940
                        path = path_;
941
                }
942
        } else if (FOLDER_TYPE(folder) == F_IMAP) {
943
                gchar *uid;
944

    
945
                g_return_val_if_fail(folder->account != NULL, NULL);
946
                uid = uriencode_for_filename(folder->account->userid);
947
                path = g_strconcat(get_imap_cache_dir(),
948
                                   G_DIR_SEPARATOR_S,
949
                                   folder->account->recv_server,
950
                                   G_DIR_SEPARATOR_S, uid, NULL);
951
                g_free(uid);
952
        } else if (FOLDER_TYPE(folder) == F_NEWS) {
953
                g_return_val_if_fail(folder->account != NULL, NULL);
954
                path = g_strconcat(get_news_cache_dir(),
955
                                   G_DIR_SEPARATOR_S,
956
                                   folder->account->nntp_server,
957
                                   NULL);
958
        } else
959
                path = NULL;
960

    
961
        return path;
962
}
963

    
964
gchar *folder_item_get_path(FolderItem *item)
965
{
966
        gchar *folder_path;
967
        gchar *item_path = NULL, *path;
968

    
969
        g_return_val_if_fail(item != NULL, NULL);
970

    
971
        folder_path = folder_get_path(item->folder);
972
        g_return_val_if_fail(folder_path != NULL, NULL);
973

    
974
        if (item->path) {
975
                item_path = g_filename_from_utf8(item->path, -1,
976
                                                 NULL, NULL, NULL);
977
                if (!item_path) {
978
                        g_warning("folder_item_get_path: faild to convert character set\n");
979
                        item_path = g_strdup(item->path);
980
                }
981
#ifdef G_OS_WIN32
982
                subst_char(item_path, '/', G_DIR_SEPARATOR);
983
#endif
984
        }
985

    
986
        if (item_path)
987
                path = g_strconcat(folder_path, G_DIR_SEPARATOR_S, item_path,
988
                                   NULL);
989
        else
990
                path = g_strdup(folder_path);
991

    
992
        g_free(item_path);
993
        g_free(folder_path);
994
        return path;
995
}
996

    
997
gint folder_item_scan(FolderItem *item)
998
{
999
        Folder *folder;
1000

    
1001
        g_return_val_if_fail(item != NULL, -1);
1002

    
1003
        folder = item->folder;
1004
        return folder->klass->scan(folder, item);
1005
}
1006

    
1007
static void folder_item_scan_foreach_func(gpointer key, gpointer val,
1008
                                          gpointer data)
1009
{
1010
        folder_item_scan(FOLDER_ITEM(key));
1011
}
1012

    
1013
void folder_item_scan_foreach(GHashTable *table)
1014
{
1015
        g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
1016
}
1017

    
1018
GSList *folder_item_get_msg_list(FolderItem *item, gboolean use_cache)
1019
{
1020
        Folder *folder;
1021

    
1022
        g_return_val_if_fail(item != NULL, NULL);
1023

    
1024
        folder = item->folder;
1025

    
1026
        if (item->stype == F_VIRTUAL)
1027
                return virtual_get_class()->get_msg_list(folder, item,
1028
                                                         use_cache);
1029

    
1030
        return folder->klass->get_msg_list(folder, item, use_cache);
1031
}
1032

    
1033
GSList *folder_item_get_uncached_msg_list(FolderItem *item)
1034
{
1035
        Folder *folder;
1036

    
1037
        g_return_val_if_fail(item != NULL, NULL);
1038
        g_return_val_if_fail(item->folder->klass->get_uncached_msg_list != NULL,
1039
                             NULL);
1040

    
1041
        folder = item->folder;
1042

    
1043
        if (item->stype == F_VIRTUAL)
1044
                return NULL;
1045

    
1046
        return folder->klass->get_uncached_msg_list(folder, item);
1047
}
1048

    
1049
gchar *folder_item_fetch_msg(FolderItem *item, gint num)
1050
{
1051
        Folder *folder;
1052

    
1053
        g_return_val_if_fail(item != NULL, NULL);
1054

    
1055
        folder = item->folder;
1056

    
1057
        return folder->klass->fetch_msg(folder, item, num);
1058
}
1059

    
1060
gint folder_item_fetch_all_msg(FolderItem *item)
1061
{
1062
        Folder *folder;
1063
        GSList *mlist;
1064
        GSList *cur;
1065
        gint num = 0;
1066
        gint ret = 0;
1067

    
1068
        g_return_val_if_fail(item != NULL, -1);
1069

    
1070
        debug_print("fetching all messages in %s ...\n", item->path);
1071

    
1072
        folder = item->folder;
1073

    
1074
        if (folder->ui_func)
1075
                folder->ui_func(folder, item, folder->ui_func_data ?
1076
                                folder->ui_func_data : GINT_TO_POINTER(num));
1077

    
1078
        mlist = folder_item_get_msg_list(item, TRUE);
1079

    
1080
        for (cur = mlist; cur != NULL; cur = cur->next) {
1081
                MsgInfo *msginfo = (MsgInfo *)cur->data;
1082
                gchar *msg;
1083

    
1084
                num++;
1085
                if (folder->ui_func)
1086
                        folder->ui_func(folder, item,
1087
                                        folder->ui_func_data ?
1088
                                        folder->ui_func_data :
1089
                                        GINT_TO_POINTER(num));
1090

    
1091
                msg = folder_item_fetch_msg(item, msginfo->msgnum);
1092
                if (!msg) {
1093
                        g_warning("Can't fetch message %d. Aborting.\n",
1094
                                  msginfo->msgnum);
1095
                        ret = -1;
1096
                        break;
1097
                }
1098
                g_free(msg);
1099
        }
1100

    
1101
        procmsg_msg_list_free(mlist);
1102

    
1103
        return ret;
1104
}
1105

    
1106
MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num)
1107
{
1108
        Folder *folder;
1109

    
1110
        g_return_val_if_fail(item != NULL, NULL);
1111

    
1112
        folder = item->folder;
1113

    
1114
        return folder->klass->get_msginfo(folder, item, num);
1115
}
1116

    
1117
gint folder_item_add_msg(FolderItem *dest, const gchar *file, MsgFlags *flags,
1118
                         gboolean remove_source)
1119
{
1120
        Folder *folder;
1121

    
1122
        g_return_val_if_fail(dest != NULL, -1);
1123
        g_return_val_if_fail(file != NULL, -1);
1124
        g_return_val_if_fail(dest->folder->klass->add_msg != NULL, -1);
1125

    
1126
        folder = dest->folder;
1127

    
1128
        return folder->klass->add_msg(folder, dest, file, flags, remove_source);
1129
}
1130

    
1131
gint folder_item_add_msgs(FolderItem *dest, GSList *file_list,
1132
                          gboolean remove_source, gint *first)
1133
{
1134
        Folder *folder;
1135

    
1136
        g_return_val_if_fail(dest != NULL, -1);
1137
        g_return_val_if_fail(file_list != NULL, -1);
1138
        g_return_val_if_fail(dest->folder->klass->add_msgs != NULL, -1);
1139

    
1140
        folder = dest->folder;
1141

    
1142
        return folder->klass->add_msgs(folder, dest, file_list, remove_source,
1143
                                       first);
1144
}
1145

    
1146
gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
1147
{
1148
        Folder *folder;
1149

    
1150
        g_return_val_if_fail(dest != NULL, -1);
1151
        g_return_val_if_fail(msginfo != NULL, -1);
1152
        g_return_val_if_fail(dest->folder->klass->move_msg != NULL, -1);
1153

    
1154
        folder = dest->folder;
1155

    
1156
        return folder->klass->move_msg(folder, dest, msginfo);
1157
}
1158

    
1159
gint folder_item_move_msgs(FolderItem *dest, GSList *msglist)
1160
{
1161
        Folder *folder;
1162

    
1163
        g_return_val_if_fail(dest != NULL, -1);
1164
        g_return_val_if_fail(msglist != NULL, -1);
1165
        g_return_val_if_fail(dest->folder->klass->move_msgs != NULL, -1);
1166

    
1167
        folder = dest->folder;
1168

    
1169
        return folder->klass->move_msgs(folder, dest, msglist);
1170
}
1171

    
1172
gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
1173
{
1174
        Folder *folder;
1175

    
1176
        g_return_val_if_fail(dest != NULL, -1);
1177
        g_return_val_if_fail(msginfo != NULL, -1);
1178
        g_return_val_if_fail(dest->folder->klass->copy_msg != NULL, -1);
1179

    
1180
        folder = dest->folder;
1181

    
1182
        return folder->klass->copy_msg(folder, dest, msginfo);
1183
}
1184

    
1185
gint folder_item_copy_msgs(FolderItem *dest, GSList *msglist)
1186
{
1187
        Folder *folder;
1188

    
1189
        g_return_val_if_fail(dest != NULL, -1);
1190
        g_return_val_if_fail(msglist != NULL, -1);
1191
        g_return_val_if_fail(dest->folder->klass->copy_msgs != NULL, -1);
1192

    
1193
        folder = dest->folder;
1194

    
1195
        return folder->klass->copy_msgs(folder, dest, msglist);
1196
}
1197

    
1198
gint folder_item_remove_msg(FolderItem *item, MsgInfo *msginfo)
1199
{
1200
        Folder *folder;
1201

    
1202
        g_return_val_if_fail(item != NULL, -1);
1203
        g_return_val_if_fail(item->folder->klass->remove_msg != NULL, -1);
1204

    
1205
        folder = item->folder;
1206

    
1207
        return folder->klass->remove_msg(folder, item, msginfo);
1208
}
1209

    
1210
gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
1211
{
1212
        Folder *folder;
1213
        gint ret = 0;
1214

    
1215
        g_return_val_if_fail(item != NULL, -1);
1216

    
1217
        folder = item->folder;
1218
        if (folder->klass->remove_msgs) {
1219
                return folder->klass->remove_msgs(folder, item, msglist);
1220
        }
1221

    
1222
        while (msglist != NULL) {
1223
                MsgInfo *msginfo = (MsgInfo *)msglist->data;
1224

    
1225
                ret = folder_item_remove_msg(item, msginfo);
1226
                if (ret != 0) break;
1227
                msglist = msglist->next;
1228
        }
1229

    
1230
        return ret;
1231
}
1232

    
1233
gint folder_item_remove_all_msg(FolderItem *item)
1234
{
1235
        Folder *folder;
1236

    
1237
        g_return_val_if_fail(item != NULL, -1);
1238
        g_return_val_if_fail(item->folder->klass->remove_all_msg != NULL, -1);
1239

    
1240
        folder = item->folder;
1241

    
1242
        return folder->klass->remove_all_msg(folder, item);
1243
}
1244

    
1245
gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
1246
{
1247
        Folder *folder;
1248

    
1249
        g_return_val_if_fail(item != NULL, FALSE);
1250
        g_return_val_if_fail(item->folder->klass->is_msg_changed != NULL,
1251
                             FALSE);
1252

    
1253
        folder = item->folder;
1254
        return folder->klass->is_msg_changed(folder, item, msginfo);
1255
}
1256

    
1257
gint folder_item_close(FolderItem *item)
1258
{
1259
        Folder *folder;
1260

    
1261
        g_return_val_if_fail(item != NULL, -1);
1262

    
1263
        item->opened = FALSE;
1264
        folder = item->folder;
1265
        return folder->klass->close(folder, item);
1266
}
1267

    
1268
gchar *folder_item_get_cache_file(FolderItem *item)
1269
{
1270
        gchar *path;
1271
        gchar *file;
1272

    
1273
        g_return_val_if_fail(item != NULL, NULL);
1274
        g_return_val_if_fail(item->path != NULL, NULL);
1275

    
1276
        path = folder_item_get_path(item);
1277
        g_return_val_if_fail(path != NULL, NULL);
1278
        if (!is_dir_exist(path))
1279
                make_dir_hier(path);
1280
        file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
1281
        g_free(path);
1282

    
1283
        return file;
1284
}
1285

    
1286
gchar *folder_item_get_mark_file(FolderItem *item)
1287
{
1288
        gchar *path;
1289
        gchar *file;
1290

    
1291
        g_return_val_if_fail(item != NULL, NULL);
1292
        g_return_val_if_fail(item->path != NULL, NULL);
1293

    
1294
        path = folder_item_get_path(item);
1295
        g_return_val_if_fail(path != NULL, NULL);
1296
        if (!is_dir_exist(path))
1297
                make_dir_hier(path);
1298
        file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
1299
        g_free(path);
1300

    
1301
        return file;
1302
}
1303

    
1304
static gboolean folder_build_tree(GNode *node, gpointer data)
1305
{
1306
        Folder *folder = FOLDER(data);
1307
        FolderItem *item;
1308
        XMLNode *xmlnode;
1309
        GList *list;
1310
        SpecialFolderItemType stype = F_NORMAL;
1311
        const gchar *name = NULL;
1312
        const gchar *path = NULL;
1313
        PrefsAccount *account = NULL;
1314
        gboolean no_sub = FALSE, no_select = FALSE, collapsed = FALSE,
1315
                 threaded = TRUE, ac_apply_sub = FALSE;
1316
        FolderSortKey sort_key = SORT_BY_NONE;
1317
        FolderSortType sort_type = SORT_ASCENDING;
1318
        gboolean qsearch_cond_type = 0;
1319
        gint new = 0, unread = 0, total = 0;
1320
        time_t mtime = 0;
1321
        gboolean use_auto_to_on_reply = FALSE;
1322
        gchar *auto_to = NULL, *auto_cc = NULL, *auto_bcc = NULL,
1323
              *auto_replyto = NULL;
1324
        gboolean trim_summary_subject = FALSE, trim_compose_subject = FALSE;
1325

    
1326
        g_return_val_if_fail(node->data != NULL, FALSE);
1327
        if (!node->parent) return FALSE;
1328

    
1329
        xmlnode = node->data;
1330
        if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
1331
                g_warning("tag name != \"folderitem\"\n");
1332
                return FALSE;
1333
        }
1334

    
1335
        list = xmlnode->tag->attr;
1336
        for (; list != NULL; list = list->next) {
1337
                XMLAttr *attr = list->data;
1338

    
1339
                if (!attr || !attr->name || !attr->value) continue;
1340
                if (!strcmp(attr->name, "type")) {
1341
                        if (!g_ascii_strcasecmp(attr->value, "normal"))
1342
                                stype = F_NORMAL;
1343
                        else if (!g_ascii_strcasecmp(attr->value, "inbox"))
1344
                                stype = F_INBOX;
1345
                        else if (!g_ascii_strcasecmp(attr->value, "outbox"))
1346
                                stype = F_OUTBOX;
1347
                        else if (!g_ascii_strcasecmp(attr->value, "draft"))
1348
                                stype = F_DRAFT;
1349
                        else if (!g_ascii_strcasecmp(attr->value, "queue"))
1350
                                stype = F_QUEUE;
1351
                        else if (!g_ascii_strcasecmp(attr->value, "trash"))
1352
                                stype = F_TRASH;
1353
#if 0
1354
                        else if (!g_ascii_strcasecmp(attr->value, "junk"))
1355
                                stype = F_JUNK;
1356
#endif
1357
                        else if (!g_ascii_strcasecmp(attr->value, "virtual"))
1358
                                stype = F_VIRTUAL;
1359
                } else if (!strcmp(attr->name, "name"))
1360
                        name = attr->value;
1361
                else if (!strcmp(attr->name, "path")) {
1362
#ifdef G_OS_WIN32
1363
                        subst_char(attr->value, G_DIR_SEPARATOR, '/');
1364
#endif
1365
                        path = attr->value;
1366
                } else if (!strcmp(attr->name, "mtime"))
1367
                        mtime = strtoul(attr->value, NULL, 10);
1368
                else if (!strcmp(attr->name, "new"))
1369
                        new = atoi(attr->value);
1370
                else if (!strcmp(attr->name, "unread"))
1371
                        unread = atoi(attr->value);
1372
                else if (!strcmp(attr->name, "total"))
1373
                        total = atoi(attr->value);
1374
                else if (!strcmp(attr->name, "no_sub"))
1375
                        no_sub = *attr->value == '1' ? TRUE : FALSE;
1376
                else if (!strcmp(attr->name, "no_select"))
1377
                        no_select = *attr->value == '1' ? TRUE : FALSE;
1378
                else if (!strcmp(attr->name, "collapsed"))
1379
                        collapsed = *attr->value == '1' ? TRUE : FALSE;
1380
                else if (!strcmp(attr->name, "threaded"))
1381
                        threaded =  *attr->value == '1' ? TRUE : FALSE;
1382
                else if (!strcmp(attr->name, "sort_key")) {
1383
                        if (!strcmp(attr->value, "none"))
1384
                                sort_key = SORT_BY_NONE;
1385
                        else if (!strcmp(attr->value, "number"))
1386
                                sort_key = SORT_BY_NUMBER;
1387
                        else if (!strcmp(attr->value, "size"))
1388
                                sort_key = SORT_BY_SIZE;
1389
                        else if (!strcmp(attr->value, "date"))
1390
                                sort_key = SORT_BY_DATE;
1391
                        else if (!strcmp(attr->value, "thread-date"))
1392
                                sort_key = SORT_BY_TDATE;
1393
                        else if (!strcmp(attr->value, "from"))
1394
                                sort_key = SORT_BY_FROM;
1395
                        else if (!strcmp(attr->value, "subject"))
1396
                                sort_key = SORT_BY_SUBJECT;
1397
                        else if (!strcmp(attr->value, "score"))
1398
                                sort_key = SORT_BY_SCORE;
1399
                        else if (!strcmp(attr->value, "label"))
1400
                                sort_key = SORT_BY_LABEL;
1401
                        else if (!strcmp(attr->value, "mark"))
1402
                                sort_key = SORT_BY_MARK;
1403
                        else if (!strcmp(attr->value, "unread"))
1404
                                sort_key = SORT_BY_UNREAD;
1405
                        else if (!strcmp(attr->value, "mime"))
1406
                                sort_key = SORT_BY_MIME;
1407
                        else if (!strcmp(attr->value, "to"))
1408
                                sort_key = SORT_BY_TO;
1409
                } else if (!strcmp(attr->name, "sort_type")) {
1410
                        if (!strcmp(attr->value, "ascending"))
1411
                                sort_type = SORT_ASCENDING;
1412
                        else
1413
                                sort_type = SORT_DESCENDING;
1414
                } else if (!strcmp(attr->name, "qsearch_cond")) {
1415
                        if (!strcmp(attr->value, "all"))
1416
                                qsearch_cond_type = 0;
1417
                        else if (!strcmp(attr->value, "unread"))
1418
                                qsearch_cond_type = 1;
1419
                        else if (!strcmp(attr->value, "mark"))
1420
                                qsearch_cond_type = 2;
1421
                        else if (!strcmp(attr->value, "clabel"))
1422
                                qsearch_cond_type = 3;
1423
                        else if (!strcmp(attr->value, "mime"))
1424
                                qsearch_cond_type = 4;
1425
                        else if (!strcmp(attr->value, "w1day"))
1426
                                qsearch_cond_type = 5;
1427
                        else if (!strcmp(attr->value, "last5"))
1428
                                qsearch_cond_type = 6;
1429
                } else if (!strcmp(attr->name, "account_id")) {
1430
                        account = account_find_from_id(atoi(attr->value));
1431
                        if (!account) g_warning("account_id: %s not found\n",
1432
                                                attr->value);
1433
                } else if (!strcmp(attr->name, "account_apply_sub"))
1434
                        ac_apply_sub = *attr->value == '1' ? TRUE : FALSE;
1435
                else if (!strcmp(attr->name, "to"))
1436
                        auto_to = g_strdup(attr->value);
1437
                else if (!strcmp(attr->name, "use_auto_to_on_reply"))
1438
                        use_auto_to_on_reply =
1439
                                *attr->value == '1' ? TRUE : FALSE;
1440
                else if (!strcmp(attr->name, "cc"))
1441
                        auto_cc = g_strdup(attr->value);
1442
                else if (!strcmp(attr->name, "bcc"))
1443
                        auto_bcc = g_strdup(attr->value);
1444
                else if (!strcmp(attr->name, "replyto"))
1445
                        auto_replyto = g_strdup(attr->value);
1446
                else if (!strcmp(attr->name, "trim_summary_subject")) {
1447
                        trim_summary_subject =
1448
                                *attr->value == '1' ? TRUE : FALSE;
1449
                } else if (!strcmp(attr->name, "trim_compose_subject")) {
1450
                        trim_compose_subject =
1451
                                *attr->value = '1' ? TRUE : FALSE;
1452
                }
1453
        }
1454

    
1455
        item = folder_item_new(name, path);
1456
        item->stype = stype;
1457
        item->mtime = mtime;
1458
        item->new = new;
1459
        item->unread = unread;
1460
        item->total = total;
1461
        item->no_sub = no_sub;
1462
        item->no_select = no_select;
1463
        item->collapsed = collapsed;
1464
        item->threaded  = threaded;
1465
        item->sort_key  = sort_key;
1466
        item->sort_type = sort_type;
1467
        item->node = node;
1468
        item->parent = FOLDER_ITEM(node->parent->data);
1469
        item->folder = folder;
1470
        switch (stype) {
1471
        case F_INBOX:  folder->inbox  = item; break;
1472
        case F_OUTBOX: folder->outbox = item; break;
1473
        case F_DRAFT:  folder->draft  = item; break;
1474
        case F_QUEUE:  folder->queue  = item; break;
1475
        case F_TRASH:  folder->trash  = item; break;
1476
        /* case F_JUNK:   folder->junk   = item; break; */
1477
        default:       break;
1478
        }
1479
        item->account = account;
1480
        item->ac_apply_sub = ac_apply_sub;
1481
        item->auto_to = auto_to;
1482
        item->use_auto_to_on_reply = use_auto_to_on_reply;
1483
        item->auto_cc = auto_cc;
1484
        item->auto_bcc = auto_bcc;
1485
        item->auto_replyto = auto_replyto;
1486
        item->trim_summary_subject = trim_summary_subject;
1487
        item->trim_compose_subject = trim_compose_subject;
1488
        item->qsearch_cond_type = qsearch_cond_type;
1489
        node->data = item;
1490
        xml_free_node(xmlnode);
1491

    
1492
        return FALSE;
1493
}
1494

    
1495
static gboolean folder_read_folder_func(GNode *node, gpointer data)
1496
{
1497
        Folder *folder;
1498
        FolderItem *item;
1499
        XMLNode *xmlnode;
1500
        GList *list;
1501
        FolderType type = F_UNKNOWN;
1502
        const gchar *name = NULL;
1503
        const gchar *path = NULL;
1504
        PrefsAccount *account = NULL;
1505
        gboolean collapsed = FALSE, threaded = TRUE, ac_apply_sub = FALSE;
1506

    
1507
        if (g_node_depth(node) != 2) return FALSE;
1508
        g_return_val_if_fail(node->data != NULL, FALSE);
1509

    
1510
        xmlnode = node->data;
1511
        if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
1512
                g_warning("tag name != \"folder\"\n");
1513
                return TRUE;
1514
        }
1515
        g_node_unlink(node);
1516
        list = xmlnode->tag->attr;
1517
        for (; list != NULL; list = list->next) {
1518
                XMLAttr *attr = list->data;
1519

    
1520
                if (!attr || !attr->name || !attr->value) continue;
1521
                if (!strcmp(attr->name, "type")) {
1522
                        if (!g_ascii_strcasecmp(attr->value, "mh"))
1523
                                type = F_MH;
1524
                        else if (!g_ascii_strcasecmp(attr->value, "mbox"))
1525
                                type = F_MBOX;
1526
                        else if (!g_ascii_strcasecmp(attr->value, "maildir"))
1527
                                type = F_MAILDIR;
1528
                        else if (!g_ascii_strcasecmp(attr->value, "imap"))
1529
                                type = F_IMAP;
1530
                        else if (!g_ascii_strcasecmp(attr->value, "news"))
1531
                                type = F_NEWS;
1532
                } else if (!strcmp(attr->name, "name"))
1533
                        name = attr->value;
1534
                else if (!strcmp(attr->name, "path"))
1535
                        path = attr->value;
1536
                else if (!strcmp(attr->name, "collapsed"))
1537
                        collapsed = *attr->value == '1' ? TRUE : FALSE;
1538
                else if (!strcmp(attr->name, "threaded"))
1539
                        threaded = *attr->value == '1' ? TRUE : FALSE;
1540
                else if (!strcmp(attr->name, "account_id")) {
1541
                        account = account_find_from_id(atoi(attr->value));
1542
                        if (!account) g_warning("account_id: %s not found\n",
1543
                                                attr->value);
1544
                } else if (!strcmp(attr->name, "account_apply_sub"))
1545
                        ac_apply_sub = *attr->value == '1' ? TRUE : FALSE;
1546
        }
1547

    
1548
        folder = folder_new(type, name, path);
1549
        g_return_val_if_fail(folder != NULL, FALSE);
1550
        if (account && FOLDER_IS_REMOTE(folder)) {
1551
                folder->account = account;
1552
                account->folder = REMOTE_FOLDER(folder);
1553
        }
1554
        if (account && FOLDER_IS_LOCAL(folder))
1555
                ac_apply_sub = TRUE;
1556
        item = FOLDER_ITEM(folder->node->data);
1557
        node->data = item;
1558
        item->node = node;
1559
        g_node_destroy(folder->node);
1560
        folder->node = node;
1561
        folder_add(folder);
1562
        item->collapsed = collapsed;
1563
        item->threaded  = threaded;
1564
        item->account   = account;
1565
        item->ac_apply_sub = ac_apply_sub;
1566

    
1567
        g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1568
                        folder_build_tree, folder);
1569

    
1570
        return FALSE;
1571
}
1572

    
1573
static gchar *folder_get_list_path(void)
1574
{
1575
        static gchar *filename = NULL;
1576

    
1577
        if (!filename)
1578
                filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1579
                                        FOLDER_LIST, NULL);
1580

    
1581
        return filename;
1582
}
1583

    
1584
#define PUT_ESCAPE_STR(fp, attr, str)                        \
1585
{                                                        \
1586
        fputs(" " attr "=\"", fp);                        \
1587
        xml_file_put_escape_str(fp, str);                \
1588
        fputs("\"", fp);                                \
1589
}
1590

    
1591
static void folder_write_list_recursive(GNode *node, gpointer data)
1592
{
1593
        FILE *fp = (FILE *)data;
1594
        FolderItem *item;
1595
        gint i, depth;
1596
        static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1597
                                           "news", "unknown"};
1598
        static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1599
                                                 "draft", "queue", "trash",
1600
                                                 "junk", "virtual"};
1601
        static gchar *sort_key_str[] = {"none", "number", "size", "date",
1602
                                        "thread-date",
1603
                                        "from", "subject", "score", "label",
1604
                                        "mark", "unread", "mime", "to"};
1605
        static gchar *qsearch_cond_str[] = {"all", "unread", "mark", "clabel",
1606
                                            "mime", "w1day", "last5"};
1607

    
1608
        g_return_if_fail(node != NULL);
1609
        g_return_if_fail(fp != NULL);
1610

    
1611
        item = FOLDER_ITEM(node->data);
1612
        g_return_if_fail(item != NULL);
1613

    
1614
        depth = g_node_depth(node);
1615
        for (i = 0; i < depth; i++)
1616
                fputs("    ", fp);
1617
        if (depth == 1) {
1618
                Folder *folder = item->folder;
1619

    
1620
                fprintf(fp, "<folder type=\"%s\"",
1621
                        folder_type_str[FOLDER_TYPE(folder)]);
1622
                if (folder->name)
1623
                        PUT_ESCAPE_STR(fp, "name", folder->name);
1624
                if (FOLDER_TYPE(folder) == F_MH)
1625
                        PUT_ESCAPE_STR(fp, "path",
1626
                                       LOCAL_FOLDER(folder)->rootpath);
1627
                if (item->collapsed && node->children)
1628
                        fputs(" collapsed=\"1\"", fp);
1629
                if (folder->account)
1630
                        fprintf(fp, " account_id=\"%d\"",
1631
                                folder->account->account_id);
1632
                else if (item->account)
1633
                        fprintf(fp, " account_id=\"%d\"",
1634
                                item->account->account_id);
1635
                if (item->ac_apply_sub)
1636
                        fputs(" account_apply_sub=\"1\"", fp);
1637
        } else {
1638
                fprintf(fp, "<folderitem type=\"%s\"",
1639
                        folder_item_stype_str[item->stype]);
1640
                if (item->name)
1641
                        PUT_ESCAPE_STR(fp, "name", item->name);
1642
                if (item->path)
1643
                        PUT_ESCAPE_STR(fp, "path", item->path);
1644

    
1645
                if (item->no_sub)
1646
                        fputs(" no_sub=\"1\"", fp);
1647
                if (item->no_select)
1648
                        fputs(" no_select=\"1\"", fp);
1649
                if (item->collapsed && node->children)
1650
                        fputs(" collapsed=\"1\"", fp);
1651
                if (item->threaded)
1652
                        fputs(" threaded=\"1\"", fp);
1653
                else
1654
                        fputs(" threaded=\"0\"", fp);
1655

    
1656
                if (item->sort_key != SORT_BY_NONE) {
1657
                        fprintf(fp, " sort_key=\"%s\"",
1658
                                sort_key_str[item->sort_key]);
1659
                        if (item->sort_type == SORT_ASCENDING)
1660
                                fprintf(fp, " sort_type=\"ascending\"");
1661
                        else
1662
                                fprintf(fp, " sort_type=\"descending\"");
1663
                }
1664
                if (item->qsearch_cond_type > 0 &&
1665
                    item->qsearch_cond_type < 7) {
1666
                        fprintf(fp, " qsearch_cond=\"%s\"",
1667
                                qsearch_cond_str[item->qsearch_cond_type]);
1668
                }
1669

    
1670
                fprintf(fp,
1671
                        " mtime=\"%lu\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1672
                        item->mtime, item->new, item->unread, item->total);
1673

    
1674
                if (item->account)
1675
                        fprintf(fp, " account_id=\"%d\"",
1676
                                item->account->account_id);
1677
                if (item->ac_apply_sub)
1678
                        fputs(" account_apply_sub=\"1\"", fp);
1679

    
1680
                if (item->auto_to)
1681
                        PUT_ESCAPE_STR(fp, "to", item->auto_to);
1682
                if (item->use_auto_to_on_reply)
1683
                        fputs(" use_auto_to_on_reply=\"1\"", fp);
1684
                if (item->auto_cc)
1685
                        PUT_ESCAPE_STR(fp, "cc", item->auto_cc);
1686
                if (item->auto_bcc)
1687
                        PUT_ESCAPE_STR(fp, "bcc", item->auto_bcc);
1688
                if (item->auto_replyto)
1689
                        PUT_ESCAPE_STR(fp, "replyto", item->auto_replyto);
1690

    
1691
                if (item->trim_summary_subject)
1692
                        fputs(" trim_summary_subject=\"1\"", fp);
1693
                if (item->trim_compose_subject)
1694
                        fputs(" trim_compose_subject=\"1\"", fp);
1695
        }
1696

    
1697
        if (node->children) {
1698
                GNode *child;
1699
                fputs(">\n", fp);
1700

    
1701
                child = node->children;
1702
                while (child) {
1703
                        GNode *cur;
1704

    
1705
                        cur = child;
1706
                        child = cur->next;
1707
                        folder_write_list_recursive(cur, data);
1708
                }
1709

    
1710
                for (i = 0; i < depth; i++)
1711
                        fputs("    ", fp);
1712
                fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1713
        } else
1714
                fputs(" />\n", fp);
1715
}
1716

    
1717
#undef PUT_ESCAPE_STR