Statistics
| Revision:

root / libsylph / mh.c @ 2164

History | View | Annotate | Download (37.7 KB)

1
/*
2
 * LibSylph -- E-Mail client library
3
 * Copyright (C) 1999-2007 Hiroyuki Yamamoto
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2.1 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
16
 * License along with this library; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
 */
19

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

    
24
#include "defs.h"
25

    
26
#include <glib.h>
27
#include <glib/gi18n.h>
28
#include <dirent.h>
29
#include <sys/stat.h>
30
#include <time.h>
31
#include <unistd.h>
32
#include <string.h>
33
#include <errno.h>
34

    
35
#ifdef G_OS_WIN32
36
#  include <windows.h>
37
#endif
38

    
39
#undef MEASURE_TIME
40

    
41
#include "sylmain.h"
42
#include "folder.h"
43
#include "mh.h"
44
#include "procmsg.h"
45
#include "procheader.h"
46
#include "utils.h"
47
#include "prefs_common.h"
48

    
49
static void        mh_folder_init                (Folder                *folder,
50
                                         const gchar        *name,
51
                                         const gchar        *path);
52

    
53
static Folder        *mh_folder_new                (const gchar        *name,
54
                                         const gchar        *path);
55
static void     mh_folder_destroy        (Folder                *folder);
56

    
57
static GSList  *mh_get_msg_list                (Folder                *folder,
58
                                         FolderItem        *item,
59
                                         gboolean         use_cache);
60
static GSList  *mh_get_uncached_msg_list(Folder                *folder,
61
                                         FolderItem        *item);
62
static gchar   *mh_fetch_msg                (Folder                *folder,
63
                                         FolderItem        *item,
64
                                         gint                 num);
65
static MsgInfo *mh_get_msginfo                (Folder                *folder,
66
                                         FolderItem        *item,
67
                                         gint                 num);
68
static gint     mh_add_msg                (Folder                *folder,
69
                                         FolderItem        *dest,
70
                                         const gchar        *file,
71
                                         MsgFlags        *flags,
72
                                         gboolean         remove_source);
73
static gint     mh_add_msgs                (Folder                *folder,
74
                                         FolderItem        *dest,
75
                                         GSList                *file_list,
76
                                         gboolean         remove_source,
77
                                         gint                *first);
78
static gint     mh_move_msg                (Folder                *folder,
79
                                         FolderItem        *dest,
80
                                         MsgInfo        *msginfo);
81
static gint     mh_move_msgs                (Folder                *folder,
82
                                         FolderItem        *dest,
83
                                         GSList                *msglist);
84
static gint     mh_copy_msg                (Folder                *folder,
85
                                         FolderItem        *dest,
86
                                         MsgInfo        *msginfo);
87
static gint     mh_copy_msgs                (Folder                *folder,
88
                                         FolderItem        *dest,
89
                                         GSList                *msglist);
90
static gint     mh_remove_msg                (Folder                *folder,
91
                                         FolderItem        *item,
92
                                         MsgInfo        *msginfo);
93
static gint     mh_remove_all_msg        (Folder                *folder,
94
                                         FolderItem        *item);
95
static gboolean mh_is_msg_changed        (Folder                *folder,
96
                                         FolderItem        *item,
97
                                         MsgInfo        *msginfo);
98
static gint    mh_close                        (Folder                *folder,
99
                                         FolderItem        *item);
100

    
101
static gint    mh_scan_folder_full        (Folder                *folder,
102
                                         FolderItem        *item,
103
                                         gboolean         count_sum);
104
static gint    mh_scan_folder                (Folder                *folder,
105
                                         FolderItem        *item);
106
static gint    mh_scan_tree                (Folder                *folder);
107

    
108
static gint    mh_create_tree                (Folder                *folder);
109
static FolderItem *mh_create_folder        (Folder                *folder,
110
                                         FolderItem        *parent,
111
                                         const gchar        *name);
112
static gint    mh_rename_folder                (Folder                *folder,
113
                                         FolderItem        *item,
114
                                         const gchar        *name);
115
static gint    mh_move_folder                (Folder                *folder,
116
                                         FolderItem        *item,
117
                                         FolderItem        *new_parent);
118
static gint    mh_remove_folder                (Folder                *folder,
119
                                         FolderItem        *item);
120

    
121
static gchar   *mh_get_new_msg_filename                (FolderItem        *dest);
122

    
123
static gint        mh_do_move_msgs                        (Folder                *folder,
124
                                                 FolderItem        *dest,
125
                                                 GSList                *msglist);
126

    
127
static time_t  mh_get_mtime                        (FolderItem        *item);
128
static GSList  *mh_get_uncached_msgs                (GHashTable        *msg_table,
129
                                                 FolderItem        *item);
130
static MsgInfo *mh_parse_msg                        (const gchar        *file,
131
                                                 FolderItem        *item);
132
static void        mh_remove_missing_folder_items        (Folder                *folder);
133
static void        mh_scan_tree_recursive                (FolderItem        *item);
134

    
135
static gboolean mh_rename_folder_func                (GNode                *node,
136
                                                 gpointer         data);
137

    
138
static FolderClass mh_class =
139
{
140
        F_MH,
141

    
142
        mh_folder_new,
143
        mh_folder_destroy,
144

    
145
        mh_scan_tree,
146
        mh_create_tree,
147

    
148
        mh_get_msg_list,
149
        mh_get_uncached_msg_list,
150
        mh_fetch_msg,
151
        mh_get_msginfo,
152
        mh_add_msg,
153
        mh_add_msgs,
154
        mh_move_msg,
155
        mh_move_msgs,
156
        mh_copy_msg,
157
        mh_copy_msgs,
158
        mh_remove_msg,
159
        NULL,
160
        mh_remove_all_msg,
161
        mh_is_msg_changed,
162
        mh_close,
163
        mh_scan_folder,
164

    
165
        mh_create_folder,
166
        mh_rename_folder,
167
        mh_move_folder,
168
        mh_remove_folder,
169
};
170

    
171

    
172
FolderClass *mh_get_class(void)
173
{
174
        return &mh_class;
175
}
176

    
177
static Folder *mh_folder_new(const gchar *name, const gchar *path)
178
{
179
        Folder *folder;
180

    
181
        folder = (Folder *)g_new0(MHFolder, 1);
182
        mh_folder_init(folder, name, path);
183

    
184
        return folder;
185
}
186

    
187
static void mh_folder_destroy(Folder *folder)
188
{
189
        folder_local_folder_destroy(LOCAL_FOLDER(folder));
190
}
191

    
192
static void mh_folder_init(Folder *folder, const gchar *name, const gchar *path)
193
{
194
        folder->klass = mh_get_class();
195
        folder_local_folder_init(folder, name, path);
196
}
197

    
198
static GSList *mh_get_msg_list_full(Folder *folder, FolderItem *item,
199
                                    gboolean use_cache, gboolean uncached_only)
