Statistics
| Revision:

root / src / folder.c @ 1

History | View | Annotate | Download (36 KB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2003 Hiroyuki Yamamoto
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
 */
19

    
20
#ifdef HAVE_CONFIG_H
21
#  include "config.h"
22
#endif
23

    
24
#include "defs.h"
25

    
26
#include <glib.h>
27
#include <stdio.h>
28
#include <string.h>
29
#include <stdlib.h>
30

    
31
#include "intl.h"
32
#include "folder.h"
33
#include "session.h"
34
#include "imap.h"
35
#include "news.h"
36
#include "mh.h"
37
#include "utils.h"
38
#include "xml.h"
39
#include "codeconv.h"
40
#include "prefs.h"
41
#include "account.h"
42
#include "prefs_account.h"
43

    
44
static GList *folder_list = NULL;
45

    
46
static void folder_init                (Folder                *folder,
47
                                 const gchar        *name);
48

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

    
55

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

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

    
75
        return folder;
76
}
77

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

    
82
        g_return_if_fail(folder != NULL);
83

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

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

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

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

    
118
        folder->klass->destroy(folder);
119

    
120
        folder_list = g_list_remove(folder_list, folder);
121

    
122
        folder_tree_destroy(folder);
123
        g_free(folder->name);
124
        g_free(folder);
125
}
126

    
127
void folder_local_folder_destroy(LocalFolder *lfolder)
128
{
129
        g_return_if_fail(lfolder != NULL);
130

    
131
        g_free(lfolder->rootpath);
132
}
133

    
134
void folder_remote_folder_destroy(RemoteFolder *rfolder)
135
{
136
        g_return_if_fail(rfolder != NULL);
137

    
138
        if (rfolder->session)
139
                session_destroy(rfolder->session);
140
}
141

    
142
#if 0
143
Folder *mbox_folder_new(const gchar *name, const gchar *path)
144
{
145
        /* not yet implemented */
146
        return NULL;
147
}
148

149
Folder *maildir_folder_new(const gchar *name, const gchar *path)
150
{
151
        /* not yet implemented */
152
        return NULL;
153
}
154
#endif
155

    
156
FolderItem *folder_item_new(const gchar *name, const gchar *path)
157
{
158
        FolderItem *item;
159

    
160
        item = g_new0(FolderItem, 1);
161

    
162
        item->stype = F_NORMAL;
163
        item->name = g_strdup(name);
164
        item->path = g_strdup(path);
165
        item->mtime = 0;
166
        item->new = 0;
167
        item->unread = 0;
168
        item->total = 0;
169
        item->unmarked_num = 0;
170
        item->last_num = -1;
171
        item->no_sub = FALSE;
172
        item->no_select = FALSE;
173
        item->collapsed = FALSE;
174
        item->threaded = TRUE;
175
        item->opened = FALSE;
176
        item->node = NULL;
177
        item->parent = NULL;
178
        item->folder = NULL;
179
        item->account = NULL;
180
        item->ac_apply_sub = FALSE;
181
        item->auto_to = NULL;
182
        item->use_auto_to_on_reply = FALSE;
183
        item->auto_cc = NULL;
184
        item->auto_bcc = NULL;
185
        item->auto_replyto = NULL;
186
        item->mark_queue = NULL;
187
        item->data = NULL;
188

    
189
        return item;
190
}
191

    
192
void folder_item_append(FolderItem *parent, FolderItem *item)
193
{
194
        g_return_if_fail(parent != NULL);
195
        g_return_if_fail(parent->folder != NULL);
196
        g_return_if_fail(parent->node != NULL);
197
        g_return_if_fail(item != NULL);
198

    
199
        item->parent = parent;
200
        item->folder = parent->folder;
201
        item->node = g_node_append_data(parent->node, item);
202
}
203

    
204
static gboolean folder_item_remove_func(GNode *node, gpointer data)
205
{
206
        FolderItem *item = FOLDER_ITEM(node->data);
207

    
208
        folder_item_destroy(item);
209
        return FALSE;
210
}
211

    
212
void folder_item_remove(FolderItem *item)
213
{
214
        GNode *node;
215

    
216
        g_return_if_fail(item != NULL);
217
        g_return_if_fail(item->folder != NULL);
218
        g_return_if_fail(item->node != NULL);
219

    
220
        node = item->node;
221

    
222
        if (item->folder->node == node)
223
                item->folder->node = NULL;
224

    
225
        g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
226
                        folder_item_remove_func, NULL);
227
        g_node_destroy(node);
228
}
229

    
230
void folder_item_remove_children(FolderItem *item)
231
{
232
        GNode *node, *next;
233

    
234
        g_return_if_fail(item != NULL);
235
        g_return_if_fail(item->folder != NULL);
236
        g_return_if_fail(item->node != NULL);
237

    
238
        node = item->node->children;
239
        while (node != NULL) {
240
                next = node->next;
241
                folder_item_remove(FOLDER_ITEM(node->data));
242
                node = next;
243
        }
244
}
245

    
246
void folder_item_destroy(FolderItem *item)
247
{
248
        Folder *folder;
249

    
250
        g_return_if_fail(item != NULL);
251

    
252
        folder = item->folder;
253
        if (folder) {
254
                if (folder->inbox == item)
255
                        folder->inbox = NULL;
256
                else if (folder->outbox == item)
257
                        folder->outbox = NULL;
258
                else if (folder->draft == item)
259
                        folder->draft = NULL;
260
                else if (folder->queue == item)
261
                        folder->queue = NULL;
262
                else if (folder->trash == item)
263
                        folder->trash = NULL;
264
        }
265

    
266
        g_free(item->name);
267
        g_free(item->path);
268
        g_free(item->auto_to);
269
        g_free(item->auto_cc);
270
        g_free(item->auto_bcc);
271
        g_free(item->auto_replyto);
272
        g_free(item);
273
}
274

    
275
void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
276
{
277
        g_return_if_fail(folder != NULL);
278

    
279
        folder->ui_func = func;
280
        folder->ui_func_data = data;
281
}
282

    
283
void folder_set_name(Folder *folder, const gchar *name)
284
{
285
        g_return_if_fail(folder != NULL);
286

    
287
        g_free(folder->name);
288
        folder->name = name ? g_strdup(name) : NULL;
289
        if (folder->node && folder->node->data) {
290
                FolderItem *item = (FolderItem *)folder->node->data;
291

    
292
                g_free(item->name);
293
                item->name = name ? g_strdup(name) : NULL;
294
        }
295
}
296

    
297
void folder_tree_destroy(Folder *folder)
298
{
299
        g_return_if_fail(folder != NULL);
300

    
301
        if (folder->node)
302
                folder_item_remove(FOLDER_ITEM(folder->node->data));
303
}
304

    
305
void folder_add(Folder *folder)
306
{
307
        Folder *cur_folder;
308
        GList *cur;
309
        gint i;
310

    
311
        g_return_if_fail(folder != NULL);
312

    
313
        for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
314
                cur_folder = FOLDER(cur->data);
315
                if (FOLDER_TYPE(folder) == F_MH) {
316
                        if (FOLDER_TYPE(cur_folder) != F_MH) break;
317
                } else if (FOLDER_TYPE(folder) == F_IMAP) {
318
                        if (FOLDER_TYPE(cur_folder) != F_MH &&
319
                            FOLDER_TYPE(cur_folder) != F_IMAP) break;
320
                } else if (FOLDER_TYPE(folder) == F_NEWS) {
321
                        if (FOLDER_TYPE(cur_folder) != F_MH &&
322
                            FOLDER_TYPE(cur_folder) != F_IMAP &&
323
                            FOLDER_TYPE(cur_folder) != F_NEWS) break;
324
                }
325
        }
