Statistics
| Revision:

root / libsylph / folder.c @ 3386

History | View | Annotate | Download (47.6 KB)

1
/*
2
 * LibSylph -- E-Mail client library
3
 * Copyright (C) 1999-2012 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
typedef struct _FolderPrivData FolderPrivData;
47

    
48
struct _FolderPrivData {
49
        Folder *folder;
50
        FolderItem *junk;
51

    
52
        FolderUIFunc2 ui_func2;
53
        gpointer ui_func2_data;
54

    
55
        gpointer data;
56
};
57

    
58
static GList *folder_list = NULL;
59
static GList *folder_priv_list = NULL;
60

    
61
static void folder_init                (Folder                *folder,
62
                                 const gchar        *name);
63

    
64
static FolderPrivData *folder_get_priv        (Folder                *folder);
65

    
66
static gboolean folder_read_folder_func        (GNode                *node,
67
                                         gpointer         data);
68
static gchar *folder_get_list_path        (void);
69
static void folder_write_list_recursive        (GNode                *node,
70
                                         gpointer         data);
71

    
72

    
73
Folder *folder_new(FolderType type, const gchar *name, const gchar *path)
74
{
75
        Folder *folder = NULL;
76

    
77
        name = name ? name : path;
78
        switch (type) {
79
        case F_MH:
80
                folder = mh_get_class()->folder_new(name, path);
81
                break;
82
        case F_IMAP:
83
                folder = imap_get_class()->folder_new(name, path);
84
                break;
85
        case F_NEWS:
86
                folder = news_get_class()->folder_new(name, path);
87
                break;
88
        default:
89
                return NULL;
90
        }
91

    
92
        return folder;
93
}
94

    
95
static void folder_init(Folder *folder, const gchar *name)
96
{
97
        FolderItem *item;
98

    
99
        g_return_if_fail(folder != NULL);
100

    
101
        folder_set_name(folder, name);
102
        folder->account = NULL;
103
        folder->inbox = NULL;
104
        folder->outbox = NULL;
105
        folder->draft = NULL;
106
        folder->queue = NULL;
107
        folder->trash = NULL;
108
        folder->ui_func = NULL;
109
        folder->ui_func_data = NULL;
110
        item = folder_item_new(name, NULL);
111
        item->folder = folder;
112
        folder->node = item->node = g_node_new(item);
113
        folder->data = NULL;
114
}
115

    
116
void folder_local_folder_init(Folder *folder, const gchar *name,
117
                              const gchar *path)
118
{
119
        folder_init(folder, name);
120
        LOCAL_FOLDER(folder)->rootpath = g_strdup(path);
121
}
122

    
123
void folder_remote_folder_init(Folder *folder, const gchar *name,
124
                               const gchar *path)
125
{
126
        folder_init(folder, name);
127
        REMOTE_FOLDER(folder)->session = NULL;
128
        REMOTE_FOLDER(folder)->remove_cache_on_destroy = TRUE;
129
}
130

    
131
void folder_destroy(Folder *folder)
132
{
133
        FolderPrivData *priv;
134

    
135
        g_return_if_fail(folder != NULL);
136
        g_return_if_fail(folder->klass->destroy != NULL);
137

    
138
        debug_print("folder_destroy: destroying Folder (%p)\n", folder);
139

    
140
        folder->klass->destroy(folder);
141

    
142
        folder_list = g_list_remove(folder_list, folder);
143

    
144
        folder_tree_destroy(folder);
145

    
146
        priv = folder_get_priv(folder);
147
        folder_priv_list = g_list_remove(folder_priv_list, priv);
148
        g_free(priv);
149

    
150
        g_free(folder->name);
151
        g_free(folder);
152
}
153

    
154
void folder_local_folder_destroy(LocalFolder *lfolder)
155
{
156
        g_return_if_fail(lfolder != NULL);
157

    
158
        g_free(lfolder->rootpath);
159
}
160

    
161
void folder_remote_folder_destroy(RemoteFolder *rfolder)
162
{
163
        g_return_if_fail(rfolder != NULL);
164

    
165
        if (rfolder->session)
166
                session_destroy(rfolder->session);
167
}
168

    
169
gint folder_remote_folder_destroy_all_sessions(void)
170
{
171
        GList *list;
172
        Folder *folder;
173
        RemoteFolder *rfolder;
174

    
175
        for (list = folder_list; list != NULL; list = list->next) {
176
                folder = FOLDER(list->data);
177
                if (FOLDER_IS_REMOTE(folder)) {
178
                        rfolder = REMOTE_FOLDER(folder);
179
                        if (rfolder->session &&
180
                            !folder_remote_folder_is_session_active(rfolder)) {
181
                                session_destroy(rfolder->session);
182
                                rfolder->session = NULL;
183
                        }
184
                }
185
        }
186

    
187
        return 0;
188
}
189

    
190
gboolean folder_remote_folder_is_session_active(RemoteFolder *rfolder)
191
{
192
        g_return_val_if_fail(rfolder != NULL, FALSE);
193

    
194
        if (FOLDER_TYPE(rfolder) == F_IMAP)
195
                return imap_is_session_active(IMAP_FOLDER(rfolder));
196

    
197
        return FALSE;
198
}
199

    
200
gboolean folder_remote_folder_active_session_exist(void)
201
{
202
        GList *list;
203
        Folder *folder;
204
        RemoteFolder *rfolder;
205

    
206
        for (list = folder_list; list != NULL; list = list->next) {
207
                folder = FOLDER(list->data);
208
                if (FOLDER_IS_REMOTE(folder)) {
209
                        rfolder = REMOTE_FOLDER(folder);
210
                        if (folder_remote_folder_is_session_active(rfolder))
211
                                return TRUE;
212
                }
213
        }
214

    
215
        return FALSE;
216
}
217

    
218
gint folder_scan_tree(Folder *folder)
219
{
220
        g_return_val_if_fail(folder != NULL, -1);
221
        g_return_val_if_fail(folder->klass->scan_tree != NULL, -1);
222

    
223
        return folder->klass->scan_tree(folder);
224
}
225

    
226
gint folder_create_tree(Folder *folder)
227
{
228
        g_return_val_if_fail(folder != NULL, -1);
229
        g_return_val_if_fail(folder->klass->create_tree != NULL, -1);
230

    
231
        return folder->klass->create_tree(folder);
232
}
233

    
234
FolderItem *folder_item_new(const gchar *name, const gchar *path)
235
{
236
        FolderItem *item;
237

    
238
        item = g_new0(FolderItem, 1);
239

    
240
        item->stype = F_NORMAL;
241
        item->name = g_strdup(name);
242
        item->path = g_strdup(path);
243
        item->mtime = 0;
244
        item->new = 0;
245
        item->unread = 0;
246
        item->total = 0;
247
        item->unmarked_num = 0;
248
        item->last_num = -1;
249
        item->no_sub = FALSE;
250
        item->no_select = FALSE;
251
        item->collapsed = FALSE;
252
        item->threaded = TRUE;
253
        item->opened = FALSE;
254
        item->updated = FALSE;
255
        item->cache_dirty = FALSE;
256
        item->mark_dirty = FALSE;
257
        item->node = NULL;
258
        item->parent = NULL;
259
        item->folder = NULL;
260
        item->account = NULL;
261
        item->ac_apply_sub = FALSE;
262
        item->auto_to = NULL;
263
        item->use_auto_to_on_reply = FALSE;
264
        item->auto_cc = NULL;
265
        item->auto_bcc = NULL;
266
        item->auto_replyto = NULL;
267
        item->mark_queue = NULL;
268
        item->last_selected = 0;
269
        item->qsearch_cond_type = 0;
270
        item->data = NULL;
271

    
272
        return item;
273
}
274

    
275
void folder_item_append(FolderItem *parent, FolderItem *item)
276
{
277
        g_return_if_fail(parent != NULL);
278
        g_return_if_fail(parent->folder != NULL);
279
        g_return_if_fail(parent->node != NULL);
280
        g_return_if_fail(item != NULL);
281

    
282
        item->parent = parent;
283
        item->folder = parent->folder;
284
        item->node = g_node_append_data(parent->node, item);
285
}
286

    
287
FolderItem *folder_item_copy(FolderItem *item)
288
{
289
        FolderItem *new_item;
290

    
291
        new_item = g_new0(FolderItem, 1);
292

    
293
        new_item->stype = item->stype;
294
        new_item->name = g_strdup(item->name);
295
        new_item->path = g_strdup(item->path);
296
        new_item->mtime = item->mtime;
297
        new_item->new = item->new;
298
        new_item->unread = item->unread;
299
        new_item->total = item->total;
300
        new_item->unmarked_num = item->unmarked_num;
301
        new_item->last_num = item->last_num;
302
        new_item->no_sub = item->no_sub;
303
        new_item->no_select = item->no_select;
304
        new_item->collapsed = item->collapsed;
305
        new_item->threaded = item->threaded;
306
        new_item->opened = item->opened;
307
        new_item->updated = item->updated;
308
        new_item->cache_dirty = item->cache_dirty;
309
        new_item->mark_dirty = item->mark_dirty;
310
        new_item->node = item->node;
311
        new_item->parent = item->parent;
312
        new_item->folder = item->folder;
313
        new_item->account = item->account;
314
        new_item->ac_apply_sub = item->ac_apply_sub;
315
        new_item->auto_to = g_strdup(item->auto_to);
316
        new_item->use_auto_to_on_reply = item->use_auto_to_on_reply;
317
        new_item->auto_cc = g_strdup(item->auto_cc);
318
        new_item->auto_bcc = g_strdup(item->auto_bcc);
319
        new_item->auto_replyto = g_strdup(item->auto_replyto);
320
        new_item->mark_queue = item->mark_queue;
321
        new_item->last_selected = item->last_selected;
322
        new_item->qsearch_cond_type = item->qsearch_cond_type;
323
        new_item->data = item->data;
324

    
325
        return new_item;
326
}
327

    
328
static gboolean folder_item_remove_func(GNode *node, gpointer data)
329
{
330
        FolderItem *item = FOLDER_ITEM(node->data);
331

    
332
        folder_item_destroy(item);
333
        return FALSE;
334
}
335

    
336
void folder_item_remove(FolderItem *item)
337
{
338
        GNode *node;
339

    
340
        g_return_if_fail(item != NULL);
341
        g_return_if_fail(item->folder != NULL);
342
        g_return_if_fail(item->node != NULL);
343

    
344
        node = item->node;
345

    
346
        if (item->folder->node == node)
347
                item->folder->node = NULL;
348

    
349
        g_node_traverse(node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
350
                        folder_item_remove_func, NULL);
351
        g_node_destroy(node);
352
}
353

    
354
void folder_item_remove_children(FolderItem *item)
355
{
356
        GNode *node, *next;
357

    
358
        g_return_if_fail(item != NULL);
359
        g_return_if_fail(item->folder != NULL);
360
        g_return_if_fail(item->node != NULL);
361

    
362
        node = item->node->children;
363
        while (node != NULL) {
364
                next = node->next;
365
                folder_item_remove(FOLDER_ITEM(node->data));
366
                node = next;
367
        }
368
}
369

    
370
void folder_item_destroy(FolderItem *item)
371
{
372
        Folder *folder;
373

    
374
        g_return_if_fail(item != NULL);
375

    
376
        folder = item->folder;
377
        if (folder) {
378
                if (folder->inbox == item)
379
                        folder->inbox = NULL;
380
                else if (folder->outbox == item)
381
                        folder->outbox = NULL;
382
                else if (folder->draft == item)
383
                        folder->draft = NULL;
384
                else if (folder->queue == item)
385
                        folder->queue = NULL;
386
                else if (folder->trash == item)
387
                        folder->trash = NULL;
388
                else if (folder_get_junk(folder) == item)
389
                        folder_set_junk(folder, NULL);
390
        }
391

    
392
        g_free(item->name);
393
        g_free(item->path);
394
        g_free(item->auto_to);
395
        g_free(item->auto_cc);
396
        g_free(item->auto_bcc);
397
        g_free(item->auto_replyto);
398
        g_free(item);
399
}
400

    
401
gint folder_item_compare(FolderItem *item_a, FolderItem *item_b)
402
{
403
        gint ret;
404
        gchar *str_a, *str_b;
405

    
406
        if (!item_a || !item_b)
407
                return 0;
408
        if (!item_a->parent || !item_b->parent)
409
                return 0;
410
        if (!item_a->name || !item_b->name)
411
                return 0;
412

    
413
        /* if both a and b are special folders, sort them according to
414
         * their types (which is in-order). Note that this assumes that
415
         * there are no multiple folders of a special type. As a special
416
         * case, two virtual folders are compared like normal ones. */