200
{
201
        GSList *mlist;
202
        GHashTable *msg_table;
203
        time_t cur_mtime;
204
        GSList *newlist = NULL;
205
#ifdef MEASURE_TIME
206
        GTimer *timer;
207
#endif
208

    
209
        g_return_val_if_fail(item != NULL, NULL);
210

    
211
#ifdef MEASURE_TIME
212
        timer = g_timer_new();
213
#endif
214

    
215
        cur_mtime = mh_get_mtime(item);
216

    
217
        if (use_cache && item->mtime == cur_mtime) {
218
                debug_print("Folder is not modified.\n");
219
                mlist = procmsg_read_cache(item, FALSE);
220
                if (!mlist) {
221
                        mlist = mh_get_uncached_msgs(NULL, item);
222
                        if (mlist)
223
                                item->cache_dirty = TRUE;
224
                }
225
        } else if (use_cache) {
226
                GSList *cur, *next;
227
                gboolean strict_cache_check = prefs_common.strict_cache_check;
228

    
229
                if (item->stype == F_QUEUE || item->stype == F_DRAFT)
230
                        strict_cache_check = TRUE;
231

    
232
                mlist = procmsg_read_cache(item, strict_cache_check);
233
                msg_table = procmsg_msg_hash_table_create(mlist);
234
                newlist = mh_get_uncached_msgs(msg_table, item);
235
                if (newlist)
236
                        item->cache_dirty = TRUE;
237
                if (msg_table)
238
                        g_hash_table_destroy(msg_table);
239

    
240
                if (!strict_cache_check) {
241
                        /* remove nonexistent messages */
242
                        for (cur = mlist; cur != NULL; cur = next) {
243
                                MsgInfo *msginfo = (MsgInfo *)cur->data;
244
                                next = cur->next;
245
                                if (!MSG_IS_CACHED(msginfo->flags)) {
246
                                        debug_print("removing nonexistent message %d from cache\n", msginfo->msgnum);
247
                                        mlist = g_slist_remove(mlist, msginfo);
248
                                        procmsg_msginfo_free(msginfo);
249
                                        item->cache_dirty = TRUE;
250
                                        item->mark_dirty = TRUE;
251
                                }
252
                        }
253
                }
254

    
255
                mlist = g_slist_concat(mlist, newlist);
256
        } else {
257
                mlist = mh_get_uncached_msgs(NULL, item);
258
                item->cache_dirty = TRUE;
259
                newlist = mlist;
260
        }
261

    
262
        procmsg_set_flags(mlist, item);
263

    
264
        if (!uncached_only)
265
                mlist = procmsg_sort_msg_list(mlist, item->sort_key,
266
                                              item->sort_type);
267

    
268
        if (item->mark_queue)
269
                item->mark_dirty = TRUE;
270

    
271
#ifdef MEASURE_TIME
272
        g_timer_stop(timer);
273
        g_print("%s: %s: elapsed time: %f sec\n",
274
                G_STRFUNC, item->path, g_timer_elapsed(timer, NULL));
275
        g_timer_destroy(timer);
276
#endif
277
        debug_print("cache_dirty: %d, mark_dirty: %d\n",
278
                    item->cache_dirty, item->mark_dirty);
279

    
280
        if (!item->opened) {
281
                item->mtime = cur_mtime;
282
                if (item->cache_dirty)
283
                        procmsg_write_cache_list(item, mlist);
284
                if (item->mark_dirty)
285
                        procmsg_write_flags_list(item, mlist);
286
        }
287

    
288
        if (uncached_only) {
289
                GSList *cur;
290

    
291
                if (newlist == NULL) {
292
                        procmsg_msg_list_free(mlist);
293
                        return NULL;
294
                }
295
                if (mlist == newlist)
296
                        return newlist;
297
                for (cur = mlist; cur != NULL; cur = cur->next) {
298
                        if (cur->next == newlist) {
299
                                cur->next = NULL;
300
                                procmsg_msg_list_free(mlist);
301
                                return newlist;
302
                        }
303
                }
304
                procmsg_msg_list_free(mlist);
305
                return NULL;
306
        }
307

    
308
        return mlist;
309
}
310

    
311
static GSList *mh_get_msg_list(Folder *folder, FolderItem *item,
312
                               gboolean use_cache)
313
{
314
        return mh_get_msg_list_full(folder, item, use_cache, FALSE);
315
}
316

    
317
static GSList *mh_get_uncached_msg_list(Folder *folder, FolderItem *item)
318
{
319
        return mh_get_msg_list_full(folder, item, TRUE, TRUE);
320
}
321

    
322
static gchar *mh_fetch_msg(Folder *folder, FolderItem *item, gint num)
323
{
324
        gchar *path;
325
        gchar *file;
326

    
327
        g_return_val_if_fail(item != NULL, NULL);
328
        g_return_val_if_fail(num > 0, NULL);
329

    
330
        if (item->last_num < 0 || num > item->last_num) {
331
                mh_scan_folder(folder, item);
332
                if (item->last_num < 0) return NULL;
333
        }
334

    
335
        if (num > item->last_num)
336
                return NULL;
337

    
338
        path = folder_item_get_path(item);
339
        file = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
340
        g_free(path);
341
        if (!is_file_exist(file)) {
342
                g_free(file);
343
                return NULL;
344
        }
345

    
346
        return file;
347
}
348

    
349
static MsgInfo *mh_get_msginfo(Folder *folder, FolderItem *item, gint num)
350
{
351
        MsgInfo *msginfo;
352
        gchar *file;
353

    
354
        g_return_val_if_fail(item != NULL, NULL);
355
        g_return_val_if_fail(num > 0, NULL);
356

    
357
        file = mh_fetch_msg(folder, item, num);
358
        if (!file) return NULL;
359

    
360
        msginfo = mh_parse_msg(file, item);
361
        if (msginfo)
362
                msginfo->msgnum = num;
363

    
364
        g_free(file);
365

    
366
        return msginfo;
367
}
368

    
369
static gchar *mh_get_new_msg_filename(FolderItem *dest)
370
{
371
        gchar *destfile;
372
        gchar *destpath;
373

    
374
        destpath = folder_item_get_path(dest);
375
        g_return_val_if_fail(destpath != NULL, NULL);
376

    
377
        if (!is_dir_exist(destpath))
378
                make_dir_hier(destpath);
379

    
380
        for (;;) {
381
                destfile = g_strdup_printf("%s%c%d", destpath, G_DIR_SEPARATOR,
382
                                           dest->last_num + 1);
383
                if (is_file_entry_exist(destfile)) {
384
                        dest->last_num++;
385
                        g_free(destfile);
386
                } else
387
                        break;
388
        }
389

    
390
        g_free(destpath);
391

    
392
        return destfile;
393
}
394

    
395
#define SET_DEST_MSG_FLAGS(fp, dest, n, fl)                                \
396
{                                                                        \
397
        MsgInfo newmsginfo;                                                \
398
                                                                        \
399
        newmsginfo.msgnum = n;                                                \
400
        newmsginfo.flags = fl;                                                \
401
        if (dest->stype == F_OUTBOX ||                                        \
402
            dest->stype == F_QUEUE  ||                                        \
403
            dest->stype == F_DRAFT) {                                        \
404
                MSG_UNSET_PERM_FLAGS(newmsginfo.flags,                        \
405
                                     MSG_NEW|MSG_UNREAD|MSG_DELETED);        \
406
        } else if (dest->stype == F_TRASH) {                                \
407
                MSG_UNSET_PERM_FLAGS(newmsginfo.flags, MSG_DELETED);        \
408
        }                                                                \
409
                                                                        \
410
        if (fp)                                                                \
411
                procmsg_write_flags(&newmsginfo, fp);                        \
412
        else if (dest->opened)                                                \
413
                procmsg_add_flags(dest, n, newmsginfo.flags);                \
414
}
415

    
416
static gint mh_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
417
                       MsgFlags *flags, gboolean remove_source)
418
{
419
        GSList file_list;
420
        MsgFileInfo fileinfo;
421

    
422
        g_return_val_if_fail(file != NULL, -1);
423

    
424
        fileinfo.file = (gchar *)file;
425
        fileinfo.flags = flags;
426
        file_list.data = &fileinfo;
427
        file_list.next = NULL;
428

    
429
        return mh_add_msgs(folder, dest, &file_list, remove_source, NULL);
430
}
431

    
432
static gint mh_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
433
                        gboolean remove_source, gint *first)
