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