326

    
327
        folder_list = g_list_insert(folder_list, folder, i);
328
}
329

    
330
GList *folder_get_list(void)
331
{
332
        return folder_list;
333
}
334

    
335
gint folder_read_list(void)
336
{
337
        GNode *node;
338
        XMLNode *xmlnode;
339
        gchar *path;
340

    
341
        path = folder_get_list_path();
342
        if (!is_file_exist(path)) return -1;
343
        node = xml_parse_file(path);
344
        if (!node) return -1;
345

    
346
        xmlnode = node->data;
347
        if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
348
                g_warning("wrong folder list\n");
349
                xml_free_tree(node);
350
                return -1;
351
        }
352

    
353
        g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
354
                        folder_read_folder_func, NULL);
355

    
356
        xml_free_tree(node);
357
        if (folder_list)
358
                return 0;
359
        else
360
                return -1;
361
}
362

    
363
void folder_write_list(void)
364
{
365
        GList *list;
366
        Folder *folder;
367
        gchar *path;
368
        PrefFile *pfile;
369

    
370
        path = folder_get_list_path();
371
        if ((pfile = prefs_file_open(path)) == NULL) return;
372

    
373
        fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
374
                conv_get_internal_charset_str());
375
        fputs("\n<folderlist>\n", pfile->fp);
376

    
377
        for (list = folder_list; list != NULL; list = list->next) {
378
                folder = list->data;
379
                folder_write_list_recursive(folder->node, pfile->fp);
380
        }
381

    
382
        fputs("</folderlist>\n", pfile->fp);
383

    
384
        if (prefs_file_close(pfile) < 0)
385
                g_warning("failed to write folder list.\n");
386
}
387

    
388
struct TotalMsgStatus
389
{
390
        guint new;
391
        guint unread;
392
        guint total;
393
        GString *str;
394
};
395

    
396
static gboolean folder_get_status_full_all_func(GNode *node, gpointer data)
397
{
398
        FolderItem *item;
399
        struct TotalMsgStatus *status = (struct TotalMsgStatus *)data;
400
        gchar *id;
401

    
402
        g_return_val_if_fail(node->data != NULL, FALSE);
403

    
404
        item = FOLDER_ITEM(node->data);
405

    
406
        if (!item->path) return FALSE;
407

    
408
        status->new += item->new;
409
        status->unread += item->unread;
410
        status->total += item->total;
411

    
412
        if (status->str) {
413
                id = folder_item_get_identifier(item);
414
                g_string_sprintfa(status->str, "%5d %5d %5d %s\n",
415
                                  item->new, item->unread,
416
                                  item->total, id);
417
                g_free(id);
418
        }
419

    
420
        return FALSE;
421
}
422

    
423
static void folder_get_status_full_all(GString *str, guint *new, guint *unread,
424
                                       guint *total)