434
{
435
        gchar *destfile;
436
        GSList *cur;
437
        MsgFileInfo *fileinfo;
438
        gint first_ = 0;
439
        FILE *fp;
440

    
441
        g_return_val_if_fail(dest != NULL, -1);
442
        g_return_val_if_fail(file_list != NULL, -1);
443

    
444
        if (dest->last_num < 0) {
445
                mh_scan_folder(folder, dest);
446
                if (dest->last_num < 0) return -1;
447
        }
448

    
449
        if ((((MsgFileInfo *)file_list->data)->flags == NULL &&
450
            file_list->next == NULL) || dest->opened)
451
                fp = NULL;
452
        else if ((fp = procmsg_open_mark_file(dest, DATA_APPEND)) == NULL)
453
                g_warning("Can't open mark file.\n");
454

    
455
        for (cur = file_list; cur != NULL; cur = cur->next) {
456
                fileinfo = (MsgFileInfo *)cur->data;
457

    
458
                destfile = mh_get_new_msg_filename(dest);
459
                if (destfile == NULL) return -1;
460
                if (first_ == 0 || first_ > dest->last_num + 1)
461
                        first_ = dest->last_num + 1;
462

    
463
                if (syl_link(fileinfo->file, destfile) < 0) {
464
                        if (copy_file(fileinfo->file, destfile, TRUE) < 0) {
465
                                g_warning(_("can't copy message %s to %s\n"),
466
                                          fileinfo->file, destfile);
467
                                g_free(destfile);
468
                                return -1;
469
                        }
470
                }
471

    
472
                g_signal_emit_by_name(syl_app_get(), "add-msg", dest, destfile, dest->last_num + 1);
473

    
474
                g_free(destfile);
475
                dest->last_num++;
476
                dest->total++;
477
                dest->updated = TRUE;
478
                dest->mtime = 0;
479

    
480
                if (fileinfo->flags) {
481
                        if (MSG_IS_RECEIVED(*fileinfo->flags)) {
482
                                if (dest->unmarked_num == 0)
483
                                        dest->new = 0;
484
                                dest->unmarked_num++;
485
                                procmsg_add_mark_queue(dest, dest->last_num,
486
                                                       *fileinfo->flags);
487
                        } else {
488
                                SET_DEST_MSG_FLAGS(fp, dest, dest->last_num,
489
                                                   *fileinfo->flags);
490
                        }
491
                        if (MSG_IS_NEW(*fileinfo->flags))
492
                                dest->new++;
493
                        if (MSG_IS_UNREAD(*fileinfo->flags))
494
                                dest->unread++;
495
                } else {
496
                        if (dest->unmarked_num == 0)
497
                                dest->new = 0;
498
                        dest->unmarked_num++;
499
                        dest->new++;
500
                        dest->unread++;
501
                }
502
        }
503

    
504
        if (fp) fclose(fp);
505

    
506
        if (first)
507
                *first = first_;
508

    
509
        if (remove_source) {
510
                for (cur = file_list; cur != NULL; cur = cur->next) {
511
                        fileinfo = (MsgFileInfo *)cur->data;
512
                        if (g_unlink(fileinfo->file) < 0)
513
                                FILE_OP_ERROR(fileinfo->file, "unlink");
514
                }
515
        }
516

    
517
        return dest->last_num;
518
}
519

    
520
static gint mh_do_move_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
521
{
522
        FolderItem *src;
523
        gchar *srcfile;
524
        gchar *destfile;
525
        FILE *fp;
526
        GSList *cur;
527
        MsgInfo *msginfo;
528

    
529
        g_return_val_if_fail(dest != NULL, -1);
530
        g_return_val_if_fail(msglist != NULL, -1);
531

    
532
        if (dest->last_num < 0) {
533
                mh_scan_folder(folder, dest);
534
                if (dest->last_num < 0) return -1;
535
        }
536

    
537
        if (dest->opened)
538
                fp = NULL;
539
        else if ((fp = procmsg_open_mark_file(dest, DATA_APPEND)) == NULL)
540
                g_warning(_("Can't open mark file.\n"));
541

    
542
        for (cur = msglist; cur != NULL; cur = cur->next) {
543
                msginfo = (MsgInfo *)cur->data;
544
                src = msginfo->folder;
545

    
546
                if (src == dest) {
547
                        g_warning(_("the src folder is identical to the dest.\n"));
548
                        continue;
549
                }
550
                debug_print("Moving message %s/%d to %s ...\n",
551
                            src->path, msginfo->msgnum, dest->path);
552

    
553
                destfile = mh_get_new_msg_filename(dest);
554
                if (!destfile) break;
555
                srcfile = procmsg_get_message_file(msginfo);
556

    
557
                g_signal_emit_by_name(syl_app_get(), "remove-msg", src, srcfile, msginfo->msgnum);
558

    
559
                if (move_file(srcfile, destfile, FALSE) < 0) {
560
                        g_free(srcfile);
561
                        g_free(destfile);
562
                        break;
563
                }
564

    
565
                g_signal_emit_by_name(syl_app_get(), "add-msg", dest, destfile, dest->last_num + 1);
566

    
567
                g_free(srcfile);
568
                g_free(destfile);
569
                src->total--;
570
                src->updated = TRUE;
571
                src->mtime = 0;
572
                dest->last_num++;
573
                dest->total++;
574
                dest->updated = TRUE;
575
                dest->mtime = 0;
576

    
577
                if (fp) {
578
                        SET_DEST_MSG_FLAGS(fp, dest, dest->last_num,
579
                                           msginfo->flags);
580
                }
581

    
582
                if (MSG_IS_NEW(msginfo->flags)) {
583
                        src->new--;
584
                        dest->new++;
585
                }
586
                if (MSG_IS_UNREAD(msginfo->flags)) {
587
                        src->unread--;
588
                        dest->unread++;
589
                }
590

    
591
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID);
592
        }
593

    
594
        if (fp) fclose(fp);
595

    
596
        return dest->last_num;