417
        if (item_a->stype != F_NORMAL && item_b->stype != F_NORMAL &&
418
            (item_a->stype != F_VIRTUAL || item_b->stype != F_VIRTUAL))
419
                return item_a->stype - item_b->stype;
420

    
421
         /* if b is normal folder, and a is not, b is smaller (ends up
422
          * lower in the list) */
423
        if (item_a->stype != F_NORMAL && item_b->stype == F_NORMAL)
424
                return item_b->stype - item_a->stype;
425

    
426
        /* if b is special folder, and a is not, b is larger (ends up
427
         * higher in the list) */
428
        if (item_a->stype == F_NORMAL && item_b->stype != F_NORMAL)
429
                return item_b->stype - item_a->stype;
430

    
431
        /* otherwise just compare the folder names */
432
        str_a = g_utf8_casefold(item_a->name, -1);
433
        str_b = g_utf8_casefold(item_b->name, -1);
434
        ret = g_utf8_collate(str_a, str_b);
435
        g_free(str_b);
436
        g_free(str_a);
437

    
438
        return ret;
439
}
440

    
441
void folder_set_ui_func(Folder *folder, FolderUIFunc func, gpointer data)
442
{
443
        g_return_if_fail(folder != NULL);
444

    
445
        folder->ui_func = func;
446
        folder->ui_func_data = data;
447
}
448

    
449
void folder_set_ui_func2(Folder *folder, FolderUIFunc2 func, gpointer data)
450
{
451
        FolderPrivData *priv;
452

    
453
        priv = folder_get_priv(folder);
454
        if (priv) {
455
                priv->ui_func2 = func;
456
                priv->ui_func2_data = data;
457
        }
458
}
459

    
460
FolderUIFunc2 folder_get_ui_func2(Folder *folder)
461
{
462
        FolderPrivData *priv;
463

    
464
        priv = folder_get_priv(folder);
465
        if (priv)
466
                return priv->ui_func2;
467

    
468
        return NULL;
469
}
470

    
471
gboolean folder_call_ui_func2(Folder *folder, FolderItem *item, guint count,
472
                              guint total)
473
{
474
        FolderPrivData *priv;
475

    
476
        priv = folder_get_priv(folder);
477
        if (priv && priv->ui_func2) {
478
                return priv->ui_func2(folder, item, count, total,
479
                                      priv->ui_func2_data);
480
        }
481

    
482
        return TRUE;
483
}
484

    
485
void folder_set_name(Folder *folder, const gchar *name)
486
{
487
        g_return_if_fail(folder != NULL);
488

    
489
        g_free(folder->name);
490
        folder->name = name ? g_strdup(name) : NULL;
491
        if (folder->node && folder->node->data) {
492
                FolderItem *item = (FolderItem *)folder->node->data;
493

    
494
                g_free(item->name);
495
                item->name = name ? g_strdup(name) : NULL;
496
        }
497
}
498

    
499
void folder_tree_destroy(Folder *folder)
500
{
501
        g_return_if_fail(folder != NULL);
502

    
503
        if (folder->node)
504
                folder_item_remove(FOLDER_ITEM(folder->node->data));
505
}
506

    
507
void folder_add(Folder *folder)
508
{
509
        Folder *cur_folder;
510
        GList *cur;
511
        gint i;
512
        FolderPrivData *priv;
513

    
514
        debug_print("Adding Folder (%p) to folder list\n", folder);
515

    
516
        g_return_if_fail(folder != NULL);
517

    
518
        for (i = 0, cur = folder_list; cur != NULL; cur = cur->next, i++) {
519
                cur_folder = FOLDER(cur->data);
520
                if (FOLDER_TYPE(folder) == F_MH) {
521
                        if (FOLDER_TYPE(cur_folder) != F_MH) break;
522
                } else if (FOLDER_TYPE(folder) == F_IMAP) {
523
                        if (FOLDER_TYPE(cur_folder) != F_MH &&
524
                            FOLDER_TYPE(cur_folder) != F_IMAP) break;
525
                } else if (FOLDER_TYPE(folder) == F_NEWS) {
526
                        if (FOLDER_TYPE(cur_folder) != F_MH &&
527
                            FOLDER_TYPE(cur_folder) != F_IMAP &&
528
                            FOLDER_TYPE(cur_folder) != F_NEWS) break;
529
                }
530
        }
531

    
532
        folder_list = g_list_insert(folder_list, folder, i);
533
        priv = g_new0(FolderPrivData, 1);
534
        priv->folder = folder;
535
        folder_priv_list = g_list_insert(folder_priv_list, priv, i);
536
}
537

    
538
GList *folder_get_list(void)
539
{
540
        return folder_list;
541
}
542

    
543
gint folder_read_list(void)
544
{
545
        GNode *node;
546
        XMLNode *xmlnode;
547
        gchar *path;
548

    
549
        path = folder_get_list_path();
550
        if (!is_file_exist(path)) return -1;
551
        node = xml_parse_file(path);
552
        if (!node) return -1;
553

    
554
        xmlnode = node->data;
555
        if (strcmp2(xmlnode->tag->tag, "folderlist") != 0) {
556
                g_warning("wrong folder list\n");
557
                xml_free_tree(node);
558
                return -1;
559
        }
560

    
561
        g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, 2,
562
                        folder_read_folder_func, NULL);
563

    
564
        xml_free_tree(node);
565
        if (folder_list)
566
                return 0;
567
        else
568
                return -1;