425
{
426
        GList *list;
427
        Folder *folder;
428
        struct TotalMsgStatus status;
429

    
430
        status.new = status.unread = status.total = 0;
431
        status.str = str;
432

    
433
        debug_print("Counting total number of messages...\n");
434

    
435
        for (list = folder_list; list != NULL; list = list->next) {
436
                folder = FOLDER(list->data);
437
                if (folder->node)
438
                        g_node_traverse(folder->node, G_PRE_ORDER,
439
                                        G_TRAVERSE_ALL, -1,
440
                                        folder_get_status_full_all_func,
441
                                        &status);
442
        }
443

    
444
        *new = status.new;
445
        *unread = status.unread;
446
        *total = status.total;
447
}
448

    
449
gchar *folder_get_status(GPtrArray *folders, gboolean full)
450
{
451
        guint new, unread, total;
452
        GString *str;
453
        gint i;
454
        gchar *ret;
455

    
456
        new = unread = total = 0;
457

    
458
        str = g_string_new(NULL);
459

    
460
        if (folders) {
461
                for (i = 0; i < folders->len; i++) {
462
                        FolderItem *item;
463

    
464
                        item = g_ptr_array_index(folders, i);
465
                        new += item->new;
466
                        unread += item->unread;
467
                        total += item->total;
468

    
469
                        if (full) {
470
                                gchar *id;
471

    
472
                                id = folder_item_get_identifier(item);
473
                                g_string_sprintfa(str, "%5d %5d %5d %s\n",
474
                                                  item->new, item->unread,
475
                                                  item->total, id);
476
                                g_free(id);
477
                        }
478
                }
479
        } else {
480
                folder_get_status_full_all(full ? str : NULL,
481
                                           &new, &unread, &total);
482
        }
483

    
484
        if (full)
485
                g_string_sprintfa(str, "%5d %5d %5d\n", new, unread, total);
486
        else
487
                g_string_sprintfa(str, "%d %d %d\n", new, unread, total);
488

    
489
        ret = str->str;
490
        g_string_free(str, FALSE);
491

    
492
        return ret;
493
}
494

    
495
Folder *folder_find_from_path(const gchar *path)
496
{
497
        GList *list;
498
        Folder *folder;
499

    
500
        for (list = folder_list; list != NULL; list = list->next) {
501
                folder = list->data;
502
                if (FOLDER_TYPE(folder) == F_MH &&
503
                    !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
504
                        return folder;
505
        }
506

    
507
        return NULL;
508
}
509

    
510
Folder *folder_find_from_name(const gchar *name, FolderType type)
511
{
512
        GList *list;
513
        Folder *folder;
514

    
515
        for (list = folder_list; list != NULL; list = list->next) {
516
                folder = list->data;
517
                if (FOLDER_TYPE(folder) == type &&
518
                    strcmp2(name, folder->name) == 0)
519
                        return folder;
520
        }
521

    
522
        return NULL;
523
}
524

    
525
static gboolean folder_item_find_func(GNode *node, gpointer data)
526
{
527
        FolderItem *item = node->data;
528
        gpointer *d = data;
529
        const gchar *path = d[0];
530

    
531
        if (path_cmp(path, item->path) != 0)
532
                return FALSE;
533

    
534
        d[1] = item;
535

    
536
        return TRUE;
537
}
538

    
539
FolderItem *folder_find_item_from_path(const gchar *path)
540
{
541
        Folder *folder;
542
        gpointer d[2];
543

    
544
        folder = folder_get_default_folder();
545
        g_return_val_if_fail(folder != NULL, NULL);
546

    
547
        d[0] = (gpointer)path;
548
        d[1] = NULL;
549
        g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
550
                        folder_item_find_func, d);
551
        return d[1];
552
}
553

    
554
FolderItem *folder_find_child_item_by_name(FolderItem *item, const gchar *name)
555
{
556
        GNode *node;
557
        FolderItem *child;
558

    
559
        for (node = item->node->children; node != NULL; node = node->next) {
560
                child = FOLDER_ITEM(node->data);
561
                if (strcmp2(g_basename(child->path), name) == 0)
562
                        return child;
563
        }
564

    
565
        return NULL;
566
}
567

    
568
static const struct {
569
        gchar *str;
570
        FolderType type;
571
} type_str_table[] = {
572
        {"#mh"     , F_MH},
573
        {"#mbox"   , F_MBOX},
574
        {"#maildir", F_MAILDIR},
575
        {"#imap"   , F_IMAP},
576
        {"#news"   , F_NEWS}
577
};
578

    
579
static gchar *folder_get_type_string(FolderType type)
580
{
581
        gint i;
582

    
583
        for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
584
             i++) {
585
                if (type_str_table[i].type == type)
586
                        return type_str_table[i].str;
587
        }
588

    
589
        return NULL;
590
}
591

    
592
static FolderType folder_get_type_from_string(const gchar *str)
593
{
594
        gint i;
595

    
596
        for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
597
             i++) {
598
                if (g_strcasecmp(type_str_table[i].str, str) == 0)
599
                        return type_str_table[i].type;
600
        }
601

    
602
        return F_UNKNOWN;
603
}
604

    
605
gchar *folder_get_identifier(Folder *folder)
606
{
607
        gchar *type_str;
608

    
609
        g_return_val_if_fail(folder != NULL, NULL);
610

    
611
        type_str = folder_get_type_string(FOLDER_TYPE(folder));
612
        return g_strconcat(type_str, "/", folder->name, NULL);
613
}
614

    
615
gchar *folder_item_get_identifier(FolderItem *item)
616
{
617
        gchar *id;
618
        gchar *folder_id;
619

    
620
        g_return_val_if_fail(item != NULL, NULL);
621
        g_return_val_if_fail(item->path != NULL, NULL);
622

    
623
        folder_id = folder_get_identifier(item->folder);
624
        id = g_strconcat(folder_id, "/", item->path, NULL);
625
        g_free(folder_id);
626

    
627
        return id;
628
}
629

    
630
FolderItem *folder_find_item_from_identifier(const gchar *identifier)
631
{
632
        Folder *folder;
633
        gpointer d[2];
634
        gchar *str;
635
        gchar *p;
636
        gchar *name;
637
        gchar *path;
638
        FolderType type;
639

    
640
        g_return_val_if_fail(identifier != NULL, NULL);
641

    
642
        if (*identifier != '#')
643
                return folder_find_item_from_path(identifier);
644

    
645
        Xstrdup_a(str, identifier, return NULL);
646

    
647
        p = strchr(str, '/');
648
        if (!p)
649
                return folder_find_item_from_path(identifier);
650
        *p = '\0';
651
        p++;
652
        type = folder_get_type_from_string(str);
653
        if (type == F_UNKNOWN)
654
                return folder_find_item_from_path(identifier);
655

    
656
        name = p;
657
        p = strchr(p, '/');
658
        if (!p)
659
                return folder_find_item_from_path(identifier);
660
        *p = '\0';
661
        p++;
662

    
663
        folder = folder_find_from_name(name, type);
664
        if (!folder)
665
                return folder_find_item_from_path(identifier);
666

    
667
        path = p;
668

    
669
        d[0] = (gpointer)path;
670
        d[1] = NULL;
671
        g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
672
                        folder_item_find_func, d);
673
        return d[1];