597
}
598

    
599
static gint mh_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
600
{
601
        GSList msglist;
602

    
603
        g_return_val_if_fail(msginfo != NULL, -1);
604

    
605
        msglist.data = msginfo;
606
        msglist.next = NULL;
607

    
608
        return mh_move_msgs(folder, dest, &msglist);
609
}
610

    
611
static gint mh_move_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
612
{
613
        MsgInfo *msginfo;
614
        GSList *file_list;
615
        gint ret = 0;
616
        gint first;
617

    
618
        msginfo = (MsgInfo *)msglist->data;
619
        if (folder == msginfo->folder->folder)
620
                return mh_do_move_msgs(folder, dest, msglist);
621

    
622
        file_list = procmsg_get_message_file_list(msglist);
623
        g_return_val_if_fail(file_list != NULL, -1);
624

    
625
        ret = mh_add_msgs(folder, dest, file_list, FALSE, &first);
626

    
627
        procmsg_message_file_list_free(file_list);
628

    
629
        if (ret != -1)
630
                ret = folder_item_remove_msgs(msginfo->folder, msglist);
631

    
632
        return ret;
633
}
634

    
635
static gint mh_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
636
{
637
        GSList msglist;
638

    
639
        g_return_val_if_fail(msginfo != NULL, -1);
640

    
641
        msglist.data = msginfo;
642
        msglist.next = NULL;
643

    
644
        return mh_copy_msgs(folder, dest, &msglist);
645
}
646

    
647
static gint mh_copy_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
648
{
649
        gchar *srcfile;
650
        gchar *destfile;
651
        FILE *fp;
652
        GSList *cur;
653
        MsgInfo *msginfo;
654

    
655
        g_return_val_if_fail(dest != NULL, -1);
656
        g_return_val_if_fail(msglist != NULL, -1);
657

    
658
        if (dest->last_num < 0) {
659
                mh_scan_folder(folder, dest);
660
                if (dest->last_num < 0) return -1;
661
        }
662

    
663
        if (dest->opened)
664
                fp = NULL;
665
        else if ((fp = procmsg_open_mark_file(dest, DATA_APPEND)) == NULL)
666
                g_warning(_("Can't open mark file.\n"));
667

    
668
        for (cur = msglist; cur != NULL; cur = cur->next) {
669
                msginfo = (MsgInfo *)cur->data;
670

    
671
                if (msginfo->folder == dest) {
672
                        g_warning(_("the src folder is identical to the dest.\n"));
673
                        continue;
674
                }
675
                debug_print(_("Copying message %s/%d to %s ...\n"),
676
                            msginfo->folder->path, msginfo->msgnum, dest->path);
677

    
678
                destfile = mh_get_new_msg_filename(dest);
679
                if (!destfile) break;
680
                srcfile = procmsg_get_message_file(msginfo);
681

    
682
                if (copy_file(srcfile, destfile, TRUE) < 0) {
683
                        FILE_OP_ERROR(srcfile, "copy");
684
                        g_free(srcfile);
685
                        g_free(destfile);
686
                        break;
687
                }
688

    
689
                g_signal_emit_by_name(syl_app_get(), "add-msg", dest, destfile, dest->last_num + 1);
690

    
691
                g_free(srcfile);
692
                g_free(destfile);
693
                dest->last_num++;
694
                dest->total++;
695
                dest->updated = TRUE;
696
                dest->mtime = 0;
697

    
698
                if (fp) {
699
                        SET_DEST_MSG_FLAGS(fp, dest, dest->last_num,
700
                                           msginfo->flags);
701
                }
702

    
703
                if (MSG_IS_NEW(msginfo->flags))
704
                        dest->new++;
705
                if (MSG_IS_UNREAD(msginfo->flags))
706
                        dest->unread++;
707
        }
708

    
709
        if (fp) fclose(fp);
710

    
711
        return dest->last_num;
712
}
713

    
714
static gint mh_remove_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo)
715
{
716
        gchar *file;
717

    
718
        g_return_val_if_fail(item != NULL, -1);
719

    
720
        file = mh_fetch_msg(folder, item, msginfo->msgnum);
721
        g_return_val_if_fail(file != NULL, -1);
722

    
723
        g_signal_emit_by_name(syl_app_get(), "remove-msg", item, file, msginfo->msgnum);
724

    
725
        if (g_unlink(file) < 0) {
726
                FILE_OP_ERROR(file, "unlink");
727
                g_free(file);
728
                return -1;
729
        }
730
        g_free(file);
731

    
732
        item->total--;
733
        item->updated = TRUE;
734
        item->mtime = 0;
735
        if (MSG_IS_NEW(msginfo->flags))
736
                item->new--;
737
        if (MSG_IS_UNREAD(msginfo->flags))
738
                item->unread--;
739
        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID);
740

    
741
        if (msginfo->msgnum == item->last_num)
742
                mh_scan_folder_full(folder, item, FALSE);
743

    
744
        return 0;
745
}
746

    
747
static gint mh_remove_all_msg(Folder *folder, FolderItem *item)
748
{
749
        gchar *path;
750
        gint val;
751

    
752
        g_return_val_if_fail(item != NULL, -1);
753

    
754
        path = folder_item_get_path(item);
755
        g_return_val_if_fail(path != NULL, -1);
756
        g_signal_emit_by_name(syl_app_get(), "remove-all-msg", item);
757
        val = remove_all_numbered_files(path);
758
        g_free(path);
759
        if (val == 0) {
760
                item->new = item->unread = item->total = 0;
761
                item->last_num = 0;
762
                item->updated = TRUE;
763
                item->mtime = 0;
764
        }
765

    
766
        return val;
767
}
768

    
769
static gboolean mh_is_msg_changed(Folder *folder, FolderItem *item,
770
                                  MsgInfo *msginfo)
771
{
772
        struct stat s;
773

    
774
        if (g_stat(itos(msginfo->msgnum), &s) < 0 ||
775
            msginfo->size  != s.st_size ||
776
            msginfo->mtime != s.st_mtime)
777
                return TRUE;
778

    
779
        return FALSE;
780
}
781

    
782
static gint mh_close(Folder *folder, FolderItem *item)
783
{
784
        return 0;
785
}
786

    
787
#ifdef G_OS_WIN32
788
struct wfddata {
789
        WIN32_FIND_DATAA wfda;
790
        WIN32_FIND_DATAW wfdw;
791
        DWORD file_attr;
792
        gchar *file_name;
793
};
794

    
795
static HANDLE find_first_file(const gchar *path, struct wfddata *wfd)
796
{
797
        HANDLE hfind;
798

    
799
        if (G_WIN32_HAVE_WIDECHAR_API()) {
800
                if (path) {
801
                        gchar *wildcard_path;
802
                        wchar_t *wpath;
803

    
804
                        wildcard_path = g_strconcat(path, "\\*", NULL);
805
                        wpath = g_utf8_to_utf16(wildcard_path, -1,
806
                                                NULL, NULL, NULL);
807
                        if (wpath) {
808
                                hfind = FindFirstFileW(wpath, &wfd->wfdw);
809
                                g_free(wpath);
810
                        } else
811
                                hfind = INVALID_HANDLE_VALUE;
812
                        g_free(wildcard_path);
813
                } else
814
                        hfind = FindFirstFileW(L"*", &wfd->wfdw);
815

    
816
                if (hfind != INVALID_HANDLE_VALUE) {
817
                        wfd->file_attr = wfd->wfdw.dwFileAttributes;
818
                        wfd->file_name = g_utf16_to_utf8(wfd->wfdw.cFileName, -1,
819
                                                         NULL, NULL, NULL);
820
                }
821
        } else {
822
                if (path) {
823
                        gchar *wildcard_path;
824
                        gchar *cp_path;
825

    
826
                        wildcard_path = g_strconcat(path, "\\*", NULL);
827
                        cp_path = g_locale_from_utf8(wildcard_path, -1,
828
                                                     NULL, NULL, NULL);
829
                        if (cp_path) {
830
                                hfind = FindFirstFileA(cp_path, &wfd->wfda);
831
                                g_free(cp_path);
832
                        } else
833
                                hfind = INVALID_HANDLE_VALUE;
834
                        g_free(wildcard_path);
835
                } else
836
                        hfind = FindFirstFileA("*", &wfd->wfda);
837

    
838
                if (hfind != INVALID_HANDLE_VALUE) {
839
                        wfd->file_attr = wfd->wfda.dwFileAttributes;
840
                        wfd->file_name = g_locale_to_utf8(wfd->wfda.cFileName,
841
                                                          -1, NULL, NULL, NULL);
842
                }
843
        }
844

    
845
        return hfind;
846
}
847

    
848
static BOOL find_next_file(HANDLE hfind, struct wfddata *wfd)
849
{
850
        BOOL retval;
851

    
852
        if (G_WIN32_HAVE_WIDECHAR_API()) {
853
                retval = FindNextFileW(hfind, &wfd->wfdw);
854
                if (retval) {
855
                        wfd->file_attr = wfd->wfdw.dwFileAttributes;
856
                        wfd->file_name = g_utf16_to_utf8(wfd->wfdw.cFileName, -1,
857
                                                         NULL, NULL, NULL);
858
                }
859
        } else {
860
                retval = FindNextFileA(hfind, &wfd->wfda);
861
                if (retval) {
862
                        wfd->file_attr = wfd->wfda.dwFileAttributes;
863
                        wfd->file_name = g_locale_to_utf8(wfd->wfda.cFileName,
864
                                                          -1, NULL, NULL, NULL);
865
                }
866
        }
867

    
868
        return retval;
869
}
870
#endif
871

    
872
static gint mh_scan_folder_full(Folder *folder, FolderItem *item,
873
                                gboolean count_sum)