569
}
570

    
571
void folder_write_list(void)
572
{
573
        GList *list;
574
        Folder *folder;
575
        gchar *path;
576
        PrefFile *pfile;
577

    
578
        path = folder_get_list_path();
579
        if ((pfile = prefs_file_open(path)) == NULL) return;
580

    
581
        fprintf(pfile->fp, "<?xml version=\"1.0\" encoding=\"%s\"?>\n",
582
                CS_INTERNAL);
583
        fputs("\n<folderlist>\n", pfile->fp);
584

    
585
        for (list = folder_list; list != NULL; list = list->next) {
586
                folder = list->data;
587
                folder_write_list_recursive(folder->node, pfile->fp);
588
        }
589

    
590
        fputs("</folderlist>\n", pfile->fp);
591

    
592
        if (prefs_file_close(pfile) < 0)
593
                g_warning("failed to write folder list.\n");
594

    
595
        if (syl_app_get())
596
                g_signal_emit_by_name(syl_app_get(), "folderlist-updated");
597
}
598

    
599
struct TotalMsgStatus
600
{
601
        guint new;
602
        guint unread;
603
        guint total;
604
        GString *str;
605
};
606

    
607
static gboolean folder_get_status_full_all_func(GNode *node, gpointer data)
608
{
609
        FolderItem *item;
610
        struct TotalMsgStatus *status = (struct TotalMsgStatus *)data;
611
        gchar *id;
612

    
613
        g_return_val_if_fail(node->data != NULL, FALSE);
614

    
615
        item = FOLDER_ITEM(node->data);
616

    
617
        if (!item->path) return FALSE;
618

    
619
        status->new += item->new;
620
        status->unread += item->unread;
621
        status->total += item->total;
622

    
623
        if (status->str) {
624
                id = folder_item_get_identifier(item);
625
                g_string_sprintfa(status->str, "%5d %5d %5d %s\n",
626
                                  item->new, item->unread,
627
                                  item->total, id);
628
                g_free(id);
629
        }
630

    
631
        return FALSE;
632
}
633

    
634
static void folder_get_status_full_all(GString *str, guint *new, guint *unread,
635
                                       guint *total)
636
{
637
        GList *list;
638
        Folder *folder;
639
        struct TotalMsgStatus status;
640

    
641
        status.new = status.unread = status.total = 0;
642
        status.str = str;
643

    
644
        debug_print("Counting total number of messages...\n");
645

    
646
        for (list = folder_list; list != NULL; list = list->next) {
647
                folder = FOLDER(list->data);
648
                if (folder->node)
649
                        g_node_traverse(folder->node, G_PRE_ORDER,
650
                                        G_TRAVERSE_ALL, -1,
651
                                        folder_get_status_full_all_func,
652
                                        &status);
653
        }
654

    
655
        *new = status.new;
656
        *unread = status.unread;
657
        *total = status.total;
658
}
659

    
660
gchar *folder_get_status(GPtrArray *folders, gboolean full)
661
{
662
        guint new, unread, total;
663
        GString *str;
664
        gint i;
665
        gchar *ret;
666

    
667
        new = unread = total = 0;
668

    
669
        str = g_string_new(NULL);
670

    
671
        if (folders) {
672
                for (i = 0; i < folders->len; i++) {
673
                        FolderItem *item;
674

    
675
                        item = g_ptr_array_index(folders, i);
676
                        new += item->new;
677
                        unread += item->unread;
678
                        total += item->total;
679

    
680
                        if (full) {
681
                                gchar *id;
682

    
683
                                id = folder_item_get_identifier(item);
684
                                g_string_sprintfa(str, "%5d %5d %5d %s\n",
685
                                                  item->new, item->unread,
686
                                                  item->total, id);
687
                                g_free(id);
688
                        }
689
                }
690
        } else {
691
                folder_get_status_full_all(full ? str : NULL,
692
                                           &new, &unread, &total);
693
        }
694

    
695
        if (full)
696
                g_string_sprintfa(str, "%5d %5d %5d\n", new, unread, total);
697
        else
698
                g_string_sprintfa(str, "%d %d %d\n", new, unread, total);
699

    
700
        ret = str->str;
701
        g_string_free(str, FALSE);
702

    
703
        return ret;
704
}
705

    
706
Folder *folder_find_from_path(const gchar *path)
707
{
708
        GList *list;
709
        Folder *folder;
710

    
711
        for (list = folder_list; list != NULL; list = list->next) {
712
                folder = list->data;
713
                if (FOLDER_TYPE(folder) == F_MH &&
714
                    !path_cmp(LOCAL_FOLDER(folder)->rootpath, path))
715
                        return folder;
716
        }
717

    
718
        return NULL;
719
}
720

    
721
Folder *folder_find_from_name(const gchar *name, FolderType type)
722
{
723
        GList *list;
724
        Folder *folder;
725

    
726
        for (list = folder_list; list != NULL; list = list->next) {
727
                folder = list->data;
728
                if (FOLDER_TYPE(folder) == type &&
729
                    strcmp2(name, folder->name) == 0)
730
                        return folder;
731
        }
732

    
733
        return NULL;
734
}
735

    
736
static gboolean folder_item_find_func(GNode *node, gpointer data)
737
{
738
        FolderItem *item = node->data;
739
        gpointer *d = data;
740
        const gchar *path = d[0];
741

    
742
        if (path_cmp(path, item->path) != 0)
743
                return FALSE;
744

    
745
        d[1] = item;
746

    
747
        return TRUE;
748
}
749

    
750
FolderItem *folder_find_item_from_path(const gchar *path)
751
{
752
        Folder *folder;
753
        gpointer d[2];
754

    
755
        folder = folder_get_default_folder();
756
        g_return_val_if_fail(folder != NULL, NULL);
757

    
758
        d[0] = (gpointer)path;
759
        d[1] = NULL;
760
        g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
761
                        folder_item_find_func, d);
762
        return d[1];
763
}
764

    
765
FolderItem *folder_find_child_item_by_name(FolderItem *item, const gchar *name)
766
{
767
        GNode *node;
768
        FolderItem *child;
769
        const gchar *base;
770

    
771
        if (!name)
772
                return NULL;
773

    
774
        for (node = item->node->children; node != NULL; node = node->next) {
775
                child = FOLDER_ITEM(node->data);
776
                base = g_basename(child->path);
777
#ifdef G_OS_WIN32
778
                if (base && g_ascii_strcasecmp(base, name) == 0)
779
#else
780
                if (strcmp2(base, name) == 0)
781
#endif
782
                        return child;
783
        }
784

    
785
        return NULL;
786
}
787

    
788
static const struct {
789
        gchar *str;
790
        FolderType type;
791
} type_str_table[] = {
792
        {"#mh"     , F_MH},
793
        {"#mbox"   , F_MBOX},
794
        {"#maildir", F_MAILDIR},
795
        {"#imap"   , F_IMAP},
796
        {"#news"   , F_NEWS}
797
};
798

    
799
static gchar *folder_get_type_string(FolderType type)
800
{
801
        gint i;
802

    
803
        for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
804
             i++) {
805
                if (type_str_table[i].type == type)
806
                        return type_str_table[i].str;
807
        }
808

    
809
        return NULL;
810
}
811

    
812
static FolderType folder_get_type_from_string(const gchar *str)
813
{
814
        gint i;
815

    
816
        for (i = 0; i < sizeof(type_str_table) / sizeof(type_str_table[0]);
817
             i++) {
818
                if (g_ascii_strcasecmp(type_str_table[i].str, str) == 0)
819
                        return type_str_table[i].type;
820
        }
821

    
822
        return F_UNKNOWN;
823
}
824

    
825
gchar *folder_get_identifier(Folder *folder)
826
{
827
        gchar *type_str;
828

    
829
        g_return_val_if_fail(folder != NULL, NULL);
830

    
831
        type_str = folder_get_type_string(FOLDER_TYPE(folder));
832
        return g_strconcat(type_str, "/", folder->name, NULL);
833
}
834

    
835
gchar *folder_item_get_identifier(FolderItem *item)
836
{
837
        gchar *id;
838
        gchar *folder_id;
839

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

    
842
        if (!item->path) {
843
                if (!item->parent)
844
                        return folder_get_identifier(item->folder);
845
                else
846
                        return NULL;
847
        }
848

    
849
        folder_id = folder_get_identifier(item->folder);
850
        id = g_strconcat(folder_id, "/", item->path, NULL);
851
        g_free(folder_id);
852

    
853
        return id;
854
}
855

    
856
FolderItem *folder_find_item_from_identifier(const gchar *identifier)
857
{
858
        Folder *folder;
859
        gpointer d[2];
860
        gchar *str;
861
        gchar *p;
862
        gchar *name;
863
        gchar *path;
864
        FolderType type;
865

    
866
        g_return_val_if_fail(identifier != NULL, NULL);
867

    
868
        if (*identifier != '#')
869
                return folder_find_item_from_path(identifier);
870

    
871
        Xstrdup_a(str, identifier, return NULL);
872

    
873
        p = strchr(str, '/');
874
        if (!p)
875
                return folder_find_item_from_path(identifier);
876
        *p = '\0';
877
        p++;
878
        type = folder_get_type_from_string(str);
879
        if (type == F_UNKNOWN)
880
                return folder_find_item_from_path(identifier);
881

    
882
        name = p;
883
        p = strchr(p, '/');
884
        if (p) {
885
                *p = '\0';
886
                p++;
887
        }
888

    
889
        folder = folder_find_from_name(name, type);
890
        if (!folder)
891
                return folder_find_item_from_path(identifier);
892

    
893
        if (!p)
894
                return FOLDER_ITEM(folder->node->data);
895

    
896
        path = p;
897

    
898
        d[0] = (gpointer)path;
899
        d[1] = NULL;
900
        g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
901
                        folder_item_find_func, d);