674
}
675

    
676
Folder *folder_get_default_folder(void)
677
{
678
        return folder_list ? FOLDER(folder_list->data) : NULL;
679
}
680

    
681
FolderItem *folder_get_default_inbox(void)
682
{
683
        Folder *folder;
684

    
685
        if (!folder_list) return NULL;
686
        folder = FOLDER(folder_list->data);
687
        g_return_val_if_fail(folder != NULL, NULL);
688
        return folder->inbox;
689
}
690

    
691
FolderItem *folder_get_default_outbox(void)
692
{
693
        Folder *folder;
694

    
695
        if (!folder_list) return NULL;
696
        folder = FOLDER(folder_list->data);
697
        g_return_val_if_fail(folder != NULL, NULL);
698
        return folder->outbox;
699
}
700

    
701
FolderItem *folder_get_default_draft(void)
702
{
703
        Folder *folder;
704

    
705
        if (!folder_list) return NULL;
706
        folder = FOLDER(folder_list->data);
707
        g_return_val_if_fail(folder != NULL, NULL);
708
        return folder->draft;
709
}
710

    
711
FolderItem *folder_get_default_queue(void)
712
{
713
        Folder *folder;
714

    
715
        if (!folder_list) return NULL;
716
        folder = FOLDER(folder_list->data);
717
        g_return_val_if_fail(folder != NULL, NULL);
718
        return folder->queue;
719
}
720

    
721
FolderItem *folder_get_default_trash(void)
722
{
723
        Folder *folder;
724

    
725
        if (!folder_list) return NULL;
726
        folder = FOLDER(folder_list->data);
727
        g_return_val_if_fail(folder != NULL, NULL);
728
        return folder->trash;
729
}
730

    
731
#define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type)        \
732
{                                                        \
733
        if (!folder->member) {                                \
734
                item = folder_item_new(dir, dir);        \
735
                item->stype = type;                        \
736
                folder_item_append(rootitem, item);        \
737
                folder->member = item;                        \
738
        }                                                \
739
}
740

    
741
void folder_set_missing_folders(void)
742
{
743
        Folder *folder;
744
        FolderItem *rootitem;
745
        FolderItem *item;
746
        GList *list;
747

    
748
        for (list = folder_list; list != NULL; list = list->next) {
749
                folder = list->data;
750
                if (FOLDER_TYPE(folder) != F_MH) continue;
751
                rootitem = FOLDER_ITEM(folder->node->data);
752
                g_return_if_fail(rootitem != NULL);
753

    
754
                if (folder->inbox && folder->outbox && folder->draft &&
755
                    folder->queue && folder->trash)
756
                        continue;
757

    
758
                if (folder->klass->create_tree(folder) < 0) {
759
                        g_warning("%s: can't create the folder tree.\n",
760
                                  LOCAL_FOLDER(folder)->rootpath);
761
                        continue;
762
                }
763

    
764
                CREATE_FOLDER_IF_NOT_EXIST(inbox,  INBOX_DIR,  F_INBOX);
765
                CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX);
766
                CREATE_FOLDER_IF_NOT_EXIST(draft,  DRAFT_DIR,  F_DRAFT);
767
                CREATE_FOLDER_IF_NOT_EXIST(queue,  QUEUE_DIR,  F_QUEUE);
768
                CREATE_FOLDER_IF_NOT_EXIST(trash,  TRASH_DIR,  F_TRASH);
769
        }
770
}
771

    
772
static gboolean folder_unref_account_func(GNode *node, gpointer data)
773
{
774
        FolderItem *item = node->data;
775
        PrefsAccount *account = data;
776

    
777
        if (item->account == account)
778
                item->account = NULL;
779

    
780
        return FALSE;
781
}
782

    
783
void folder_unref_account_all(PrefsAccount *account)
784
{
785
        Folder *folder;
786
        GList *list;
787

    
788
        if (!account) return;
789

    
790
        for (list = folder_list; list != NULL; list = list->next) {
791
                folder = list->data;
792
                if (folder->account == account)
793
                        folder->account = NULL;
794
                g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
795
                                folder_unref_account_func, account);
796
        }
797
}
798

    
799
#undef CREATE_FOLDER_IF_NOT_EXIST
800

    
801
gchar *folder_get_path(Folder *folder)
802
{
803
        gchar *path;
804

    
805
        g_return_val_if_fail(folder != NULL, NULL);
806

    
807
        if (FOLDER_TYPE(folder) == F_MH) {
808
                path = g_filename_from_utf8(LOCAL_FOLDER(folder)->rootpath,
809
                                            -1, NULL, NULL, NULL);
810
                if (!path) {
811
                        g_warning("folder_get_path: faild to convert character set\n");
812
                        path = g_strdup(LOCAL_FOLDER(folder)->rootpath);
813
                }
814
        } else if (FOLDER_TYPE(folder) == F_IMAP) {
815
                g_return_val_if_fail(folder->account != NULL, NULL);
816
                path = g_strconcat(get_imap_cache_dir(),
817
                                   G_DIR_SEPARATOR_S,
818
                                   folder->account->recv_server,
819
                                   G_DIR_SEPARATOR_S,
820
                                   folder->account->userid,
821
                                   NULL);
822
        } else if (FOLDER_TYPE(folder) == F_NEWS) {
823
                g_return_val_if_fail(folder->account != NULL, NULL);
824
                path = g_strconcat(get_news_cache_dir(),
825
                                   G_DIR_SEPARATOR_S,
826
                                   folder->account->nntp_server,
827
                                   NULL);
828
        } else
829
                path = NULL;
830

    
831
        return path;
832
}
833

    
834
gchar *folder_item_get_path(FolderItem *item)
835
{
836
        gchar *folder_path;
837
        gchar *item_path = NULL, *path;
838

    
839
        g_return_val_if_fail(item != NULL, NULL);
840

    
841
        folder_path = folder_get_path(item->folder);
842
        g_return_val_if_fail(folder_path != NULL, NULL);
843

    
844
        if (item->path) {
845
                item_path = g_filename_from_utf8(item->path, -1,
846
                                                 NULL, NULL, NULL);
847
                if (!item_path) {
848
                        g_warning("folder_item_get_path: faild to convert character set\n");
849
                        item_path = g_strdup(item->path);
850
                }
851
        }
852

    
853
        if (folder_path[0] == G_DIR_SEPARATOR) {
854
                if (item_path)
855
                        path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
856
                                           item_path, NULL);
857
                else
858
                        path = g_strdup(folder_path);
859
        } else {
860
                if (item_path)
861
                        path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
862
                                           folder_path, G_DIR_SEPARATOR_S,
863
                                           item_path, NULL);
864
                else
865
                        path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
866
                                           folder_path, NULL);
867
        }
868

    
869
        g_free(item_path);
870
        g_free(folder_path);
871
        return path;
872
}
873

    
874
gint folder_item_scan(FolderItem *item)
875
{
876
        Folder *folder;
877

    
878
        g_return_val_if_fail(item != NULL, -1);
879

    
880
        folder = item->folder;
881
        return folder->klass->scan(folder, item);
882
}
883

    
884
static void folder_item_scan_foreach_func(gpointer key, gpointer val,
885
                                          gpointer data)