874
{
875
        gchar *path;
876
#ifdef G_OS_WIN32
877
        struct wfddata wfd;
878
        HANDLE hfind;
879
#else
880
        DIR *dp;
881
        struct dirent *d;
882
#endif
883
        gint max = 0;
884
        gint num;
885
        gint n_msg = 0;
886

    
887
        g_return_val_if_fail(item != NULL, -1);
888

    
889
        debug_print("mh_scan_folder(): Scanning %s ...\n", item->path);
890

    
891
        path = folder_item_get_path(item);
892
        g_return_val_if_fail(path != NULL, -1);
893
        if (change_dir(path) < 0) {
894
                g_free(path);
895
                return -1;
896
        }
897
        g_free(path);
898

    
899
#ifdef G_OS_WIN32
900
        if ((hfind = find_first_file(NULL, &wfd)) == INVALID_HANDLE_VALUE) {
901
                g_warning("failed to open directory\n");
902
#else
903
        if ((dp = opendir(".")) == NULL) {
904
                FILE_OP_ERROR(item->path, "opendir");
905
#endif
906
                return -1;
907
        }
908

    
909
        if (folder->ui_func)
910
                folder->ui_func(folder, item, folder->ui_func_data);
911

    
912
#ifdef G_OS_WIN32
913
        do {
914
                if ((wfd.file_attr &
915
                     (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0) {
916
                        if (wfd.file_name) {
917
                                if ((num = to_number(wfd.file_name)) > 0) {
918
                                        n_msg++;
919
                                        if (max < num)
920
                                                max = num;
921
                                }
922
                        }
923
                }
924

    
925
                if (wfd.file_name) {
926
                        g_free(wfd.file_name);
927
                        wfd.file_name = NULL;
928
                }
929
        } while (find_next_file(hfind, &wfd));
930

    
931
        FindClose(hfind);
932
#else
933
        while ((d = readdir(dp)) != NULL) {
934
                if ((num = to_number(d->d_name)) > 0 &&
935
                    dirent_is_regular_file(d)) {
936
                        n_msg++;
937
                        if (max < num)
938
                                max = num;
939
                }
940
        }
941

    
942
        closedir(dp);
943
#endif
944

    
945
        if (n_msg == 0)
946
                item->new = item->unread = item->total = 0;
947
        else if (count_sum) {
948
                gint new, unread, total, min, max_;
949

    
950
                procmsg_get_mark_sum
951
                        (item, &new, &unread, &total, &min, &max_, 0);
952

    
953
                if (n_msg > total) {
954
                        item->unmarked_num = new = n_msg - total;
955
                        unread += n_msg - total;
956
                } else
957
                        item->unmarked_num = 0;
958

    
959
                item->new = new;
960
                item->unread = unread;
961
                item->total = n_msg;
962
        }
963

    
964
        item->updated = TRUE;
965
        item->mtime = 0;
966

    
967
        debug_print("Last number in dir %s = %d\n", item->path, max);
968
        item->last_num = max;
969

    
970
        return 0;
971
}
972

    
973
static gint mh_scan_folder(Folder *folder, FolderItem *item)
974
{
975
        return mh_scan_folder_full(folder, item, TRUE);
976
}
977

    
978
static gint mh_scan_tree(Folder *folder)
979
{
980
        FolderItem *item;
981
        gchar *rootpath;
982

    
983
        g_return_val_if_fail(folder != NULL, -1);
984

    
985
        if (!folder->node) {
986
                item = folder_item_new(folder->name, NULL);
987
                item->folder = folder;
988
                folder->node = item->node = g_node_new(item);
989
        } else
990
                item = FOLDER_ITEM(folder->node->data);
991

    
992
        rootpath = folder_item_get_path(item);
993
        if (change_dir(rootpath) < 0) {
994
                g_free(rootpath);
995
                return -1;
996
        }
997
        g_free(rootpath);
998

    
999
        mh_create_tree(folder);
1000
        mh_remove_missing_folder_items(folder);
1001
        mh_scan_tree_recursive(item);
1002

    
1003
        return 0;
1004
}
1005

    
1006
#define MAKE_DIR_IF_NOT_EXIST(dir) \
1007
{ \
1008
        if (!is_dir_exist(dir)) { \
1009
                if (is_file_exist(dir)) { \
1010
                        g_warning(_("File `%s' already exists.\n" \
1011
                                    "Can't create folder."), dir); \
1012
                        return -1; \
1013
                } \
1014
                if (make_dir(dir) < 0) \
1015
                        return -1; \
1016
        } \
1017
}
1018

    
1019
#define MAKE_DIR_HIER_IF_NOT_EXIST(dir) \
1020
{ \
1021
        if (!is_dir_exist(dir)) { \
1022
                if (is_file_exist(dir)) { \
1023
                        g_warning(_("File `%s' already exists.\n" \
1024
                                    "Can't create folder."), dir); \
1025
                        return -1; \
1026
                } \
1027
                if (make_dir_hier(dir) < 0) \
1028
                        return -1; \
1029
        } \
1030
}
1031

    
1032
static gint mh_create_tree(Folder *folder)
1033
{
1034
        gchar *rootpath;
1035

    
1036
        g_return_val_if_fail(folder != NULL, -1);
1037

    
1038
        CHDIR_RETURN_VAL_IF_FAIL(get_mail_base_dir(), -1);
1039
        rootpath = LOCAL_FOLDER(folder)->rootpath;
1040
        MAKE_DIR_HIER_IF_NOT_EXIST(rootpath);
1041
        CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
1042
        MAKE_DIR_IF_NOT_EXIST(INBOX_DIR);
1043
        MAKE_DIR_IF_NOT_EXIST(OUTBOX_DIR);
1044
        MAKE_DIR_IF_NOT_EXIST(QUEUE_DIR);
1045
        MAKE_DIR_IF_NOT_EXIST(DRAFT_DIR);
1046
        MAKE_DIR_IF_NOT_EXIST(TRASH_DIR);
1047

    
1048
        return 0;
1049
}
1050

    
1051
#undef MAKE_DIR_IF_NOT_EXIST
1052
#undef MAKE_DIR_HIER_IF_NOT_EXIST
1053

    
1054
static FolderItem *mh_create_folder(Folder *folder, FolderItem *parent,
1055
                                    const gchar *name)
1056
{
1057
        gchar *path;
1058
        gchar *fs_name;
1059
        gchar *fullpath;
1060
        FolderItem *new_item;
1061

    
1062
        g_return_val_if_fail(folder != NULL, NULL);
1063
        g_return_val_if_fail(parent != NULL, NULL);
1064
        g_return_val_if_fail(name != NULL, NULL);
1065

    
1066
        path = folder_item_get_path(parent);
1067
        fs_name = g_filename_from_utf8(name, -1, NULL, NULL, NULL);
1068
        fullpath = g_strconcat(path, G_DIR_SEPARATOR_S,
1069
                               fs_name ? fs_name : name, NULL);
1070
        g_free(fs_name);
1071
        g_free(path);
1072

    
1073
        if (make_dir_hier(fullpath) < 0) {
1074
                g_free(fullpath);
1075
                return NULL;
1076
        }
1077

    
1078
        g_free(fullpath);
1079

    
1080
        /* path is a logical folder path */
1081
        if (parent->path)
1082
                path = g_strconcat(parent->path, "/", name, NULL);
1083
        else
1084
                path = g_strdup(name);
1085
        new_item = folder_item_new(name, path);
1086
        folder_item_append(parent, new_item);
1087
        g_free(path);
1088

    
1089
        return new_item;
1090
}
1091

    
1092
static gint mh_move_folder_real(Folder *folder, FolderItem *item,
1093
                                FolderItem *new_parent, const gchar *name)
1094
{
1095
        gchar *rootpath;
1096
        gchar *oldpath;
1097
        gchar *newpath;
1098
        gchar *dirname;
1099
        gchar *new_dir;
1100
        gchar *name_;
1101
        gchar *utf8_name;
1102
        gchar *paths[2];
1103
        gchar *old_id, *new_id;
1104

    
1105
        g_return_val_if_fail(folder != NULL, -1);
1106
        g_return_val_if_fail(item != NULL, -1);
1107
        g_return_val_if_fail(folder == item->folder, -1);
1108
        g_return_val_if_fail(item->path != NULL, -1);
1109
        g_return_val_if_fail(new_parent != NULL || name != NULL, -1);
1110
        if (new_parent) {
1111
                g_return_val_if_fail(item != new_parent, -1);
1112
                g_return_val_if_fail(item->parent != new_parent, -1);
1113
                g_return_val_if_fail(item->folder == new_parent->folder, -1);
1114
                if (g_node_is_ancestor(item->node, new_parent->node)) {
1115
                        g_warning("folder to be moved is ancestor of new parent\n");
1116
                        return -1;
1117
                }
1118
        }
1119

    
1120
        oldpath = folder_item_get_path(item);
1121
        if (new_parent) {
1122
                if (name) {
1123
                        name_ = g_filename_from_utf8(name, -1, NULL, NULL,
1124
                                                     NULL);
1125
                        if (!name_)
1126
                                name_ = g_strdup(name);
1127
                        utf8_name = g_strdup(name);
1128
                } else {
1129
                        name_ = g_path_get_basename(oldpath);
1130
                        utf8_name = g_filename_to_utf8(name_, -1, NULL, NULL,
1131
                                                       NULL);
1132
                        if (!utf8_name)
1133
                                utf8_name = g_strdup(name_);
1134
                }
1135
                new_dir = folder_item_get_path(new_parent);
1136
                newpath = g_strconcat(new_dir, G_DIR_SEPARATOR_S, name_, NULL);
1137
                g_free(new_dir);
1138
        } else {
1139
                name_ = g_filename_from_utf8(name, -1, NULL, NULL, NULL);
1140
                utf8_name = g_strdup(name);
1141
                dirname = g_dirname(oldpath);
1142
                newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S,
1143
                                      name_ ? name_ : name, NULL);
1144
                g_free(dirname);
1145
        }
1146
        g_free(name_);
1147

    
1148
        if (is_file_entry_exist(newpath)) {
1149
                g_warning("%s already exists\n", newpath);
1150
                g_free(oldpath);
1151
                g_free(newpath);
1152
                g_free(utf8_name);
1153
                return -1;
1154
        }
1155

    
1156
        rootpath = folder_get_path(folder);
1157
        if (change_dir(rootpath) < 0) {
1158
                g_free(rootpath);
1159
                g_free(oldpath);
1160
                g_free(newpath);
1161
                g_free(utf8_name);
1162
                return -1;
1163
        }
1164
        g_free(rootpath);
1165

    
1166
        debug_print("mh_move_folder: rename(%s, %s)\n", oldpath, newpath);
1167

    
1168
        if (g_rename(oldpath, newpath) < 0) {
1169
                FILE_OP_ERROR(oldpath, "rename");
1170
                g_free(oldpath);
1171
                g_free(newpath);
1172
                g_free(utf8_name);
1173
                return -1;
1174
        }
1175

    
1176
        g_free(oldpath);
1177
        g_free(newpath);
1178

    
1179
        old_id = folder_item_get_identifier(item);
1180

    
1181
        if (new_parent) {
1182
                g_node_unlink(item->node);
1183
                g_node_append(new_parent->node, item->node);
1184
                item->parent = new_parent;
1185
                if (new_parent->path != NULL) {
1186
                        newpath = g_strconcat(new_parent->path, "/", utf8_name,
1187
                                              NULL);
1188
                        g_free(utf8_name);
1189
                } else
1190
                        newpath = utf8_name;
1191
        } else {
1192
                if (strchr(item->path, '/') != NULL) {
1193
                        dirname = g_dirname(item->path);
1194
                        newpath = g_strconcat(dirname, "/", utf8_name, NULL);
1195
                        g_free(dirname);
1196
                        g_free(utf8_name);
1197
                } else
1198
                        newpath = utf8_name;
1199
        }
1200

    
1201
        if (name) {
1202
                g_free(item->name);
1203
                item->name = g_strdup(name);
1204
        }
1205

    
1206
        paths[0] = g_strdup(item->path);
1207
        paths[1] = newpath;
1208
        g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1209
                        mh_rename_folder_func, paths);
1210

    
1211
        g_free(paths[0]);
1212
        g_free(paths[1]);
1213

    
1214
        new_id = folder_item_get_identifier(item);
1215
        g_signal_emit_by_name(syl_app_get(), "move-folder", item, old_id,
1216
                              new_id);
1217
        g_free(new_id);
1218
        g_free(old_id);
1219

    
1220
        return 0;
1221
}
1222

    
1223
static gint mh_move_folder(Folder *folder, FolderItem *item,
1224
                           FolderItem *new_parent)
1225
{
1226
        return mh_move_folder_real(folder, item, new_parent, NULL);
1227
}
1228

    
1229
static gint mh_rename_folder(Folder *folder, FolderItem *item,
1230
                             const gchar *name)
1231
{
1232
        return mh_move_folder_real(folder, item, NULL, name);
1233
}
1234

    
1235
static gint mh_remove_folder(Folder *folder, FolderItem *item)
1236
{
1237
        gchar *path;
1238

    
1239
        g_return_val_if_fail(folder != NULL, -1);
1240
        g_return_val_if_fail(item != NULL, -1);
1241
        g_return_val_if_fail(item->path != NULL, -1);
1242

    
1243
        path = folder_item_get_path(item);
1244
        if (remove_dir_recursive(path) < 0) {
1245
                g_warning("can't remove directory `%s'\n", path);
1246
                g_free(path);
1247
                return -1;
1248
        }
1249

    
1250
        g_free(path);
1251
        g_signal_emit_by_name(syl_app_get(), "remove-folder", item);
1252
        folder_item_remove(item);
1253
        return 0;
1254
}
1255

    
1256

    
1257
static time_t mh_get_mtime(FolderItem *item)
1258
{
1259
        gchar *path;
1260
        struct stat s;
1261

    
1262
        path = folder_item_get_path(item);
1263
        if (g_stat(path, &s) < 0) {
1264
                FILE_OP_ERROR(path, "stat");
1265
                return -1;
1266
        } else {
1267
                return MAX(s.st_mtime, s.st_ctime);
1268
        }
1269
}
1270

    
1271
static GSList *mh_get_uncached_msgs(GHashTable *msg_table, FolderItem *item)
1272
{
1273
        gchar *path;
1274
        GDir *dp;
1275
        const gchar *dir_name;
1276
        GSList *newlist = NULL;
1277
        GSList *last = NULL;
1278
        MsgInfo *msginfo;
1279
        gint n_newmsg = 0;
1280
        gint num;
1281

    
1282
        g_return_val_if_fail(item != NULL, NULL);
1283

    
1284
        path = folder_item_get_path(item);
1285
        g_return_val_if_fail(path != NULL, NULL);
1286
        if (change_dir(path) < 0) {
1287
                g_free(path);
1288
                return NULL;
1289
        }
1290
        g_free(path);
1291

    
1292
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
1293
                FILE_OP_ERROR(item->path, "opendir");
1294
                return NULL;
1295
        }
1296

    
1297
        debug_print("Searching uncached messages...\n");
1298

    
1299
        if (msg_table) {
1300
                while ((dir_name = g_dir_read_name(dp)) != NULL) {
1301
                        if ((num = to_number(dir_name)) <= 0) continue;
1302

    
1303
                        msginfo = g_hash_table_lookup
1304
                                (msg_table, GUINT_TO_POINTER(num));
1305

    
1306
                        if (msginfo) {
1307
                                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_CACHED);
1308
                        } else {
1309
                                /* not found in the cache (uncached message) */
1310
                                msginfo = mh_parse_msg(dir_name, item);
1311
                                if (!msginfo) continue;
1312

    
1313
                                if (!newlist)
1314
                                        last = newlist =
1315
                                                g_slist_append(NULL, msginfo);
1316
                                else {
1317
                                        last = g_slist_append(last, msginfo);
1318
                                        last = last->next;
1319
                                }
1320
                                n_newmsg++;
1321
                        }
1322
                }
1323
        } else {
1324
                /* discard all previous cache */
1325
                while ((dir_name = g_dir_read_name(dp)) != NULL) {
1326
                        if (to_number(dir_name) <= 0) continue;
1327

    
1328
                        msginfo = mh_parse_msg(dir_name, item);
1329
                        if (!msginfo) continue;
1330

    
1331
                        if (!newlist)
1332
                                last = newlist = g_slist_append(NULL, msginfo);
1333
                        else {
1334
                                last = g_slist_append(last, msginfo);
1335
                                last = last->next;
1336
                        }
1337
                        n_newmsg++;
1338
                }
1339
        }