902
        return d[1];
903
}
904

    
905
FolderItem *folder_find_item_and_num_from_id(const gchar *identifier, gint *num)
906
{
907
        gchar *id;
908
        gchar *msg;
909
        FolderItem *item;
910

    
911
        g_return_val_if_fail(identifier != NULL, NULL);
912

    
913
        id = g_path_get_dirname(identifier);
914
        msg = g_path_get_basename(identifier);
915
        item = folder_find_item_from_identifier(id);
916
        *num = to_number(msg);
917
        g_free(msg);
918
        g_free(id);
919

    
920
        return item;
921
}
922

    
923
Folder *folder_get_default_folder(void)
924
{
925
        return folder_list ? FOLDER(folder_list->data) : NULL;
926
}
927

    
928
FolderItem *folder_get_default_inbox(void)
929
{
930
        Folder *folder;
931

    
932
        if (!folder_list) return NULL;
933
        folder = FOLDER(folder_list->data);
934
        g_return_val_if_fail(folder != NULL, NULL);
935
        return folder->inbox;
936
}
937

    
938
FolderItem *folder_get_default_outbox(void)
939
{
940
        Folder *folder;
941

    
942
        if (!folder_list) return NULL;
943
        folder = FOLDER(folder_list->data);
944
        g_return_val_if_fail(folder != NULL, NULL);
945
        return folder->outbox;
946
}
947

    
948
FolderItem *folder_get_default_draft(void)
949
{
950
        Folder *folder;
951

    
952
        if (!folder_list) return NULL;
953
        folder = FOLDER(folder_list->data);
954
        g_return_val_if_fail(folder != NULL, NULL);
955
        return folder->draft;
956
}
957

    
958
FolderItem *folder_get_default_queue(void)
959
{
960
        Folder *folder;
961

    
962
        if (!folder_list) return NULL;
963
        folder = FOLDER(folder_list->data);
964
        g_return_val_if_fail(folder != NULL, NULL);
965
        return folder->queue;
966
}
967

    
968
FolderItem *folder_get_default_trash(void)
969
{
970
        Folder *folder;
971

    
972
        if (!folder_list) return NULL;
973
        folder = FOLDER(folder_list->data);
974
        g_return_val_if_fail(folder != NULL, NULL);
975
        return folder->trash;
976
}
977

    
978
FolderItem *folder_get_default_junk(void)
979
{
980
        FolderPrivData *priv;
981

    
982
        if (!folder_list) return NULL;
983
        if (!folder_priv_list) return NULL;
984
        priv = (FolderPrivData *)folder_priv_list->data;
985
        g_return_val_if_fail(priv != NULL, NULL);
986
        g_return_val_if_fail(priv->folder != NULL, NULL);
987
        return priv->junk;
988
}
989

    
990
static FolderPrivData *folder_get_priv(Folder *folder)
991
{
992
        FolderPrivData *priv;
993
        GList *cur;
994

    
995
        g_return_val_if_fail(folder != NULL, NULL);
996

    
997
        for (cur = folder_priv_list; cur != NULL; cur = cur->next) {
998
                priv = (FolderPrivData *)cur->data;
999
                if (priv->folder == folder)
1000
                        return priv;
1001
        }
1002

    
1003
        g_warning("folder_get_priv: private data for Folder (%p) not found.",
1004
                  folder);
1005

    
1006
        return NULL;
1007
}
1008

    
1009
FolderItem *folder_get_junk(Folder *folder)
1010
{
1011
        FolderPrivData *priv;
1012

    
1013
        priv = folder_get_priv(folder);
1014
        if (priv)
1015
                return priv->junk;
1016

    
1017
        return NULL;
1018
}
1019

    
1020
void folder_set_junk(Folder *folder, FolderItem *item)
1021
{
1022
        FolderPrivData *priv;
1023

    
1024
        priv = folder_get_priv(folder);
1025
        if (priv)
1026
                priv->junk = item;
1027
}
1028

    
1029
gboolean folder_item_is_trash(FolderItem *item)
1030
{
1031
        PrefsAccount *ac;
1032
        FolderItem *trash;
1033

    
1034
        g_return_val_if_fail(item != NULL, FALSE);
1035

    
1036
        if (item->stype == F_TRASH)
1037
                return TRUE;
1038

    
1039
        ac = account_find_from_item_property(item);
1040
        if (ac && ac->set_trash_folder && ac->trash_folder) {
1041
                trash = folder_find_item_from_identifier(ac->trash_folder);
1042
                if (trash == item)
1043
                        return TRUE;
1044
        }
1045

    
1046
        return FALSE;
1047
}
1048

    
1049
#define CREATE_FOLDER_IF_NOT_EXIST(member, dir, type)        \
1050
{                                                        \
1051
        if (!folder->member) {                                \
1052
                item = folder_item_new(dir, dir);        \
1053
                item->stype = type;                        \
1054
                folder_item_append(rootitem, item);        \
1055
                folder->member = item;                        \
1056
        }                                                \
1057
}
1058

    
1059
void folder_set_missing_folders(void)
1060
{
1061
        Folder *folder;
1062
        FolderItem *rootitem;
1063
        FolderItem *item;
1064
        GList *list;
1065

    
1066
        for (list = folder_list; list != NULL; list = list->next) {
1067
                folder = list->data;
1068
                if (FOLDER_TYPE(folder) != F_MH) continue;
1069
                rootitem = FOLDER_ITEM(folder->node->data);
1070
                g_return_if_fail(rootitem != NULL);
1071

    
1072
                if (folder->inbox && folder->outbox && folder->draft &&
1073
                    folder->queue && folder->trash && folder_get_junk(folder))
1074
                        continue;
1075

    
1076
                if (folder_create_tree(folder) < 0) {
1077
                        g_warning("%s: can't create the folder tree.\n",
1078
                                  LOCAL_FOLDER(folder)->rootpath);
1079
                        continue;
1080
                }
1081

    
1082
                CREATE_FOLDER_IF_NOT_EXIST(inbox,  INBOX_DIR,  F_INBOX);
1083
                CREATE_FOLDER_IF_NOT_EXIST(outbox, OUTBOX_DIR, F_OUTBOX);
1084
                CREATE_FOLDER_IF_NOT_EXIST(draft,  DRAFT_DIR,  F_DRAFT);
1085
                CREATE_FOLDER_IF_NOT_EXIST(queue,  QUEUE_DIR,  F_QUEUE);
1086
                CREATE_FOLDER_IF_NOT_EXIST(trash,  TRASH_DIR,  F_TRASH);
1087

    
1088
                if (!folder_get_junk(folder)) {
1089
                        item = folder_item_new(JUNK_DIR, JUNK_DIR);
1090
                        item->stype = F_JUNK;
1091
                        folder_item_append(rootitem, item);
1092
                        folder_set_junk(folder, item);
1093
                }
1094
        }
1095
}
1096

    
1097
static gboolean folder_unref_account_func(GNode *node, gpointer data)
1098
{
1099
        FolderItem *item = node->data;
1100
        PrefsAccount *account = data;
1101

    
1102
        if (item->account == account)
1103
                item->account = NULL;
1104

    
1105
        return FALSE;
1106
}
1107

    
1108
void folder_unref_account_all(PrefsAccount *account)
1109
{
1110
        Folder *folder;
1111
        GList *list;
1112

    
1113
        if (!account) return;
1114

    
1115
        for (list = folder_list; list != NULL; list = list->next) {
1116
                folder = list->data;
1117
                if (folder->account == account)
1118
                        folder->account = NULL;
1119
                g_node_traverse(folder->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1120
                                folder_unref_account_func, account);
1121
        }
1122
}
1123

    
1124
#undef CREATE_FOLDER_IF_NOT_EXIST
1125

    
1126
gchar *folder_get_path(Folder *folder)
1127
{
1128
        gchar *path;
1129

    
1130
        g_return_val_if_fail(folder != NULL, NULL);
1131

    
1132
        if (FOLDER_TYPE(folder) == F_MH) {
1133
                path = g_filename_from_utf8(LOCAL_FOLDER(folder)->rootpath,
1134
                                            -1, NULL, NULL, NULL);
1135
                if (!path) {
1136
                        g_warning("folder_get_path: faild to convert character set\n");
1137
                        path = g_strdup(LOCAL_FOLDER(folder)->rootpath);
1138
                }
1139
                if (!g_path_is_absolute(path)) {
1140
                        gchar *path_;
1141

    
1142
                        path_ = g_strconcat(get_mail_base_dir(),
1143
                                            G_DIR_SEPARATOR_S, path, NULL);
1144
                        g_free(path);
1145
                        path = path_;
1146
                }
1147
        } else if (FOLDER_TYPE(folder) == F_IMAP) {
1148
                gchar *server;
1149
                gchar *uid;
1150

    
1151
                g_return_val_if_fail(folder->account != NULL, NULL);
1152
                server = uriencode_for_filename(folder->account->recv_server);
1153
                uid = uriencode_for_filename(folder->account->userid);
1154
                path = g_strconcat(get_imap_cache_dir(),
1155
                                   G_DIR_SEPARATOR_S, server,
1156
                                   G_DIR_SEPARATOR_S, uid, NULL);
1157
                g_free(uid);
1158
                g_free(server);
1159
        } else if (FOLDER_TYPE(folder) == F_NEWS) {
1160
                gchar *server;
1161

    
1162
                g_return_val_if_fail(folder->account != NULL, NULL);
1163
                server = uriencode_for_filename(folder->account->nntp_server);
1164
                path = g_strconcat(get_news_cache_dir(),
1165
                                   G_DIR_SEPARATOR_S, server, NULL);
1166
                g_free(server);
1167
        } else
1168
                path = NULL;
1169

    
1170
        return path;
1171
}
1172

    
1173
gchar *folder_item_get_path(FolderItem *item)
1174
{
1175
        gchar *folder_path;
1176
        gchar *item_path = NULL, *path;
1177

    
1178
        g_return_val_if_fail(item != NULL, NULL);
1179

    
1180
        folder_path = folder_get_path(item->folder);
1181
        g_return_val_if_fail(folder_path != NULL, NULL);
1182

    
1183
        if (item->path) {
1184
                item_path = g_filename_from_utf8(item->path, -1,
1185
                                                 NULL, NULL, NULL);
1186
                if (!item_path) {
1187
                        g_warning("folder_item_get_path: faild to convert character set\n");
1188
                        item_path = g_strdup(item->path);
1189
                }
1190
#ifdef G_OS_WIN32
1191
                subst_char(item_path, '/', G_DIR_SEPARATOR);
1192
#endif
1193
        }
1194

    
1195
        if (item_path)
1196
                path = g_strconcat(folder_path, G_DIR_SEPARATOR_S, item_path,
1197
                                   NULL);
1198
        else
1199
                path = g_strdup(folder_path);
1200

    
1201
        g_free(item_path);
1202
        g_free(folder_path);
1203
        return path;
1204
}
1205

    
1206
gint folder_item_scan(FolderItem *item)
1207
{
1208
        Folder *folder;
1209

    
1210
        g_return_val_if_fail(item != NULL, -1);
1211

    
1212
        folder = item->folder;
1213
        return folder->klass->scan(folder, item);
1214
}
1215

    
1216
static void folder_item_scan_foreach_func(gpointer key, gpointer val,
1217
                                          gpointer data)