886
{
887
        folder_item_scan(FOLDER_ITEM(key));
888
}
889

    
890
void folder_item_scan_foreach(GHashTable *table)
891
{
892
        g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
893
}
894

    
895
GSList *folder_item_get_msg_list(FolderItem *item, gboolean use_cache)
896
{
897
        Folder *folder;
898

    
899
        g_return_val_if_fail(item != NULL, NULL);
900

    
901
        folder = item->folder;
902
        return folder->klass->get_msg_list(folder, item, use_cache);
903
}
904

    
905
gchar *folder_item_fetch_msg(FolderItem *item, gint num)
906
{
907
        Folder *folder;
908

    
909
        g_return_val_if_fail(item != NULL, NULL);
910

    
911
        folder = item->folder;
912

    
913
        return folder->klass->fetch_msg(folder, item, num);
914
}
915

    
916
gint folder_item_fetch_all_msg(FolderItem *item)
917
{
918
        Folder *folder;
919
        GSList *mlist;
920
        GSList *cur;
921
        gint num = 0;
922
        gint ret = 0;
923

    
924
        g_return_val_if_fail(item != NULL, -1);
925

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

    
928
        folder = item->folder;
929

    
930
        if (folder->ui_func)
931
                folder->ui_func(folder, item, folder->ui_func_data ?
932
                                folder->ui_func_data : GINT_TO_POINTER(num));
933

    
934
        mlist = folder_item_get_msg_list(item, TRUE);
935

    
936
        for (cur = mlist; cur != NULL; cur = cur->next) {
937
                MsgInfo *msginfo = (MsgInfo *)cur->data;
938
                gchar *msg;
939

    
940
                num++;
941
                if (folder->ui_func)
942
                        folder->ui_func(folder, item,
943
                                        folder->ui_func_data ?
944
                                        folder->ui_func_data :
945
                                        GINT_TO_POINTER(num));
946

    
947
                msg = folder_item_fetch_msg(item, msginfo->msgnum);
948
                if (!msg) {
949
                        g_warning("Can't fetch message %d. Aborting.\n",
950
                                  msginfo->msgnum);
951
                        ret = -1;
952
                        break;
953
                }
954
                g_free(msg);
955
        }
956

    
957
        procmsg_msg_list_free(mlist);
958

    
959
        return ret;
960
}
961

    
962
MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num)
963
{
964
        Folder *folder;
965

    
966
        g_return_val_if_fail(item != NULL, NULL);
967

    
968
        folder = item->folder;
969

    
970
        return folder->klass->get_msginfo(folder, item, num);
971
}
972

    
973
gint folder_item_add_msg(FolderItem *dest, const gchar *file, MsgFlags *flags,
974
                         gboolean remove_source)
975
{
976
        Folder *folder;
977

    
978
        g_return_val_if_fail(dest != NULL, -1);
979
        g_return_val_if_fail(file != NULL, -1);
980
        g_return_val_if_fail(dest->folder->klass->add_msg != NULL, -1);
981

    
982
        folder = dest->folder;
983

    
984
        return folder->klass->add_msg(folder, dest, file, flags, remove_source);
985
}
986

    
987
gint folder_item_add_msgs(FolderItem *dest, GSList *file_list,
988
                          gboolean remove_source, gint *first)
989
{
990
        Folder *folder;
991

    
992
        g_return_val_if_fail(dest != NULL, -1);
993
        g_return_val_if_fail(file_list != NULL, -1);
994
        g_return_val_if_fail(dest->folder->klass->add_msgs != NULL, -1);
995

    
996
        folder = dest->folder;
997

    
998
        return folder->klass->add_msgs(folder, dest, file_list, remove_source,
999
                                       first);
1000
}
1001

    
1002
gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
1003
{
1004
        Folder *folder;
1005

    
1006
        g_return_val_if_fail(dest != NULL, -1);
1007
        g_return_val_if_fail(msginfo != NULL, -1);
1008
        g_return_val_if_fail(dest->folder->klass->move_msg != NULL, -1);
1009

    
1010
        folder = dest->folder;
1011

    
1012
        return folder->klass->move_msg(folder, dest, msginfo);
1013
}
1014

    
1015
gint folder_item_move_msgs(FolderItem *dest, GSList *msglist)
1016
{
1017
        Folder *folder;
1018

    
1019
        g_return_val_if_fail(dest != NULL, -1);
1020
        g_return_val_if_fail(msglist != NULL, -1);
1021
        g_return_val_if_fail(dest->folder->klass->move_msgs != NULL, -1);
1022

    
1023
        folder = dest->folder;
1024

    
1025
        return folder->klass->move_msgs(folder, dest, msglist);
1026
}
1027

    
1028
gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
1029
{
1030
        Folder *folder;
1031

    
1032
        g_return_val_if_fail(dest != NULL, -1);
1033
        g_return_val_if_fail(msginfo != NULL, -1);
1034
        g_return_val_if_fail(dest->folder->klass->copy_msg != NULL, -1);
1035

    
1036
        folder = dest->folder;
1037

    
1038
        return folder->klass->copy_msg(folder, dest, msginfo);
1039
}
1040

    
1041
gint folder_item_copy_msgs(FolderItem *dest, GSList *msglist)
1042
{
1043
        Folder *folder;
1044

    
1045
        g_return_val_if_fail(dest != NULL, -1);
1046
        g_return_val_if_fail(msglist != NULL, -1);
1047
        g_return_val_if_fail(dest->folder->klass->copy_msgs != NULL, -1);
1048

    
1049
        folder = dest->folder;
1050

    
1051
        return folder->klass->copy_msgs(folder, dest, msglist);
1052
}
1053

    
1054
gint folder_item_remove_msg(FolderItem *item, MsgInfo *msginfo)
1055
{
1056
        Folder *folder;
1057

    
1058
        g_return_val_if_fail(item != NULL, -1);
1059
        g_return_val_if_fail(item->folder->klass->remove_msg != NULL, -1);
1060

    
1061
        folder = item->folder;
1062

    
1063
        return folder->klass->remove_msg(folder, item, msginfo);
1064
}
1065

    
1066
gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
1067
{
1068
        Folder *folder;
1069
        gint ret = 0;
1070

    
1071
        g_return_val_if_fail(item != NULL, -1);
1072

    
1073
        folder = item->folder;
1074
        if (folder->klass->remove_msgs) {
1075
                return folder->klass->remove_msgs(folder, item, msglist);
1076
        }
1077

    
1078
        while (msglist != NULL) {
1079
                MsgInfo *msginfo = (MsgInfo *)msglist->data;
1080

    
1081
                ret = folder_item_remove_msg(item, msginfo);
1082
                if (ret != 0) break;
1083
                msglist = msglist->next;
1084
        }
1085

    
1086
        return ret;
1087
}
1088

    
1089
gint folder_item_remove_all_msg(FolderItem *item)
1090
{
1091
        Folder *folder;
1092

    
1093
        g_return_val_if_fail(item != NULL, -1);
1094
        g_return_val_if_fail(item->folder->klass->remove_all_msg != NULL, -1);
1095

    
1096
        folder = item->folder;
1097

    
1098
        return folder->klass->remove_all_msg(folder, item);
1099
}
1100

    
1101
gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
1102
{
1103
        Folder *folder;
1104

    
1105
        g_return_val_if_fail(item != NULL, FALSE);
1106
        g_return_val_if_fail(item->folder->klass->is_msg_changed != NULL,
1107
                             FALSE);
1108

    
1109
        folder = item->folder;
1110
        return folder->klass->is_msg_changed(folder, item, msginfo);
1111
}
1112

    
1113
gint folder_item_close(FolderItem *item)
1114
{
1115
        Folder *folder;
1116

    
1117
        g_return_val_if_fail(item != NULL, -1);
1118

    
1119
        item->opened = FALSE;
1120
        folder = item->folder;
1121
        return folder->klass->close(folder, item);
1122
}
1123

    
1124
gchar *folder_item_get_cache_file(FolderItem *item)
1125
{
1126
        gchar *path;
1127
        gchar *file;
1128

    
1129
        g_return_val_if_fail(item != NULL, NULL);
1130
        g_return_val_if_fail(item->path != NULL, NULL);
1131

    
1132
        path = folder_item_get_path(item);
1133
        g_return_val_if_fail(path != NULL, NULL);
1134
        if (!is_dir_exist(path))
1135
                make_dir_hier(path);
1136
        file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
1137
        g_free(path);
1138

    
1139
        return file;
1140
}
1141

    
1142
gchar *folder_item_get_mark_file(FolderItem *item)
1143
{
1144
        gchar *path;
1145
        gchar *file;
1146

    
1147
        g_return_val_if_fail(item != NULL, NULL);
1148
        g_return_val_if_fail(item->path != NULL, NULL);
1149

    
1150
        path = folder_item_get_path(item);
1151
        g_return_val_if_fail(path != NULL, NULL);
1152
        if (!is_dir_exist(path))
1153
                make_dir_hier(path);
1154
        file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
1155
        g_free(path);
1156

    
1157
        return file;
1158
}
1159

    
1160
static gboolean folder_build_tree(GNode *node, gpointer data)
1161
{
1162
        Folder *folder = FOLDER(data);
1163
        FolderItem *item;
1164
        XMLNode *xmlnode;
1165
        GList *list;
1166
        SpecialFolderItemType stype = F_NORMAL;
1167
        const gchar *name = NULL;
1168
        const gchar *path = NULL;
1169
        PrefsAccount *account = NULL;
1170
        gboolean no_sub = FALSE, no_select = FALSE, collapsed = FALSE,
1171
                 threaded = TRUE, ac_apply_sub = FALSE;
1172
        FolderSortKey sort_key = SORT_BY_NONE;
1173
        FolderSortType sort_type = SORT_ASCENDING;
1174
        gint new = 0, unread = 0, total = 0;
1175
        time_t mtime = 0;
1176
        gboolean use_auto_to_on_reply = FALSE;
1177
        gchar *auto_to = NULL, *auto_cc = NULL, *auto_bcc = NULL,
1178
              *auto_replyto = NULL;
1179
        gboolean trim_summary_subject = FALSE, trim_compose_subject = FALSE;
1180

    
1181
        g_return_val_if_fail(node->data != NULL, FALSE);
1182
        if (!node->parent) return FALSE;
1183

    
1184
        xmlnode = node->data;
1185
        if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
1186
                g_warning("tag name != \"folderitem\"\n");
1187
                return FALSE;
1188
        }