1340

    
1341
        g_dir_close(dp);
1342

    
1343
        if (n_newmsg)
1344
                debug_print("%d uncached message(s) found.\n", n_newmsg);
1345
        else
1346
                debug_print("done.\n");
1347

    
1348
        /* sort new messages in numerical order */
1349
        if (newlist && item->sort_key == SORT_BY_NONE) {
1350
                debug_print("Sorting uncached messages in numerical order...\n");
1351
                newlist = g_slist_sort
1352
                        (newlist, (GCompareFunc)procmsg_cmp_msgnum_for_sort);
1353
                debug_print("done.\n");
1354
        }
1355

    
1356
        return newlist;
1357
}
1358

    
1359
static MsgInfo *mh_parse_msg(const gchar *file, FolderItem *item)
1360
{
1361
        MsgInfo *msginfo;
1362
        MsgFlags flags;
1363

    
1364
        g_return_val_if_fail(item != NULL, NULL);
1365
        g_return_val_if_fail(file != NULL, NULL);
1366

    
1367
        flags.perm_flags = MSG_NEW|MSG_UNREAD;
1368
        flags.tmp_flags = 0;
1369

    
1370
        if (item->stype == F_QUEUE) {
1371
                MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
1372
        } else if (item->stype == F_DRAFT) {
1373
                MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
1374
        }
1375

    
1376
        msginfo = procheader_parse_file(file, flags, FALSE);
1377
        if (!msginfo) return NULL;
1378

    
1379
        msginfo->msgnum = atoi(file);
1380
        msginfo->folder = item;
1381

    
1382
        return msginfo;
1383
}
1384

    
1385
#if 0
1386
static gboolean mh_is_maildir_one(const gchar *path, const gchar *dir)
1387
{
1388
        gchar *entry;
1389
        gboolean result;
1390

1391
        entry = g_strconcat(path, G_DIR_SEPARATOR_S, dir, NULL);
1392
        result = is_dir_exist(entry);
1393
        g_free(entry);
1394

1395
        return result;
1396
}
1397