1218
{
1219
        folder_item_scan(FOLDER_ITEM(key));
1220
}
1221

    
1222
void folder_item_scan_foreach(GHashTable *table)
1223
{
1224
        g_hash_table_foreach(table, folder_item_scan_foreach_func, NULL);
1225
}
1226

    
1227
GSList *folder_item_get_msg_list(FolderItem *item, gboolean use_cache)
1228
{
1229
        Folder *folder;
1230

    
1231
        g_return_val_if_fail(item != NULL, NULL);
1232

    
1233
        folder = item->folder;
1234

    
1235
        if (item->stype == F_VIRTUAL)
1236
                return virtual_get_class()->get_msg_list(folder, item,
1237
                                                         use_cache);
1238

    
1239
        return folder->klass->get_msg_list(folder, item, use_cache);
1240
}
1241

    
1242
GSList *folder_item_get_uncached_msg_list(FolderItem *item)
1243
{
1244
        Folder *folder;
1245

    
1246
        g_return_val_if_fail(item != NULL, NULL);
1247
        g_return_val_if_fail(item->folder->klass->get_uncached_msg_list != NULL,
1248
                             NULL);
1249

    
1250
        folder = item->folder;
1251

    
1252
        if (item->stype == F_VIRTUAL)
1253
                return NULL;
1254

    
1255
        return folder->klass->get_uncached_msg_list(folder, item);
1256
}
1257

    
1258
gchar *folder_item_fetch_msg(FolderItem *item, gint num)
1259
{
1260
        Folder *folder;
1261

    
1262
        g_return_val_if_fail(item != NULL, NULL);
1263

    
1264
        folder = item->folder;
1265

    
1266
        return folder->klass->fetch_msg(folder, item, num);
1267
}
1268

    
1269
gint folder_item_fetch_all_msg(FolderItem *item)
1270
{
1271
        Folder *folder;
1272
        GSList *mlist;
1273
        GSList *cur;
1274
        gint num = 0;
1275
        gint ret = 0;
1276

    
1277
        g_return_val_if_fail(item != NULL, -1);
1278

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

    
1281
        folder = item->folder;
1282

    
1283
        if (folder->ui_func)
1284
                folder->ui_func(folder, item, folder->ui_func_data ?
1285
                                folder->ui_func_data : GINT_TO_POINTER(num));
1286

    
1287
        mlist = folder_item_get_msg_list(item, TRUE);
1288

    
1289
        for (cur = mlist; cur != NULL; cur = cur->next) {
1290
                MsgInfo *msginfo = (MsgInfo *)cur->data;
1291
                gchar *msg;
1292

    
1293
                num++;
1294
                if (folder->ui_func)
1295
                        folder->ui_func(folder, item,
1296
                                        folder->ui_func_data ?
1297
                                        folder->ui_func_data :
1298
                                        GINT_TO_POINTER(num));
1299

    
1300
                msg = folder_item_fetch_msg(item, msginfo->msgnum);
1301
                if (!msg) {
1302
                        g_warning("Can't fetch message %d. Aborting.\n",
1303
                                  msginfo->msgnum);
1304
                        ret = -1;
1305
                        break;
1306
                }
1307
                g_free(msg);
1308
        }
1309

    
1310
        procmsg_msg_list_free(mlist);
1311

    
1312
        return ret;
1313
}
1314

    
1315
MsgInfo *folder_item_get_msginfo(FolderItem *item, gint num)
1316
{
1317
        Folder *folder;
1318

    
1319
        g_return_val_if_fail(item != NULL, NULL);
1320

    
1321
        folder = item->folder;
1322

    
1323
        return folder->klass->get_msginfo(folder, item, num);
1324
}
1325

    
1326
gint folder_item_add_msg(FolderItem *dest, const gchar *file, MsgFlags *flags,
1327
                         gboolean remove_source)
1328
{
1329
        Folder *folder;
1330

    
1331
        g_return_val_if_fail(dest != NULL, -1);
1332
        g_return_val_if_fail(file != NULL, -1);
1333
        g_return_val_if_fail(dest->folder->klass->add_msg != NULL, -1);
1334

    
1335
        folder = dest->folder;
1336

    
1337
        return folder->klass->add_msg(folder, dest, file, flags, remove_source);
1338
}
1339

    
1340
gint folder_item_add_msgs(FolderItem *dest, GSList *file_list,
1341
                          gboolean remove_source, gint *first)
1342
{
1343
        Folder *folder;
1344

    
1345
        g_return_val_if_fail(dest != NULL, -1);
1346
        g_return_val_if_fail(file_list != NULL, -1);
1347
        g_return_val_if_fail(dest->folder->klass->add_msgs != NULL, -1);
1348

    
1349
        folder = dest->folder;
1350

    
1351
        return folder->klass->add_msgs(folder, dest, file_list, remove_source,
1352
                                       first);
1353
}
1354

    
1355
gint folder_item_add_msg_msginfo(FolderItem *dest, MsgInfo *msginfo,
1356
                                 gboolean remove_source)
1357
{
1358
        Folder *folder;
1359

    
1360
        g_return_val_if_fail(dest != NULL, -1);
1361
        g_return_val_if_fail(msginfo != NULL, -1);
1362
        g_return_val_if_fail(msginfo->file_path != NULL, -1);
1363
        g_return_val_if_fail(dest->folder->klass->add_msg_msginfo != NULL, -1);
1364

    
1365
        folder = dest->folder;
1366

    
1367
        return folder->klass->add_msg_msginfo(folder, dest, msginfo,
1368
                                              remove_source);
1369
}
1370

    
1371
gint folder_item_add_msgs_msginfo(FolderItem *dest, GSList *msglist,
1372
                                  gboolean remove_source, gint *first)
1373
{
1374
        Folder *folder;
1375

    
1376
        g_return_val_if_fail(dest != NULL, -1);
1377
        g_return_val_if_fail(msglist != NULL, -1);
1378
        g_return_val_if_fail(dest->folder->klass->add_msgs_msginfo != NULL, -1);
1379

    
1380
        folder = dest->folder;
1381

    
1382
        return folder->klass->add_msgs_msginfo(folder, dest, msglist,
1383
                                               remove_source, first);
1384
}
1385

    
1386
#define IS_FROM_QUEUE(m, d) \
1387
        (m->folder && m->folder->stype == F_QUEUE && \
1388
         MSG_IS_QUEUED(m->flags) && d->stype != F_QUEUE)