1189

    
1190
        list = xmlnode->tag->attr;
1191
        for (; list != NULL; list = list->next) {
1192
                XMLAttr *attr = list->data;
1193

    
1194
                if (!attr || !attr->name || !attr->value) continue;
1195
                if (!strcmp(attr->name, "type")) {
1196
                        if (!strcasecmp(attr->value, "normal"))
1197
                                stype = F_NORMAL;
1198
                        else if (!strcasecmp(attr->value, "inbox"))
1199
                                stype = F_INBOX;
1200
                        else if (!strcasecmp(attr->value, "outbox"))
1201
                                stype = F_OUTBOX;
1202
                        else if (!strcasecmp(attr->value, "draft"))
1203
                                stype = F_DRAFT;
1204
                        else if (!strcasecmp(attr->value, "queue"))
1205
                                stype = F_QUEUE;
1206
                        else if (!strcasecmp(attr->value, "trash"))
1207
                                stype = F_TRASH;
1208
                } else if (!strcmp(attr->name, "name"))
1209
                        name = attr->value;
1210
                else if (!strcmp(attr->name, "path"))
1211
                        path = attr->value;
1212
                else if (!strcmp(attr->name, "mtime"))
1213
                        mtime = strtoul(attr->value, NULL, 10);
1214
                else if (!strcmp(attr->name, "new"))
1215
                        new = atoi(attr->value);
1216
                else if (!strcmp(attr->name, "unread"))
1217
                        unread = atoi(attr->value);
1218
                else if (!strcmp(attr->name, "total"))
1219
                        total = atoi(attr->value);
1220
                else if (!strcmp(attr->name, "no_sub"))
1221
                        no_sub = *attr->value == '1' ? TRUE : FALSE;
1222
                else if (!strcmp(attr->name, "no_select"))
1223
                        no_select = *attr->value == '1' ? TRUE : FALSE;
1224
                else if (!strcmp(attr->name, "collapsed"))
1225
                        collapsed = *attr->value == '1' ? TRUE : FALSE;
1226
                else if (!strcmp(attr->name, "threaded"))
1227
                        threaded =  *attr->value == '1' ? TRUE : FALSE;
1228
                else if (!strcmp(attr->name, "sort_key")) {
1229
                        if (!strcmp(attr->value, "none"))
1230
                                sort_key = SORT_BY_NONE;
1231
                        else if (!strcmp(attr->value, "number"))
1232
                                sort_key = SORT_BY_NUMBER;
1233
                        else if (!strcmp(attr->value, "size"))
1234
                                sort_key = SORT_BY_SIZE;
1235
                        else if (!strcmp(attr->value, "date"))
1236
                                sort_key = SORT_BY_DATE;
1237
                        else if (!strcmp(attr->value, "from"))
1238
                                sort_key = SORT_BY_FROM;
1239
                        else if (!strcmp(attr->value, "subject"))
1240
                                sort_key = SORT_BY_SUBJECT;
1241
                        else if (!strcmp(attr->value, "score"))
1242
                                sort_key = SORT_BY_SCORE;
1243
                        else if (!strcmp(attr->value, "label"))
1244
                                sort_key = SORT_BY_LABEL;
1245
                        else if (!strcmp(attr->value, "mark"))
1246
                                sort_key = SORT_BY_MARK;
1247
                        else if (!strcmp(attr->value, "unread"))
1248
                                sort_key = SORT_BY_UNREAD;
1249
                        else if (!strcmp(attr->value, "mime"))
1250
                                sort_key = SORT_BY_MIME;
1251
                        else if (!strcmp(attr->value, "to"))
1252
                                sort_key = SORT_BY_TO;
1253
                } else if (!strcmp(attr->name, "sort_type")) {
1254
                        if (!strcmp(attr->value, "ascending"))
1255
                                sort_type = SORT_ASCENDING;
1256
                        else
1257
                                sort_type = SORT_DESCENDING;
1258
                } else if (!strcmp(attr->name, "account_id")) {
1259
                        account = account_find_from_id(atoi(attr->value));
1260
                        if (!account) g_warning("account_id: %s not found\n",
1261
                                                attr->value);
1262
                } else if (!strcmp(attr->name, "account_apply_sub"))
1263
                        ac_apply_sub = *attr->value == '1' ? TRUE : FALSE;
1264
                else if (!strcmp(attr->name, "to"))
1265
                        auto_to = g_strdup(attr->value);
1266
                else if (!strcmp(attr->name, "use_auto_to_on_reply"))
1267
                        use_auto_to_on_reply =
1268
                                *attr->value == '1' ? TRUE : FALSE;
1269
                else if (!strcmp(attr->name, "cc"))
1270
                        auto_cc = g_strdup(attr->value);
1271
                else if (!strcmp(attr->name, "bcc"))
1272
                        auto_bcc = g_strdup(attr->value);
1273
                else if (!strcmp(attr->name, "replyto"))
1274
                        auto_replyto = g_strdup(attr->value);
1275
                else if (!strcmp(attr->name, "trim_summary_subject")) {
1276
                        trim_summary_subject =
1277
                                *attr->value == '1' ? TRUE : FALSE;
1278
                } else if (!strcmp(attr->name, "trim_compose_subject")) {
1279
                        trim_compose_subject =
1280
                                *attr->value = '1' ? TRUE : FALSE;
1281
                }
1282
        }
