Statistics
| Revision:

root / libsylph / folder.c @ 2247

History | View | Annotate | Download (42.2 kB)

1
/*
2
 * LibSylph -- E-Mail client library
3
 * Copyright (C) 1999-2009 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_add_msg_msginfo(FolderItem *dest, MsgInfo *msginfo,
1147
                                 gboolean remove_source)
1148
{
1149
        Folder *folder;
1150
1151
        g_return_val_if_fail(dest != NULL, -1);
1152
        g_return_val_if_fail(msginfo != NULL, -1);
1153
        g_return_val_if_fail(msginfo->file_path != NULL, -1);
1154
        g_return_val_if_fail(dest->folder->klass->add_msg_msginfo != NULL, -1);
1155
1156
        folder = dest->folder;
1157
1158
        return folder->klass->add_msg_msginfo(folder, dest, msginfo,
1159
                                              remove_source);
1160
}
1161
1162
gint folder_item_add_msgs_msginfo(FolderItem *dest, GSList *msglist,
1163
                                  gboolean remove_source, gint *first)
1164
{
1165
        Folder *folder;
1166
1167
        g_return_val_if_fail(dest != NULL, -1);
1168
        g_return_val_if_fail(msglist != NULL, -1);
1169
        g_return_val_if_fail(dest->folder->klass->add_msgs_msginfo != NULL, -1);
1170
1171
        folder = dest->folder;
1172
1173
        return folder->klass->add_msgs_msginfo(folder, dest, msglist,
1174
                                               remove_source, first);
1175
}
1176
1177
gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
1178
{
1179
        Folder *folder;
1180
1181
        g_return_val_if_fail(dest != NULL, -1);
1182
        g_return_val_if_fail(msginfo != NULL, -1);
1183
        g_return_val_if_fail(dest->folder->klass->move_msg != NULL, -1);
1184
1185
        folder = dest->folder;
1186
1187
        return folder->klass->move_msg(folder, dest, msginfo);
1188
}
1189
1190
gint folder_item_move_msgs(FolderItem *dest, GSList *msglist)
1191
{
1192
        Folder *folder;
1193
1194
        g_return_val_if_fail(dest != NULL, -1);
1195
        g_return_val_if_fail(msglist != NULL, -1);
1196
        g_return_val_if_fail(dest->folder->klass->move_msgs != NULL, -1);
1197
1198
        folder = dest->folder;
1199
1200
        return folder->klass->move_msgs(folder, dest, msglist);
1201
}
1202
1203
gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
1204
{
1205
        Folder *folder;
1206
1207
        g_return_val_if_fail(dest != NULL, -1);
1208
        g_return_val_if_fail(msginfo != NULL, -1);
1209
        g_return_val_if_fail(dest->folder->klass->copy_msg != NULL, -1);
1210
1211
        folder = dest->folder;
1212
1213
        return folder->klass->copy_msg(folder, dest, msginfo);
1214
}
1215
1216
gint folder_item_copy_msgs(FolderItem *dest, GSList *msglist)
1217
{
1218
        Folder *folder;
1219
1220
        g_return_val_if_fail(dest != NULL, -1);
1221
        g_return_val_if_fail(msglist != NULL, -1);
1222
        g_return_val_if_fail(dest->folder->klass->copy_msgs != NULL, -1);
1223
1224
        folder = dest->folder;
1225
1226
        return folder->klass->copy_msgs(folder, dest, msglist);
1227
}
1228
1229
gint folder_item_remove_msg(FolderItem *item, MsgInfo *msginfo)
1230
{
1231
        Folder *folder;
1232
1233
        g_return_val_if_fail(item != NULL, -1);
1234
        g_return_val_if_fail(item->folder->klass->remove_msg != NULL, -1);
1235
1236
        folder = item->folder;
1237
1238
        return folder->klass->remove_msg(folder, item, msginfo);
1239
}
1240
1241
gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
1242
{
1243
        Folder *folder;
1244
        gint ret = 0;
1245
1246
        g_return_val_if_fail(item != NULL, -1);
1247
1248
        folder = item->folder;
1249
        if (folder->klass->remove_msgs) {
1250
                return folder->klass->remove_msgs(folder, item, msglist);
1251
        }
1252
1253
        while (msglist != NULL) {
1254
                MsgInfo *msginfo = (MsgInfo *)msglist->data;
1255
1256
                ret = folder_item_remove_msg(item, msginfo);
1257
                if (ret != 0) break;
1258
                msglist = msglist->next;
1259
        }
1260
1261
        return ret;
1262
}
1263
1264
gint folder_item_remove_all_msg(FolderItem *item)
1265
{
1266
        Folder *folder;
1267
1268
        g_return_val_if_fail(item != NULL, -1);
1269
        g_return_val_if_fail(item->folder->klass->remove_all_msg != NULL, -1);
1270
1271
        folder = item->folder;
1272
1273
        return folder->klass->remove_all_msg(folder, item);
1274
}
1275
1276
gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
1277
{
1278
        Folder *folder;
1279
1280
        g_return_val_if_fail(item != NULL, FALSE);
1281
        g_return_val_if_fail(item->folder->klass->is_msg_changed != NULL,
1282
                             FALSE);
1283
1284
        folder = item->folder;
1285
        return folder->klass->is_msg_changed(folder, item, msginfo);
1286
}
1287
1288
gint folder_item_close(FolderItem *item)
1289
{
1290
        Folder *folder;
1291
1292
        g_return_val_if_fail(item != NULL, -1);
1293
1294
        item->opened = FALSE;
1295
        folder = item->folder;
1296
        return folder->klass->close(folder, item);
1297
}
1298
1299
gchar *folder_item_get_cache_file(FolderItem *item)
1300
{
1301
        gchar *path;
1302
        gchar *file;
1303
1304
        g_return_val_if_fail(item != NULL, NULL);
1305
        g_return_val_if_fail(item->path != NULL, NULL);
1306
1307
        path = folder_item_get_path(item);
1308
        g_return_val_if_fail(path != NULL, NULL);
1309
        if (!is_dir_exist(path))
1310
                make_dir_hier(path);
1311
        file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
1312
        g_free(path);
1313
1314
        return file;
1315
}
1316
1317
gchar *folder_item_get_mark_file(FolderItem *item)
1318
{
1319
        gchar *path;
1320
        gchar *file;
1321
1322
        g_return_val_if_fail(item != NULL, NULL);
1323
        g_return_val_if_fail(item->path != NULL, NULL);
1324
1325
        path = folder_item_get_path(item);
1326
        g_return_val_if_fail(path != NULL, NULL);
1327
        if (!is_dir_exist(path))
1328
                make_dir_hier(path);
1329
        file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
1330
        g_free(path);
1331
1332
        return file;
1333
}
1334
1335
static gboolean folder_build_tree(GNode *node, gpointer data)
1336
{
1337
        Folder *folder = FOLDER(data);
1338
        FolderItem *item;
1339
        XMLNode *xmlnode;
1340
        GList *list;
1341
        SpecialFolderItemType stype = F_NORMAL;
1342
        const gchar *name = NULL;
1343
        const gchar *path = NULL;
1344
        PrefsAccount *account = NULL;
1345
        gboolean no_sub = FALSE, no_select = FALSE, collapsed = FALSE,
1346
                 threaded = TRUE, ac_apply_sub = FALSE;
1347
        FolderSortKey sort_key = SORT_BY_NONE;
1348
        FolderSortType sort_type = SORT_ASCENDING;
1349
        gboolean qsearch_cond_type = 0;
1350
        gint new = 0, unread = 0, total = 0;
1351
        time_t mtime = 0;
1352
        gboolean use_auto_to_on_reply = FALSE;
1353
        gchar *auto_to = NULL, *auto_cc = NULL, *auto_bcc = NULL,
1354
              *auto_replyto = NULL;
1355
        gboolean trim_summary_subject = FALSE, trim_compose_subject = FALSE;
1356
1357
        g_return_val_if_fail(node->data != NULL, FALSE);
1358
        if (!node->parent) return FALSE;
1359
1360
        xmlnode = node->data;
1361
        if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
1362
                g_warning("tag name != \"folderitem\"\n");
1363
                return FALSE;
1364
        }
1365
1366
        list = xmlnode->tag->attr;
1367
        for (; list != NULL; list = list->next) {
1368
                XMLAttr *attr = list->data;
1369
1370
                if (!attr || !attr->name || !attr->value) continue;
1371
                if (!strcmp(attr->name, "type")) {
1372
                        if (!g_ascii_strcasecmp(attr->value, "normal"))
1373
                                stype = F_NORMAL;
1374
                        else if (!g_ascii_strcasecmp(attr->value, "inbox"))
1375
                                stype = F_INBOX;
1376
                        else if (!g_ascii_strcasecmp(attr->value, "outbox"))
1377
                                stype = F_OUTBOX;
1378
                        else if (!g_ascii_strcasecmp(attr->value, "draft"))
1379
                                stype = F_DRAFT;
1380
                        else if (!g_ascii_strcasecmp(attr->value, "queue"))
1381
                                stype = F_QUEUE;
1382
                        else if (!g_ascii_strcasecmp(attr->value, "trash"))
1383
                                stype = F_TRASH;
1384
#if 0
1385
                        else if (!g_ascii_strcasecmp(attr->value, "junk"))
1386
                                stype = F_JUNK;
1387
#endif
1388
                        else if (!g_ascii_strcasecmp(attr->value, "virtual"))
1389
                                stype = F_VIRTUAL;
1390
                } else if (!strcmp(attr->name, "name"))
1391
                        name = attr->value;
1392
                else if (!strcmp(attr->name, "path")) {
1393
#ifdef G_OS_WIN32
1394
                        subst_char(attr->value, G_DIR_SEPARATOR, '/');
1395
#endif
1396
                        path = attr->value;
1397
                } else if (!strcmp(attr->name, "mtime"))
1398
                        mtime = strtoul(attr->value, NULL, 10);
1399
                else if (!strcmp(attr->name, "new"))
1400
                        new = atoi(attr->value);
1401
                else if (!strcmp(attr->name, "unread"))
1402
                        unread = atoi(attr->value);
1403
                else if (!strcmp(attr->name, "total"))
1404
                        total = atoi(attr->value);
1405
                else if (!strcmp(attr->name, "no_sub"))
1406
                        no_sub = *attr->value == '1' ? TRUE : FALSE;
1407
                else if (!strcmp(attr->name, "no_select"))
1408
                        no_select = *attr->value == '1' ? TRUE : FALSE;
1409
                else if (!strcmp(attr->name, "collapsed"))
1410
                        collapsed = *attr->value == '1' ? TRUE : FALSE;
1411
                else if (!strcmp(attr->name, "threaded"))
1412
                        threaded =  *attr->value == '1' ? TRUE : FALSE;
1413
                else if (!strcmp(attr->name, "sort_key")) {
1414
                        if (!strcmp(attr->value, "none"))
1415
                                sort_key = SORT_BY_NONE;
1416
                        else if (!strcmp(attr->value, "number"))
1417
                                sort_key = SORT_BY_NUMBER;
1418
                        else if (!strcmp(attr->value, "size"))
1419
                                sort_key = SORT_BY_SIZE;
1420
                        else if (!strcmp(attr->value, "date"))
1421
                                sort_key = SORT_BY_DATE;
1422
                        else if (!strcmp(attr->value, "thread-date"))
1423
                                sort_key = SORT_BY_TDATE;
1424
                        else if (!strcmp(attr->value, "from"))
1425
                                sort_key = SORT_BY_FROM;
1426
                        else if (!strcmp(attr->value, "subject"))
1427
                                sort_key = SORT_BY_SUBJECT;
1428
                        else if (!strcmp(attr->value, "score"))
1429
                                sort_key = SORT_BY_SCORE;
1430
                        else if (!strcmp(attr->value, "label"))
1431
                                sort_key = SORT_BY_LABEL;
1432
                        else if (!strcmp(attr->value, "mark"))
1433
                                sort_key = SORT_BY_MARK;
1434
                        else if (!strcmp(attr->value, "unread"))
1435
                                sort_key = SORT_BY_UNREAD;
1436
                        else if (!strcmp(attr->value, "mime"))
1437
                                sort_key = SORT_BY_MIME;
1438
                        else if (!strcmp(attr->value, "to"))
1439
                                sort_key = SORT_BY_TO;
1440
                } else if (!strcmp(attr->name, "sort_type")) {
1441
                        if (!strcmp(attr->value, "ascending"))
1442
                                sort_type = SORT_ASCENDING;
1443
                        else
1444
                                sort_type = SORT_DESCENDING;
1445
                } else if (!strcmp(attr->name, "qsearch_cond")) {
1446
                        if (!strcmp(attr->value, "all"))
1447
                                qsearch_cond_type = 0;
1448
                        else if (!strcmp(attr->value, "unread"))
1449
                                qsearch_cond_type = 1;
1450
                        else if (!strcmp(attr->value, "mark"))
1451
                                qsearch_cond_type = 2;
1452
                        else if (!strcmp(attr->value, "clabel"))
1453
                                qsearch_cond_type = 3;
1454
                        else if (!strcmp(attr->value, "mime"))
1455
                                qsearch_cond_type = 4;
1456
                        else if (!strcmp(attr->value, "w1day"))
1457
                                qsearch_cond_type = 5;
1458
                        else if (!strcmp(attr->value, "last5"))
1459
                                qsearch_cond_type = 6;
1460
                } else if (!strcmp(attr->name, "account_id")) {
1461
                        account = account_find_from_id(atoi(attr->value));
1462
                        if (!account) g_warning("account_id: %s not found\n",
1463
                                                attr->value);
1464
                } else if (!strcmp(attr->name, "account_apply_sub"))
1465
                        ac_apply_sub = *attr->value == '1' ? TRUE : FALSE;
1466
                else if (!strcmp(attr->name, "to"))
1467
                        auto_to = g_strdup(attr->value);
1468
                else if (!strcmp(attr->name, "use_auto_to_on_reply"))
1469
                        use_auto_to_on_reply =
1470
                                *attr->value == '1' ? TRUE : FALSE;
1471
                else if (!strcmp(attr->name, "cc"))
1472
                        auto_cc = g_strdup(attr->value);
1473
                else if (!strcmp(attr->name, "bcc"))
1474
                        auto_bcc = g_strdup(attr->value);
1475
                else if (!strcmp(attr->name, "replyto"))
1476
                        auto_replyto = g_strdup(attr->value);
1477
                else if (!strcmp(attr->name, "trim_summary_subject")) {
1478
                        trim_summary_subject =
1479
                                *attr->value == '1' ? TRUE : FALSE;
1480
                } else if (!strcmp(attr->name, "trim_compose_subject")) {
1481
                        trim_compose_subject =
1482
                                *attr->value = '1' ? TRUE : FALSE;
1483
                }
1484
        }
1485
1486
        item = folder_item_new(name, path);
1487
        item->stype = stype;
1488
        item->mtime = mtime;
1489
        item->new = new;
1490
        item->unread = unread;
1491
        item->total = total;
1492
        item->no_sub = no_sub;
1493
        item->no_select = no_select;
1494
        item->collapsed = collapsed;
1495
        item->threaded  = threaded;
1496
        item->sort_key  = sort_key;
1497
        item->sort_type = sort_type;
1498
        item->node = node;
1499
        item->parent = FOLDER_ITEM(node->parent->data);
1500
        item->folder = folder;
1501
        switch (stype) {
1502
        case F_INBOX:  folder->inbox  = item; break;
1503
        case F_OUTBOX: folder->outbox = item; break;
1504
        case F_DRAFT:  folder->draft  = item; break;
1505
        case F_QUEUE:  folder->queue  = item; break;
1506
        case F_TRASH:  folder->trash  = item; break;
1507
        /* case F_JUNK:   folder->junk   = item; break; */