1389

    
1390
gint folder_item_move_msg(FolderItem *dest, MsgInfo *msginfo)
1391
{
1392
        Folder *folder;
1393

    
1394
        g_return_val_if_fail(dest != NULL, -1);
1395
        g_return_val_if_fail(msginfo != NULL, -1);
1396
        g_return_val_if_fail(dest->folder->klass->move_msg != NULL, -1);
1397

    
1398
        folder = dest->folder;
1399

    
1400
        if (IS_FROM_QUEUE(msginfo, dest)) {
1401
                GSList msglist;
1402

    
1403
                msglist.data = msginfo;
1404
                msglist.next = NULL;
1405
                return procmsg_add_messages_from_queue(dest, &msglist, TRUE);
1406
        }
1407

    
1408
        return folder->klass->move_msg(folder, dest, msginfo);
1409
}
1410

    
1411
gint folder_item_move_msgs(FolderItem *dest, GSList *msglist)
1412
{
1413
        Folder *folder;
1414
        MsgInfo *msginfo;
1415

    
1416
        g_return_val_if_fail(dest != NULL, -1);
1417
        g_return_val_if_fail(msglist != NULL, -1);
1418
        g_return_val_if_fail(dest->folder->klass->move_msgs != NULL, -1);
1419

    
1420
        folder = dest->folder;
1421

    
1422
        msginfo = (MsgInfo *)msglist->data;
1423
        if (IS_FROM_QUEUE(msginfo, dest))
1424
                return procmsg_add_messages_from_queue(dest, msglist, TRUE);
1425

    
1426
        return folder->klass->move_msgs(folder, dest, msglist);
1427
}
1428

    
1429
gint folder_item_copy_msg(FolderItem *dest, MsgInfo *msginfo)
1430
{
1431
        Folder *folder;
1432

    
1433
        g_return_val_if_fail(dest != NULL, -1);
1434
        g_return_val_if_fail(msginfo != NULL, -1);
1435
        g_return_val_if_fail(dest->folder->klass->copy_msg != NULL, -1);
1436

    
1437
        folder = dest->folder;
1438

    
1439
        if (IS_FROM_QUEUE(msginfo, dest)) {
1440
                GSList msglist;
1441

    
1442
                msglist.data = msginfo;
1443
                msglist.next = NULL;
1444
                return procmsg_add_messages_from_queue(dest, &msglist, FALSE);
1445
        }
1446

    
1447
        return folder->klass->copy_msg(folder, dest, msginfo);
1448
}
1449

    
1450
gint folder_item_copy_msgs(FolderItem *dest, GSList *msglist)
1451
{
1452
        Folder *folder;
1453
        MsgInfo *msginfo;
1454

    
1455
        g_return_val_if_fail(dest != NULL, -1);
1456
        g_return_val_if_fail(msglist != NULL, -1);
1457
        g_return_val_if_fail(dest->folder->klass->copy_msgs != NULL, -1);
1458

    
1459
        folder = dest->folder;
1460

    
1461
        msginfo = (MsgInfo *)msglist->data;
1462
        if (IS_FROM_QUEUE(msginfo, dest))
1463
                return procmsg_add_messages_from_queue(dest, msglist, FALSE);
1464

    
1465
        return folder->klass->copy_msgs(folder, dest, msglist);
1466
}
1467

    
1468
#undef IS_FROM_QUEUE
1469

    
1470
gint folder_item_remove_msg(FolderItem *item, MsgInfo *msginfo)
1471
{
1472
        Folder *folder;
1473

    
1474
        g_return_val_if_fail(item != NULL, -1);
1475
        g_return_val_if_fail(item->folder->klass->remove_msg != NULL, -1);
1476

    
1477
        folder = item->folder;
1478

    
1479
        return folder->klass->remove_msg(folder, item, msginfo);
1480
}
1481

    
1482
gint folder_item_remove_msgs(FolderItem *item, GSList *msglist)
1483
{
1484
        Folder *folder;
1485
        gint ret = 0;
1486

    
1487
        g_return_val_if_fail(item != NULL, -1);
1488

    
1489
        folder = item->folder;
1490
        if (folder->klass->remove_msgs) {
1491
                return folder->klass->remove_msgs(folder, item, msglist);
1492
        }
1493

    
1494
        while (msglist != NULL) {
1495
                MsgInfo *msginfo = (MsgInfo *)msglist->data;
1496

    
1497
                ret = folder_item_remove_msg(item, msginfo);
1498
                if (ret != 0) break;
1499
                msglist = msglist->next;
1500
        }
1501

    
1502
        return ret;
1503
}
1504

    
1505
gint folder_item_remove_all_msg(FolderItem *item)
1506
{
1507
        Folder *folder;
1508

    
1509
        g_return_val_if_fail(item != NULL, -1);
1510
        g_return_val_if_fail(item->folder->klass->remove_all_msg != NULL, -1);
1511

    
1512
        folder = item->folder;
1513

    
1514
        return folder->klass->remove_all_msg(folder, item);
1515
}
1516

    
1517
gboolean folder_item_is_msg_changed(FolderItem *item, MsgInfo *msginfo)
1518
{
1519
        Folder *folder;
1520

    
1521
        g_return_val_if_fail(item != NULL, FALSE);
1522
        g_return_val_if_fail(item->folder->klass->is_msg_changed != NULL,
1523
                             FALSE);
1524

    
1525
        folder = item->folder;
1526
        return folder->klass->is_msg_changed(folder, item, msginfo);
1527
}
1528

    
1529
gint folder_item_close(FolderItem *item)
1530
{
1531
        Folder *folder;
1532

    
1533
        g_return_val_if_fail(item != NULL, -1);
1534

    
1535
        item->opened = FALSE;
1536
        folder = item->folder;
1537
        return folder->klass->close(folder, item);
1538
}
1539

    
1540
gchar *folder_item_get_cache_file(FolderItem *item)
1541
{
1542
        gchar *path;
1543
        gchar *file;
1544

    
1545
        g_return_val_if_fail(item != NULL, NULL);
1546
        g_return_val_if_fail(item->path != NULL, NULL);
1547

    
1548
        path = folder_item_get_path(item);
1549
        g_return_val_if_fail(path != NULL, NULL);
1550
        if (!is_dir_exist(path))
1551
                make_dir_hier(path);
1552
        file = g_strconcat(path, G_DIR_SEPARATOR_S, CACHE_FILE, NULL);
1553
        g_free(path);
1554

    
1555
        return file;
1556
}
1557

    
1558
gchar *folder_item_get_mark_file(FolderItem *item)
1559
{
1560
        gchar *path;
1561
        gchar *file;
1562

    
1563
        g_return_val_if_fail(item != NULL, NULL);
1564
        g_return_val_if_fail(item->path != NULL, NULL);
1565

    
1566
        path = folder_item_get_path(item);
1567
        g_return_val_if_fail(path != NULL, NULL);
1568
        if (!is_dir_exist(path))
1569
                make_dir_hier(path);
1570
        file = g_strconcat(path, G_DIR_SEPARATOR_S, MARK_FILE, NULL);
1571
        g_free(path);
1572

    
1573
        return file;
1574
}
1575

    
1576
static gboolean folder_build_tree(GNode *node, gpointer data)
1577
{
1578
        Folder *folder = FOLDER(data);
1579
        FolderItem *item;
1580
        XMLNode *xmlnode;
1581
        GList *list;
1582
        SpecialFolderItemType stype = F_NORMAL;
1583
        const gchar *name = NULL;
1584
        const gchar *path = NULL;
1585
        PrefsAccount *account = NULL;
1586
        gboolean no_sub = FALSE, no_select = FALSE, collapsed = FALSE,
1587
                 threaded = TRUE, ac_apply_sub = FALSE;
1588
        FolderSortKey sort_key = SORT_BY_NONE;
1589
        FolderSortType sort_type = SORT_ASCENDING;
1590
        gboolean qsearch_cond_type = 0;
1591
        gint new = 0, unread = 0, total = 0;
1592
        time_t mtime = 0;
1593
        gboolean use_auto_to_on_reply = FALSE;
1594
        gchar *auto_to = NULL, *auto_cc = NULL, *auto_bcc = NULL,
1595
              *auto_replyto = NULL;
1596
        gboolean trim_summary_subject = FALSE, trim_compose_subject = FALSE;
1597

    
1598
        g_return_val_if_fail(node->data != NULL, FALSE);
1599
        if (!node->parent) return FALSE;
1600

    
1601
        xmlnode = node->data;
1602
        if (strcmp2(xmlnode->tag->tag, "folderitem") != 0) {
1603
                g_warning("tag name != \"folderitem\"\n");
1604
                return FALSE;
1605
        }
1606

    
1607
        list = xmlnode->tag->attr;
1608
        for (; list != NULL; list = list->next) {
1609
                XMLAttr *attr = list->data;
1610

    
1611
                if (!attr || !attr->name || !attr->value) continue;
1612
                if (!strcmp(attr->name, "type")) {
1613
                        if (!g_ascii_strcasecmp(attr->value, "normal"))
1614
                                stype = F_NORMAL;
1615
                        else if (!g_ascii_strcasecmp(attr->value, "inbox"))
1616
                                stype = F_INBOX;
1617
                        else if (!g_ascii_strcasecmp(attr->value, "outbox"))
1618
                                stype = F_OUTBOX;
1619
                        else if (!g_ascii_strcasecmp(attr->value, "draft"))
1620
                                stype = F_DRAFT;
1621
                        else if (!g_ascii_strcasecmp(attr->value, "queue"))
1622
                                stype = F_QUEUE;
1623
                        else if (!g_ascii_strcasecmp(attr->value, "trash"))
1624
                                stype = F_TRASH;
1625
                        else if (!g_ascii_strcasecmp(attr->value, "junk"))
1626
                                stype = F_JUNK;
1627
                        else if (!g_ascii_strcasecmp(attr->value, "virtual"))
1628
                                stype = F_VIRTUAL;
1629
                } else if (!strcmp(attr->name, "name"))
1630
                        name = attr->value;
1631
                else if (!strcmp(attr->name, "path")) {
1632
#ifdef G_OS_WIN32
1633
                        subst_char(attr->value, G_DIR_SEPARATOR, '/');
1634
#endif
1635
                        path = attr->value;
1636
                } else if (!strcmp(attr->name, "mtime"))
1637
                        mtime = strtoll(attr->value, NULL, 10);
1638
                else if (!strcmp(attr->name, "new"))
1639
                        new = atoi(attr->value);
1640
                else if (!strcmp(attr->name, "unread"))
1641
                        unread = atoi(attr->value);
1642
                else if (!strcmp(attr->name, "total"))
1643
                        total = atoi(attr->value);
1644
                else if (!strcmp(attr->name, "no_sub"))
1645
                        no_sub = *attr->value == '1' ? TRUE : FALSE;
1646
                else if (!strcmp(attr->name, "no_select"))
1647
                        no_select = *attr->value == '1' ? TRUE : FALSE;
1648
                else if (!strcmp(attr->name, "collapsed"))
1649
                        collapsed = *attr->value == '1' ? TRUE : FALSE;
1650
                else if (!strcmp(attr->name, "threaded"))
1651
                        threaded =  *attr->value == '1' ? TRUE : FALSE;
1652
                else if (!strcmp(attr->name, "sort_key")) {
1653
                        if (!strcmp(attr->value, "none"))
1654
                                sort_key = SORT_BY_NONE;
1655
                        else if (!strcmp(attr->value, "number"))
1656
                                sort_key = SORT_BY_NUMBER;
1657
                        else if (!strcmp(attr->value, "size"))
1658
                                sort_key = SORT_BY_SIZE;
1659
                        else if (!strcmp(attr->value, "date"))
1660
                                sort_key = SORT_BY_DATE;
1661
                        else if (!strcmp(attr->value, "thread-date"))
1662
                                sort_key = SORT_BY_TDATE;
1663
                        else if (!strcmp(attr->value, "from"))
1664
                                sort_key = SORT_BY_FROM;
1665
                        else if (!strcmp(attr->value, "subject"))
1666
                                sort_key = SORT_BY_SUBJECT;
1667
                        else if (!strcmp(attr->value, "score"))
1668
                                sort_key = SORT_BY_SCORE;
1669
                        else if (!strcmp(attr->value, "label"))
1670
                                sort_key = SORT_BY_LABEL;
1671
                        else if (!strcmp(attr->value, "mark"))
1672
                                sort_key = SORT_BY_MARK;
1673
                        else if (!strcmp(attr->value, "unread"))
1674
                                sort_key = SORT_BY_UNREAD;
1675
                        else if (!strcmp(attr->value, "mime"))
1676
                                sort_key = SORT_BY_MIME;
1677
                        else if (!strcmp(attr->value, "to"))
1678
                                sort_key = SORT_BY_TO;
1679
                } else if (!strcmp(attr->name, "sort_type")) {
1680
                        if (!strcmp(attr->value, "ascending"))
1681
                                sort_type = SORT_ASCENDING;
1682
                        else
1683
                                sort_type = SORT_DESCENDING;
1684
                } else if (!strcmp(attr->name, "qsearch_cond")) {
1685
                        if (!strcmp(attr->value, "all"))
1686
                                qsearch_cond_type = 0;
1687
                        else if (!strcmp(attr->value, "unread"))
1688
                                qsearch_cond_type = 1;
1689
                        else if (!strcmp(attr->value, "mark"))
1690
                                qsearch_cond_type = 2;
1691
                        else if (!strcmp(attr->value, "clabel"))
1692
                                qsearch_cond_type = 3;
1693
                        else if (!strcmp(attr->value, "mime"))
1694
                                qsearch_cond_type = 4;
1695
                        else if (!strcmp(attr->value, "w1day"))
1696
                                qsearch_cond_type = 5;
1697
                        else if (!strcmp(attr->value, "last5"))
1698
                                qsearch_cond_type = 6;
1699
                        else if (!strcmp(attr->value, "last7"))
1700
                                qsearch_cond_type = 7;
1701
                        else if (!strcmp(attr->value, "in-addressbook"))
1702
                                qsearch_cond_type = 8;
1703
                        else if (!strcmp(attr->value, "last30"))
1704
                                qsearch_cond_type = 9;
1705
                } else if (!strcmp(attr->name, "account_id")) {
1706
                        account = account_find_from_id(atoi(attr->value));
1707
                        if (!account) g_warning("account_id: %s not found\n",
1708
                                                attr->value);
1709
                } else if (!strcmp(attr->name, "account_apply_sub"))
1710
                        ac_apply_sub = *attr->value == '1' ? TRUE : FALSE;
1711
                else if (!strcmp(attr->name, "to"))
1712
                        auto_to = g_strdup(attr->value);
1713
                else if (!strcmp(attr->name, "use_auto_to_on_reply"))
1714
                        use_auto_to_on_reply =
1715
                                *attr->value == '1' ? TRUE : FALSE;
1716
                else if (!strcmp(attr->name, "cc"))
1717
                        auto_cc = g_strdup(attr->value);
1718
                else if (!strcmp(attr->name, "bcc"))
1719
                        auto_bcc = g_strdup(attr->value);
1720
                else if (!strcmp(attr->name, "replyto"))
1721
                        auto_replyto = g_strdup(attr->value);
1722
                else if (!strcmp(attr->name, "trim_summary_subject")) {
1723
                        trim_summary_subject =
1724
                                *attr->value == '1' ? TRUE : FALSE;
1725
                } else if (!strcmp(attr->name, "trim_compose_subject")) {
1726
                        trim_compose_subject =
1727
                                *attr->value = '1' ? TRUE : FALSE;
1728
                }
1729
        }
1730

    
1731
        item = folder_item_new(name, path);
1732
        item->stype = stype;
1733
        item->mtime = mtime;
1734
        item->new = new;
1735
        item->unread = unread;
1736
        item->total = total;
1737
        item->no_sub = no_sub;
1738
        item->no_select = no_select;
1739
        item->collapsed = collapsed;
1740
        item->threaded  = threaded;
1741
        item->sort_key  = sort_key;
1742
        item->sort_type = sort_type;
1743
        item->node = node;
1744
        item->parent = FOLDER_ITEM(node->parent->data);
1745
        item->folder = folder;
1746
        switch (stype) {
1747
        case F_INBOX:  folder->inbox  = item; break;
1748
        case F_OUTBOX: folder->outbox = item; break;
1749
        case F_DRAFT:  folder->draft  = item; break;
1750
        case F_QUEUE:  folder->queue  = item; break;
1751
        case F_TRASH:  folder->trash  = item; break;
1752
        case F_JUNK:   folder_set_junk(folder, item); break;
1753
        default:       break;
1754
        }
1755
        item->account = account;
1756
        item->ac_apply_sub = ac_apply_sub;
1757
        item->auto_to = auto_to;
1758
        item->use_auto_to_on_reply = use_auto_to_on_reply;
1759
        item->auto_cc = auto_cc;
1760
        item->auto_bcc = auto_bcc;
1761
        item->auto_replyto = auto_replyto;
1762
        item->trim_summary_subject = trim_summary_subject;
1763
        item->trim_compose_subject = trim_compose_subject;
1764
        item->qsearch_cond_type = qsearch_cond_type;
1765
        node->data = item;
1766
        xml_free_node(xmlnode);
1767

    
1768
        return FALSE;
1769
}
1770

    
1771
static gboolean folder_read_folder_func(GNode *node, gpointer data)
1772
{
1773
        Folder *folder;
1774
        FolderItem *item;
1775
        XMLNode *xmlnode;
1776
        GList *list;
1777
        FolderType type = F_UNKNOWN;
1778
        const gchar *name = NULL;
1779
        const gchar *path = NULL;
1780
        PrefsAccount *account = NULL;
1781
        gboolean collapsed = FALSE, threaded = TRUE, ac_apply_sub = FALSE;
1782

    
1783
        if (g_node_depth(node) != 2) return FALSE;
1784
        g_return_val_if_fail(node->data != NULL, FALSE);
1785

    
1786
        xmlnode = node->data;
1787
        if (strcmp2(xmlnode->tag->tag, "folder") != 0) {
1788
                g_warning("tag name != \"folder\"\n");
1789
                return TRUE;
1790
        }
1791
        g_node_unlink(node);
1792
        list = xmlnode->tag->attr;
1793
        for (; list != NULL; list = list->next) {
1794
                XMLAttr *attr = list->data;
1795

    
1796
                if (!attr || !attr->name || !attr->value) continue;
1797
                if (!strcmp(attr->name, "type")) {
1798
                        if (!g_ascii_strcasecmp(attr->value, "mh"))
1799
                                type = F_MH;
1800
                        else if (!g_ascii_strcasecmp(attr->value, "mbox"))
1801
                                type = F_MBOX;
1802
                        else if (!g_ascii_strcasecmp(attr->value, "maildir"))
1803
                                type = F_MAILDIR;
1804
                        else if (!g_ascii_strcasecmp(attr->value, "imap"))
1805
                                type = F_IMAP;
1806
                        else if (!g_ascii_strcasecmp(attr->value, "news"))
1807
                                type = F_NEWS;
1808
                } else if (!strcmp(attr->name, "name"))
1809
                        name = attr->value;
1810
                else if (!strcmp(attr->name, "path"))
1811
                        path = attr->value;
1812
                else if (!strcmp(attr->name, "collapsed"))
1813
                        collapsed = *attr->value == '1' ? TRUE : FALSE;
1814
                else if (!strcmp(attr->name, "threaded"))
1815
                        threaded = *attr->value == '1' ? TRUE : FALSE;
1816
                else if (!strcmp(attr->name, "account_id")) {
1817
                        account = account_find_from_id(atoi(attr->value));
1818
                        if (!account) g_warning("account_id: %s not found\n",
1819
                                                attr->value);
1820
                } else if (!strcmp(attr->name, "account_apply_sub"))
1821
                        ac_apply_sub = *attr->value == '1' ? TRUE : FALSE;
1822
        }
1823

    
1824
        folder = folder_new(type, name, path);
1825
        g_return_val_if_fail(folder != NULL, FALSE);
1826
        if (account && FOLDER_IS_REMOTE(folder)) {
1827
                folder->account = account;
1828
                account->folder = REMOTE_FOLDER(folder);
1829
        }
1830
        if (account && FOLDER_IS_LOCAL(folder))
1831
                ac_apply_sub = TRUE;
1832
        item = FOLDER_ITEM(folder->node->data);
1833
        node->data = item;
1834
        item->node = node;
1835
        g_node_destroy(folder->node);
1836
        folder->node = node;
1837
        folder_add(folder);
1838
        item->collapsed = collapsed;
1839
        item->threaded  = threaded;
1840
        item->account   = account;
1841
        item->ac_apply_sub = ac_apply_sub;
1842

    
1843
        g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1844
                        folder_build_tree, folder);