1283

    
1284
        item = folder_item_new(name, path);
1285
        item->stype = stype;
1286
        item->mtime = mtime;
1287
        item->new = new;
1288
        item->unread = unread;
1289
        item->total = total;
1290
        item->no_sub = no_sub;
1291
        item->no_select = no_select;
1292
        item->collapsed = collapsed;
1293
        item->threaded  = threaded;
1294
        item->sort_key  = sort_key;
1295
        item->sort_type = sort_type;
1296
        item->node = node;
1297
        item->parent = FOLDER_ITEM(node->parent->data);
1298
        item->folder = folder;
1299
        switch (stype) {
1300
        case F_INBOX:  folder->inbox  = item; break;
1301
        case F_OUTBOX: folder->outbox = item; break;
1302
        case F_DRAFT:  folder->draft  = item; break;
1303
        case F_QUEUE:  folder->queue  = item; break;
1304
        case F_TRASH:  folder->trash  = item; break;
1305
        default:       break;
1306
        }
1307
        item->account = account;
1308
        item->ac_apply_sub = ac_apply_sub;
1309
        item->auto_to = auto_to;
1310
        item->use_auto_to_on_reply = use_auto_to_on_reply;
1311
        item->auto_cc = auto_cc;
1312
        item->auto_bcc = auto_bcc;
1313
        item->auto_replyto = auto_replyto;
1314
        item->trim_summary_subject = trim_summary_subject;
1315
        item->trim_compose_subject = trim_compose_subject;
1316
        node->data = item;
1317
        xml_free_node(xmlnode);
1318

    
1319
        return FALSE;
1320
}
1321

    
1322
static gboolean folder_read_folder_func(GNode *node, gpointer data)
1323
{
1324
        Folder *folder;
1325
        FolderItem *item;
1326
        XMLNode *xmlnode;
1327
        GList *list;
1328
        FolderType type = F_UNKNOWN;
1329
        const gchar *name = NULL;
1330
        const gchar *path = NULL;
1331
        PrefsAccount *account = NULL;
1332
        gboolean collapsed = FALSE, threaded = TRUE, ac_apply_sub = FALSE;
1333

    
1334
        if (g_node_depth(node) != 2) return FALSE;
1335
        g_return_val_if_fail(node->data != NULL, FALSE);
1336

    
1337
        xmlnode = node->data;
1338
        if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
1339
                g_warning("tag name != \"folder\"\n");
1340
                return TRUE;
1341
        }
1342
        g_node_unlink(node);
1343
        list = xmlnode->tag->attr;
1344
        for (; list != NULL; list = list->next) {
1345
                XMLAttr *attr = list->data;
1346

    
1347
                if (!attr || !attr->name || !attr->value) continue;
1348
                if (!strcmp(attr->name, "type")) {
1349
                        if (!strcasecmp(attr->value, "mh"))
1350
                                type = F_MH;
1351
                        else if (!strcasecmp(attr->value, "mbox"))
1352
                                type = F_MBOX;
1353
                        else if (!strcasecmp(attr->value, "maildir"))
1354
                                type = F_MAILDIR;
1355
                        else if (!strcasecmp(attr->value, "imap"))
1356
                                type = F_IMAP;
1357
                        else if (!strcasecmp(attr->value, "news"))
1358
                                type = F_NEWS;
1359
                } else if (!strcmp(attr->name, "name"))
1360
                        name = attr->value;
1361
                else if (!strcmp(attr->name, "path"))
1362
                        path = attr->value;
1363
                else if (!strcmp(attr->name, "collapsed"))
1364
                        collapsed = *attr->value == '1' ? TRUE : FALSE;
1365
                else if (!strcmp(attr->name, "threaded"))
1366
                        threaded = *attr->value == '1' ? TRUE : FALSE;
1367
                else if (!strcmp(attr->name, "account_id")) {
1368
                        account = account_find_from_id(atoi(attr->value));
1369
                        if (!account) g_warning("account_id: %s not found\n",
1370
                                                attr->value);
1371
                } else if (!strcmp(attr->name, "account_apply_sub"))
1372
                        ac_apply_sub = *attr->value == '1' ? TRUE : FALSE;
1373
        }