1398
/*
1399
 * check whether PATH is a Maildir style mailbox.
1400
 * This is the case if the 3 subdir: new, cur, tmp are existing.
1401
 * This functon assumes that entry is an directory
1402
 */
1403
static gboolean mh_is_maildir(const gchar *path)
1404
{
1405
        return mh_is_maildir_one(path, "new") &&
1406
               mh_is_maildir_one(path, "cur") &&
1407
               mh_is_maildir_one(path, "tmp");
1408
}
1409
#endif
1410

    
1411
static gboolean mh_remove_missing_folder_items_func(GNode *node, gpointer data)
1412
{
1413
        FolderItem *item;
1414
        gchar *path;
1415

    
1416
        g_return_val_if_fail(node->data != NULL, FALSE);
1417

    
1418
        if (G_NODE_IS_ROOT(node))
1419
                return FALSE;
1420

    
1421
        item = FOLDER_ITEM(node->data);
1422

    
1423
#if 0
1424
        if (item->path && strchr(item->path, '/')) {
1425
                debug_print("folder '%s' includes Unix path separator. removing...\n", item->path);
1426
                folder_item_remove(item);
1427
                return FALSE;
1428
        }
1429
#endif
1430

    
1431
        path = folder_item_get_path(item);
1432
        if (!is_dir_exist(path)) {
1433
                debug_print("folder '%s' not found. removing...\n", path);
1434
                folder_item_remove(item);
1435
        }
1436
        g_free(path);
1437

    
1438
        return FALSE;
1439
}
1440

    
1441
static void mh_remove_missing_folder_items(Folder *folder)
1442
{
1443
        g_return_if_fail(folder != NULL);
1444

    
1445
        debug_print("searching missing folders...\n");
1446

    
1447
        g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
1448
                        mh_remove_missing_folder_items_func, folder);