1508
        default:       break;
1509
        }
1510
        item->account = account;
1511
        item->ac_apply_sub = ac_apply_sub;
1512
        item->auto_to = auto_to;
1513
        item->use_auto_to_on_reply = use_auto_to_on_reply;
1514
        item->auto_cc = auto_cc;
1515
        item->auto_bcc = auto_bcc;
1516
        item->auto_replyto = auto_replyto;
1517
        item->trim_summary_subject = trim_summary_subject;
1518
        item->trim_compose_subject = trim_compose_subject;
1519
        item->qsearch_cond_type = qsearch_cond_type;
1520
        node->data = item;
1521
        xml_free_node(xmlnode);
1522
1523
        return FALSE;
1524
}
1525
1526
static gboolean folder_read_folder_func(GNode *node, gpointer data)
1527
{
1528
        Folder *folder;
1529
        FolderItem *item;
1530
        XMLNode *xmlnode;
1531
        GList *list;
1532
        FolderType type = F_UNKNOWN;
1533
        const gchar *name = NULL;
1534
        const gchar *path = NULL;
1535
        PrefsAccount *account = NULL;
1536
        gboolean collapsed = FALSE, threaded = TRUE, ac_apply_sub = FALSE;
1537
1538
        if (g_node_depth(node) != 2) return FALSE;
1539
        g_return_val_if_fail(node->data != NULL, FALSE);
1540
1541
        xmlnode = node->data;
1542
        if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
1543
                g_warning("tag name != \"folder\"\n");
1544
                return TRUE;
1545
        }