1845

    
1846
        return FALSE;
1847
}
1848

    
1849
static gchar *folder_get_list_path(void)
1850
{
1851
        static gchar *filename = NULL;
1852

    
1853
        if (!filename)
1854
                filename =  g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1855
                                        FOLDER_LIST, NULL);
1856

    
1857
        return filename;
1858
}
1859

    
1860
#define PUT_ESCAPE_STR(fp, attr, str)                        \
1861
{                                                        \
1862
        fputs(" " attr "=\"", fp);                        \
1863
        xml_file_put_escape_str(fp, str);                \
1864
        fputs("\"", fp);                                \
1865
}
1866

    
1867
static void folder_write_list_recursive(GNode *node, gpointer data)
1868
{
1869
        FILE *fp = (FILE *)data;
1870
        FolderItem *item;
1871
        gint i, depth;
1872
        static gchar *folder_type_str[] = {"mh", "mbox", "maildir", "imap",
1873
                                           "news", "unknown"};
1874
        static gchar *folder_item_stype_str[] = {"normal", "inbox", "outbox",
1875
                                                 "draft", "queue", "trash",
1876
                                                 "junk", "virtual"};
1877
        static gchar *sort_key_str[] = {"none", "number", "size", "date",
1878
                                        "thread-date",
1879
                                        "from", "subject", "score", "label",
1880
                                        "mark", "unread", "mime", "to"};
1881
        static gchar *qsearch_cond_str[] = {"all", "unread", "mark", "clabel",
1882
                                            "mime", "w1day", "last5", "last7",
1883
                                            "in-addressbook", "last30"};
1884

    
1885
        g_return_if_fail(node != NULL);
1886
        g_return_if_fail(fp != NULL);
1887

    
1888
        item = FOLDER_ITEM(node->data);
1889
        g_return_if_fail(item != NULL);
1890

    
1891
        depth = g_node_depth(node);
1892
        for (i = 0; i < depth; i++)
1893
                fputs("    ", fp);
1894
        if (depth == 1) {
1895
                Folder *folder = item->folder;
1896

    
1897
                fprintf(fp, "<folder type=\"%s\"",
1898
                        folder_type_str[FOLDER_TYPE(folder)]);
1899
                if (folder->name)
1900
                        PUT_ESCAPE_STR(fp, "name", folder->name);
1901
                if (FOLDER_TYPE(folder) == F_MH)
1902
                        PUT_ESCAPE_STR(fp, "path",
1903
                                       LOCAL_FOLDER(folder)->rootpath);
1904
                if (item->collapsed && node->children)
1905
                        fputs(" collapsed=\"1\"", fp);
1906
                if (folder->account)
1907
                        fprintf(fp, " account_id=\"%d\"",
1908
                                folder->account->account_id);
1909
                else if (item->account)
1910
                        fprintf(fp, " account_id=\"%d\"",
1911
                                item->account->account_id);
1912
                if (item->ac_apply_sub)
1913
                        fputs(" account_apply_sub=\"1\"", fp);
1914
        } else {
1915
                fprintf(fp, "<folderitem type=\"%s\"",
1916
                        folder_item_stype_str[item->stype]);
1917
                if (item->name)
1918
                        PUT_ESCAPE_STR(fp, "name", item->name);
1919
                if (item->path)
1920
                        PUT_ESCAPE_STR(fp, "path", item->path);
1921

    
1922
                if (item->no_sub)
1923
                        fputs(" no_sub=\"1\"", fp);
1924
                if (item->no_select)
1925
                        fputs(" no_select=\"1\"", fp);
1926
                if (item->collapsed && node->children)
1927
                        fputs(" collapsed=\"1\"", fp);
1928
                if (item->threaded)
1929
                        fputs(" threaded=\"1\"", fp);
1930
                else
1931
                        fputs(" threaded=\"0\"", fp);
1932

    
1933
                if (item->sort_key != SORT_BY_NONE) {
1934
                        fprintf(fp, " sort_key=\"%s\"",
1935
                                sort_key_str[item->sort_key]);
1936
                        if (item->sort_type == SORT_ASCENDING)
1937
                                fprintf(fp, " sort_type=\"ascending\"");
1938
                        else
1939
                                fprintf(fp, " sort_type=\"descending\"");
1940
                }
1941
                if (item->qsearch_cond_type > 0 &&
1942
                    item->qsearch_cond_type < 10) {
1943
                        fprintf(fp, " qsearch_cond=\"%s\"",
1944
                                qsearch_cond_str[item->qsearch_cond_type]);
1945
                }
1946

    
1947
                fprintf(fp,
1948
                        " mtime=\"%lld\" new=\"%d\" unread=\"%d\" total=\"%d\"",
1949
                        (gint64)item->mtime, item->new, item->unread, item->total);
1950

    
1951
                if (item->account)
1952
                        fprintf(fp, " account_id=\"%d\"",
1953
                                item->account->account_id);
1954
                if (item->ac_apply_sub)
1955
                        fputs(" account_apply_sub=\"1\"", fp);
1956

    
1957
                if (item->auto_to)
1958
                        PUT_ESCAPE_STR(fp, "to", item->auto_to);
1959
                if (item->use_auto_to_on_reply)
1960
                        fputs(" use_auto_to_on_reply=\"1\"", fp);
1961
                if (item->auto_cc)
1962
                        PUT_ESCAPE_STR(fp, "cc", item->auto_cc);
1963
                if (item->auto_bcc)
1964
                        PUT_ESCAPE_STR(fp, "bcc", item->auto_bcc);
1965
                if (item->auto_replyto)
1966
                        PUT_ESCAPE_STR(fp, "replyto", item->auto_replyto);
1967

    
1968
                if (item->trim_summary_subject)
1969
                        fputs(" trim_summary_subject=\"1\"", fp);
1970
                if (item->trim_compose_subject)
1971
                        fputs(" trim_compose_subject=\"1\"", fp);
1972
        }
1973

    
1974
        if (node->children) {
1975
                GNode *child;
1976
                fputs(">\n", fp);
1977

    
1978
                child = node->children;
1979
                while (child) {
1980
                        GNode *cur;
1981

    
1982
                        cur = child;
1983
                        child = cur->next;
1984
                        folder_write_list_recursive(cur, data);
1985
                }
1986

    
1987
                for (i = 0; i < depth; i++)
1988
                        fputs("    ", fp);
1989
                fprintf(fp, "</%s>\n", depth == 1 ? "folder" : "folderitem");
1990
        } else
1991
                fputs(" />\n", fp);
1992
}
1993

    
1994
#undef PUT_ESCAPE_STR