1449
}
1450

    
1451
static void mh_scan_tree_recursive(FolderItem *item)
1452
{
1453
        Folder *folder;
1454
#ifdef G_OS_WIN32
1455
        struct wfddata wfd;
1456
        HANDLE hfind;
1457
#else
1458
        DIR *dp;
1459
        struct dirent *d;
1460
        struct stat s;
1461
#endif
1462
        const gchar *dir_name;
1463
        gchar *fs_path;
1464
        gchar *entry;
1465
        gchar *utf8entry;
1466
        gchar *utf8name;
1467
        gint n_msg = 0;
1468

    
1469
        g_return_if_fail(item != NULL);
1470
        g_return_if_fail(item->folder != NULL);
1471

    
1472
        if (item->stype == F_VIRTUAL)
1473
                return;
1474

    
1475
        folder = item->folder;
1476

    
1477
        fs_path = item->path ?
1478
                g_filename_from_utf8(item->path, -1, NULL, NULL, NULL)
1479
                : g_strdup(".");
1480
        if (!fs_path)
1481
                fs_path = g_strdup(item->path);
1482
#ifdef G_OS_WIN32
1483
        hfind = find_first_file(fs_path, &wfd);
1484
        if (hfind == INVALID_HANDLE_VALUE) {
1485
                g_warning("failed to open directory: %s\n", fs_path);
1486
                g_free(fs_path);
1487
                return;
1488
        }
1489
#else
1490
        dp = opendir(fs_path);
1491
        if (!dp) {
1492
                FILE_OP_ERROR(fs_path, "opendir");
1493
                g_free(fs_path);
1494
                return;
1495
        }
1496
#endif
1497
        g_free(fs_path);
1498

    
1499
        debug_print("scanning %s ...\n",
1500
                    item->path ? item->path
1501
                    : LOCAL_FOLDER(item->folder)->rootpath);
1502
        if (folder->ui_func)
1503
                folder->ui_func(folder, item, folder->ui_func_data);
1504

    
1505
#ifdef G_OS_WIN32
1506
        do {
1507
                if (!wfd.file_name) continue;
1508
                if (wfd.file_name[0] == '.') {
1509
                        g_free(wfd.file_name);
1510
                        wfd.file_name = NULL;
1511
                        continue;
1512
                }
1513
                dir_name = utf8name = wfd.file_name;
1514
                wfd.file_name = NULL;
1515
#else
1516
        while ((d = readdir(dp)) != NULL) {
1517
                dir_name = d->d_name;
1518
                if (dir_name[0] == '.') continue;
1519

    
1520
                utf8name = g_filename_to_utf8(dir_name, -1, NULL, NULL, NULL);
1521
                if (!utf8name)
1522
                        utf8name = g_strdup(dir_name);
1523
#endif
1524

    
1525
                if (item->path) {
1526
                        utf8entry = g_strconcat(item->path, "/", utf8name,
1527
                                                NULL);
1528
                } else
1529
                        utf8entry = g_strdup(utf8name);
1530
                entry = g_filename_from_utf8(utf8entry, -1, NULL, NULL, NULL);
1531
                if (!entry)
1532
                        entry = g_strdup(utf8entry);
1533
#ifdef G_OS_WIN32
1534
                subst_char(entry, '/', G_DIR_SEPARATOR);
1535
#endif
1536

    
1537
                if (
1538
#ifdef G_OS_WIN32
1539
                        (wfd.file_attr & FILE_ATTRIBUTE_DIRECTORY) != 0
1540
#else
1541
#if HAVE_DIRENT_D_TYPE
1542
                        d->d_type == DT_DIR ||
1543
                        ((d->d_type == DT_UNKNOWN || d->d_type == DT_LNK) &&
1544
#endif
1545
                        g_stat(entry, &s) == 0 && S_ISDIR(s.st_mode)
1546
#if HAVE_DIRENT_D_TYPE
1547
                        )
1548
#endif
1549
#endif /* G_OS_WIN32 */
1550
                   ) {
1551
                        FolderItem *new_item = NULL;
1552
                        GNode *node;
1553

    
1554
#ifndef G_OS_WIN32
1555
                        if (g_utf8_validate(utf8name, -1, NULL) == FALSE) {
1556
                                g_warning(_("Directory name\n"
1557
                                            "'%s' is not a valid UTF-8 string.\n"
1558
                                            "Maybe the locale encoding is used for filename.\n"
1559
                                            "If that is the case, you must set the following environmental variable\n"
1560
                                            "(see README for detail):\n"
1561
                                            "\n"
1562
                                            "\tG_FILENAME_ENCODING=@locale\n"),
1563
                                          utf8name);
1564
                                g_free(entry);
1565
                                g_free(utf8entry);
1566
                                g_free(utf8name);
1567
                                continue;
1568
                        }
1569
#endif /* G_OS_WIN32 */
1570

    
1571
                        node = item->node;
1572
                        for (node = node->children; node != NULL; node = node->next) {
1573
                                FolderItem *cur_item = FOLDER_ITEM(node->data);
1574
                                if (!strcmp2(cur_item->path, utf8entry)) {
1575
                                        new_item = cur_item;
1576
                                        break;
1577
                                }
1578
                        }
1579
                        if (!new_item) {
1580
                                debug_print("new folder '%s' found.\n",
1581
                                            utf8entry);
1582
                                new_item = folder_item_new(utf8name, utf8entry);
1583
                                folder_item_append(item, new_item);
1584
                        }
1585

    
1586
                        if (!item->path) {
1587
                                if (!folder->inbox &&
1588
                                    !strcmp(dir_name, INBOX_DIR)) {
1589
                                        new_item->stype = F_INBOX;
1590
                                        folder->inbox = new_item;
1591
                                } else if (!folder->outbox &&
1592
                                           !strcmp(dir_name, OUTBOX_DIR)) {
1593
                                        new_item->stype = F_OUTBOX;
1594
                                        folder->outbox = new_item;
1595
                                } else if (!folder->draft &&
1596
                                           !strcmp(dir_name, DRAFT_DIR)) {
1597
                                        new_item->stype = F_DRAFT;
1598
                                        folder->draft = new_item;
1599
                                } else if (!folder->queue &&
1600
                                           !strcmp(dir_name, QUEUE_DIR)) {
1601
                                        new_item->stype = F_QUEUE;
1602
                                        folder->queue = new_item;
1603
                                } else if (!folder->trash &&
1604
                                           !strcmp(dir_name, TRASH_DIR)) {
1605
                                        new_item->stype = F_TRASH;
1606
                                        folder->trash = new_item;
1607
                                }
1608
                        }
1609

    
1610
                        mh_scan_tree_recursive(new_item);
1611
                } else if (to_number(dir_name) > 0) n_msg++;
1612

    
1613
                g_free(entry);
1614
                g_free(utf8entry);
1615
                g_free(utf8name);
1616
#ifdef G_OS_WIN32
1617
        } while (find_next_file(hfind, &wfd));
1618
#else
1619
        }
1620
#endif
1621

    
1622
#ifdef G_OS_WIN32
1623
        FindClose(hfind);
1624
#else
1625
        closedir(dp);
1626
#endif
1627

    
1628
        if (item->path) {
1629
                gint new, unread, total, min, max;
1630

    
1631
                procmsg_get_mark_sum
1632
                        (item, &new, &unread, &total, &min, &max, 0);
1633
                if (n_msg > total) {
1634
                        new += n_msg - total;
1635
                        unread += n_msg - total;
1636
                }
1637
                item->new = new;
1638
                item->unread = unread;
1639
                item->total = n_msg;
1640
                item->updated = TRUE;
1641
                item->mtime = 0;
1642
        }
1643
}
1644

    
1645
static gboolean mh_rename_folder_func(GNode *node, gpointer data)
1646
{
1647
        FolderItem *item = node->data;
1648
        gchar **paths = data;
1649
        const gchar *oldpath = paths[0];
1650
        const gchar *newpath = paths[1];
1651
        gchar *base;
1652
        gchar *new_itempath;
1653
        gint oldpathlen;
1654

    
1655
        oldpathlen = strlen(oldpath);
1656
        if (strncmp(oldpath, item->path, oldpathlen) != 0) {
1657
                g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
1658
                return TRUE;
1659
        }
1660

    
1661
        base = item->path + oldpathlen;
1662
        while (*base == '/') base++;
1663
        if (*base == '\0')
1664
                new_itempath = g_strdup(newpath);
1665
        else
1666
                new_itempath = g_strconcat(newpath, "/", base, NULL);
1667
        g_free(item->path);
1668
        item->path = new_itempath;
1669

    
1670
        return FALSE;
1671
}