1546
        g_node_unlink(node);
1547
        list = xmlnode->tag->attr;
1548
        for (; list != NULL; list = list->next) {
1549
                XMLAttr *attr = list->data;
1550
1551
                if (!attr || !attr->name || !attr->value) continue;
1552
                if (!strcmp(attr->name, "type")) {
1553
                        if (!g_ascii_strcasecmp(attr->value, "mh"))
1554
                                type = F_MH;
1555
                        else if (!g_ascii_strcasecmp(attr->value, "mbox"))
1556
                                type = F_MBOX;
1557
                        else if (!g_ascii_strcasecmp(attr->value, "maildir"))
1558
                                type = F_MAILDIR;
1559
                        else if (!g_ascii_strcasecmp(attr->value, "imap"))
1560
                                type = F_IMAP;
1561
                        else if (!g_ascii_strcasecmp(attr->value, "news"))
1562
                                type = F_NEWS;
1563
                } else if (!strcmp(attr->name, "name"))
1564
                        name = attr->value;
1565
                else if (!strcmp(attr->name, "path"))
1566
                        path = attr->value;
1567
                else if (!strcmp(attr->name, "collapsed"))
1568
                        collapsed = *attr->value == '1' ? TRUE : FALSE;
1569
                else if (!strcmp(attr->name, "threaded"))
1570
                        threaded = *attr->value == '1' ? TRUE : FALSE;
1571
                else if (!strcmp(attr->name, "account_id")) {
1572
                        account = account_find_from_id(atoi(attr->value));
1573
                        if (!account) g_warning("account_id: %s not found\n",
1574
                                                attr->value);
1575
                } else if (!strcmp(attr->name, "account_apply_sub"))
1576
                        ac_apply_sub = *attr->value == '1' ? TRUE : FALSE;
1577
        }