1374

    
1375
        folder = folder_new(type, name, path);
1376
        g_return_val_if_fail(folder != NULL, FALSE);
1377
        folder->account = account;
1378
        if (account && (type == F_IMAP || type == F_NEWS))
1379
                account->folder = REMOTE_FOLDER(folder);
1380
        item = FOLDER_ITEM(folder->node->data);
1381
        node->data = item;
1382
        item->node = node;
1383
        g_node_destroy(folder->node);
1384
        folder->node = node;
1385
        folder_add(folder);
1386
        item->collapsed = collapsed;
1387
        item->threaded  = threaded;
1388
        item->account   = account;
1389
        item->ac_apply_sub = ac_apply_sub;
1390

    
1391
        g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1392
                        folder_build_tree, folder);
1393

    
1394
        return FALSE;
1395
}
1396

    
1397
static gchar *folder_get_list_path(void)
1398
{
1399
        static gchar *filename = NULL;
1400

    
1401
        if (!filename)
1402
                filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1403
                                        FOLDER_LIST, NULL);
1404

    
1405
        return filename;
1406
}
1407

    
1408
#define PUT_ESCAPE_STR(fp, attr, str)                        \
1409
{                                                        \
1410
        fputs(" " attr "=\"", fp);                        \
1411
        xml_file_put_escape_str(fp, str);                \
1412
        fputs("\"", fp);                                \
1413
}
1414

    
1415
static void folder_write_list_recursive(GNode *node, gpointer data)
1416
{
1417
        FILE *fp = (FILE *)data;
1418
        FolderItem *item;
1419
        gint i, depth;
1420
        static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1421
                                           "news", "unknown"};
1422
        static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1423
                                                 "draft", "queue", "trash"};
1424
        static gchar *sort_key_str[] = {"none", "number", "size", "date",
1425
                                        "from", "subject", "score", "label",
1426
                                        "mark", "unread", "mime", "to"};
1427

    
1428
        g_return_if_fail(node != NULL);
1429
        g_return_if_fail(fp != NULL);
1430

    
1431
        item = FOLDER_ITEM(node->data);
1432
        g_return_if_fail(item != NULL);
1433

    
1434
        depth = g_node_depth(node);
1435
        for (i = 0; i < depth; i++)
1436
                fputs("    ", fp);
1437
        if (depth == 1) {
1438
                Folder *folder = item->folder;
1439

    
1440
                fprintf(fp, "<folder type=\"%s\"",
1441
                        folder_type_str[FOLDER_TYPE(folder)]);
1442
                if (folder->name)
1443
                        PUT_ESCAPE_STR(fp, "name", folder->name);
1444
                if (FOLDER_TYPE(folder) == F_MH)
1445
                        PUT_ESCAPE_STR(fp, "path",
1446
                                       LOCAL_FOLDER(folder)->rootpath);
1447
                if (item->collapsed && node->children)
1448
                        fputs(" collapsed=\"1\"", fp);
1449
                if (folder->account)
1450
                        fprintf(fp, " account_id=\"%d\"",
1451
                                folder->account->account_id);
1452
                if (item->ac_apply_sub)
1453
                        fputs(" account_apply_sub=\"1\"", fp);
1454
        } else {
1455
                fprintf(fp, "<folderitem type=\"%s\"",
1456
                        folder_item_stype_str[item->stype]);
1457
                if (item->name)
1458
                        PUT_ESCAPE_STR(fp, "name", item->name);
1459
                if (item->path)
1460
                        PUT_ESCAPE_STR(fp, "path", item->path);
1461

    
1462
                if (item->no_sub)
1463
                        fputs(" no_sub=\"1\"", fp);
1464
                if (item->no_select)
1465
                        fputs(" no_select=\"1\"", fp);
1466
                if (item->collapsed && node->children)
1467
                        fputs(" collapsed=\"1\"", fp);
1468
                if (item->threaded)
1469
                        fputs(" threaded=\"1\"", fp);
1470
                else
1471
                        fputs(" threaded=\"0\"", fp);
1472

    
1473
                if (item->sort_key != SORT_BY_NONE) {
1474
                        fprintf(fp, " sort_key=\"%s\"",
1475
                                sort_key_str[item->sort_key]);
1476
                        if (item->sort_type == SORT_ASCENDING)
1477
                                fprintf(fp, " sort_type=\"ascending\"");
1478
                        else
1479
                                fprintf(fp, " sort_type=\"descending\"");
1480
                }
1481

    
1482
                fprintf(fp,
1483
                        " mtime=\"%lu\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1484
                        item->mtime, item->new, item->unread, item->total);
1485

    
1486
                if (item->account)
1487
                        fprintf(fp, " account_id=\"%d\"",
1488
                                item->account->account_id);
1489
                if (item->ac_apply_sub)
1490
                        fputs(" account_apply_sub=\"1\"", fp);
1491

    
1492
                if (item->auto_to)
1493
                        PUT_ESCAPE_STR(fp, "to", item->auto_to);
1494
                if (item->use_auto_to_on_reply)
1495
                        fputs(" use_auto_to_on_reply=\"1\"", fp);
1496
                if (item->auto_cc)
1497
                        PUT_ESCAPE_STR(fp, "cc", item->auto_cc);
1498
                if (item->auto_bcc)
1499
                        PUT_ESCAPE_STR(fp, "bcc", item->auto_bcc);
1500
                if (item->auto_replyto)
1501
                        PUT_ESCAPE_STR(fp, "replyto", item->auto_replyto);
1502

    
1503
                if (item->trim_summary_subject)
1504
                        fputs(" trim_summary_subject=\"1\"", fp);
1505
                if (item->trim_compose_subject)
1506
                        fputs(" trim_compose_subject=\"1\"", fp);
1507
        }
1508

    
1509
        if (node->children) {
1510
                GNode *child;
1511
                fputs(">\n", fp);
1512

    
1513
                child = node->children;
1514
                while (child) {
1515
                        GNode *cur;
1516

    
1517
                        cur = child;
1518
                        child = cur->next;
1519
                        folder_write_list_recursive(cur, data);
1520
                }
1521

    
1522
                for (i = 0; i < depth; i++)
1523
                        fputs("    ", fp);
1524
                fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1525
        } else
1526
                fputs(" />\n", fp);
1527
}
1528

    
1529
#undef PUT_ESCAPE_STR