1578
1579
        folder = folder_new(type, name, path);
1580
        g_return_val_if_fail(folder != NULL, FALSE);
1581
        if (account && FOLDER_IS_REMOTE(folder)) {
1582
                folder->account = account;
1583
                account->folder = REMOTE_FOLDER(folder);
1584
        }
1585
        if (account && FOLDER_IS_LOCAL(folder))
1586
                ac_apply_sub = TRUE;
1587
        item = FOLDER_ITEM(folder->node->data);
1588
        node->data = item;
1589
        item->node = node;
1590
        g_node_destroy(folder->node);
1591
        folder->node = node;
1592
        folder_add(folder);
1593
        item->collapsed = collapsed;
1594
        item->threaded  = threaded;
1595
        item->account   = account;
1596
        item->ac_apply_sub = ac_apply_sub;
1597
1598
        g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1599
                        folder_build_tree, folder);
1600
1601
        return FALSE;
1602
}
1603
1604
static gchar *folder_get_list_path(void)
1605
{
1606
        static gchar *filename = NULL;
1607
1608
        if (!filename)
1609
                filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1610
                                        FOLDER_LIST, NULL);
1611
1612
        return filename;
1613
}
1614
1615
#define PUT_ESCAPE_STR(fp, attr, str)                        \
1616
{                                                        \
1617
        fputs(" " attr "=\"", fp);                        \
1618
        xml_file_put_escape_str(fp, str);                \
1619
        fputs("\"", fp);                                \
1620
}
1621
1622
static void folder_write_list_recursive(GNode *node, gpointer data)
1623
{
1624
        FILE *fp = (FILE *)data;
1625
        FolderItem *item;
1626
        gint i, depth;
1627
        static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1628
                                           "news", "unknown"};
1629
        static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1630
                                                 "draft", "queue", "trash",
1631
                                                 "junk", "virtual"};
1632
        static gchar *sort_key_str[] = {"none", "number", "size", "date",
1633
                                        "thread-date",
1634
                                        "from", "subject", "score", "label",
1635
                                        "mark", "unread", "mime", "to"};
1636
        static gchar *qsearch_cond_str[] = {"all", "unread", "mark", "clabel",
1637
                                            "mime", "w1day", "last5"};
1638
1639
        g_return_if_fail(node != NULL);
1640
        g_return_if_fail(fp != NULL);
1641
1642
        item = FOLDER_ITEM(node->data);
1643
        g_return_if_fail(item != NULL);
1644
1645
        depth = g_node_depth(node);
1646
        for (i = 0; i < depth; i++)
1647
                fputs("    ", fp);
1648
        if (depth == 1) {
1649
                Folder *folder = item->folder;
1650
1651
                fprintf(fp, "<folder type=\"%s\"",
1652
                        folder_type_str[FOLDER_TYPE(folder)]);
1653
                if (folder->name)
1654
                        PUT_ESCAPE_STR(fp, "name", folder->name);
1655
                if (FOLDER_TYPE(folder) == F_MH)
1656
                        PUT_ESCAPE_STR(fp, "path",
1657
                                       LOCAL_FOLDER(folder)->rootpath);
1658
                if (item->collapsed && node->children)
1659
                        fputs(" collapsed=\"1\"", fp);
1660
                if (folder->account)
1661
                        fprintf(fp, " account_id=\"%d\"",
1662
                                folder->account->account_id);
1663
                else if (item->account)
1664
                        fprintf(fp, " account_id=\"%d\"",
1665
                                item->account->account_id);
1666
                if (item->ac_apply_sub)
1667
                        fputs(" account_apply_sub=\"1\"", fp);
1668
        } else {
1669
                fprintf(fp, "<folderitem type=\"%s\"",
1670
                        folder_item_stype_str[item->stype]);
1671
                if (item->name)
1672
                        PUT_ESCAPE_STR(fp, "name", item->name);
1673
                if (item->path)
1674
                        PUT_ESCAPE_STR(fp, "path", item->path);
1675
1676
                if (item->no_sub)
1677
                        fputs(" no_sub=\"1\"", fp);
1678
                if (item->no_select)
1679
                        fputs(" no_select=\"1\"", fp);
1680
                if (item->collapsed && node->children)
1681
                        fputs(" collapsed=\"1\"", fp);
1682
                if (item->threaded)
1683
                        fputs(" threaded=\"1\"", fp);
1684
                else
1685
                        fputs(" threaded=\"0\"", fp);
1686
1687
                if (item->sort_key != SORT_BY_NONE) {
1688
                        fprintf(fp, " sort_key=\"%s\"",
1689
                                sort_key_str[item->sort_key]);
1690
                        if (item->sort_type == SORT_ASCENDING)
1691
                                fprintf(fp, " sort_type=\"ascending\"");
1692
                        else
1693
                                fprintf(fp, " sort_type=\"descending\"");
1694
                }
1695
                if (item->qsearch_cond_type > 0 &&
1696
                    item->qsearch_cond_type < 7) {
1697
                        fprintf(fp, " qsearch_cond=\"%s\"",
1698
                                qsearch_cond_str[item->qsearch_cond_type]);
1699
                }
1700
1701
                fprintf(fp,
1702
                        " mtime=\"%lu\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1703
                        item->mtime, item->new, item->unread, item->total);
1704
1705
                if (item->account)
1706
                        fprintf(fp, " account_id=\"%d\"",
1707
                                item->account->account_id);
1708
                if (item->ac_apply_sub)
1709
                        fputs(" account_apply_sub=\"1\"", fp);
1710
1711
                if (item->auto_to)
1712
                        PUT_ESCAPE_STR(fp, "to", item->auto_to);
1713
                if (item->use_auto_to_on_reply)
1714
                        fputs(" use_auto_to_on_reply=\"1\"", fp);
1715
                if (item->auto_cc)
1716
                        PUT_ESCAPE_STR(fp, "cc", item->auto_cc);
1717
                if (item->auto_bcc)
1718
                        PUT_ESCAPE_STR(fp, "bcc", item->auto_bcc);
1719
                if (item->auto_replyto)
1720
                        PUT_ESCAPE_STR(fp, "replyto", item->auto_replyto);
1721
1722
                if (item->trim_summary_subject)
1723
                        fputs(" trim_summary_subject=\"1\"", fp);
1724
                if (item->trim_compose_subject)
1725
                        fputs(" trim_compose_subject=\"1\"", fp);
1726
        }
1727
1728
        if (node->children) {
1729
                GNode *child;
1730
                fputs(">\n", fp);
1731
1732
                child = node->children;
1733
                while (child) {
1734
                        GNode *cur;
1735
1736
                        cur = child;
1737
                        child = cur->next;
1738
                        folder_write_list_recursive(cur, data);
1739
                }
1740
1741
                for (i = 0; i < depth; i++)
1742
                        fputs("    ", fp);
1743
                fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1744
        } else
1745
                fputs(" />\n", fp);
1746
}
1747
1748
#undef PUT_ESCAPE_STR