Statistics
| Revision:

root / libsylph / mh.c @ 1686

History | View | Annotate | Download (37 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 "folder.h"
42
#include "mh.h"
43
#include "procmsg.h"
44
#include "procheader.h"
45
#include "utils.h"
46
#include "prefs_common.h"
47
48
static void        mh_folder_init                (Folder                *folder,
49
                                         const gchar        *name,
50
                                         const gchar        *path);
51
52
static Folder        *mh_folder_new                (const gchar        *name,
53
                                         const gchar        *path);
54
static void     mh_folder_destroy        (Folder                *folder);
55
56
static GSList  *mh_get_msg_list                (Folder                *folder,
57
                                         FolderItem        *item,
58
                                         gboolean         use_cache);
59
static GSList  *mh_get_uncached_msg_list(Folder                *folder,
60
                                         FolderItem        *item);
61
static gchar   *mh_fetch_msg                (Folder                *folder,
62
                                         FolderItem        *item,
63
                                         gint                 num);
64
static MsgInfo *mh_get_msginfo                (Folder                *folder,
65
                                         FolderItem        *item,
66
                                         gint                 num);
67
static gint     mh_add_msg                (Folder                *folder,
68
                                         FolderItem        *dest,
69
                                         const gchar        *file,
70
                                         MsgFlags        *flags,
71
                                         gboolean         remove_source);
72
static gint     mh_add_msgs                (Folder                *folder,
73
                                         FolderItem        *dest,
74
                                         GSList                *file_list,
75
                                         gboolean         remove_source,
76
                                         gint                *first);
77
static gint     mh_move_msg                (Folder                *folder,
78
                                         FolderItem        *dest,
79
                                         MsgInfo        *msginfo);
80
static gint     mh_move_msgs                (Folder                *folder,
81
                                         FolderItem        *dest,
82
                                         GSList                *msglist);
83
static gint     mh_copy_msg                (Folder                *folder,
84
                                         FolderItem        *dest,
85
                                         MsgInfo        *msginfo);
86
static gint     mh_copy_msgs                (Folder                *folder,
87
                                         FolderItem        *dest,
88
                                         GSList                *msglist);
89
static gint     mh_remove_msg                (Folder                *folder,
90
                                         FolderItem        *item,
91
                                         MsgInfo        *msginfo);
92
static gint     mh_remove_all_msg        (Folder                *folder,
93
                                         FolderItem        *item);
94
static gboolean mh_is_msg_changed        (Folder                *folder,
95
                                         FolderItem        *item,
96
                                         MsgInfo        *msginfo);
97
static gint    mh_close                        (Folder                *folder,
98
                                         FolderItem        *item);
99
100
static gint    mh_scan_folder_full        (Folder                *folder,
101
                                         FolderItem        *item,
102
                                         gboolean         count_sum);
103
static gint    mh_scan_folder                (Folder                *folder,
104
                                         FolderItem        *item);
105
static gint    mh_scan_tree                (Folder                *folder);
106
107
static gint    mh_create_tree                (Folder                *folder);
108
static FolderItem *mh_create_folder        (Folder                *folder,
109
                                         FolderItem        *parent,
110
                                         const gchar        *name);
111
static gint    mh_rename_folder                (Folder                *folder,
112
                                         FolderItem        *item,
113
                                         const gchar        *name);
114
static gint    mh_move_folder                (Folder                *folder,
115
                                         FolderItem        *item,
116
                                         FolderItem        *new_parent);
117
static gint    mh_remove_folder                (Folder                *folder,
118
                                         FolderItem        *item);
119
120
static gchar   *mh_get_new_msg_filename                (FolderItem        *dest);
121
122
static gint        mh_do_move_msgs                        (Folder                *folder,
123
                                                 FolderItem        *dest,
124
                                                 GSList                *msglist);
125
126
static time_t  mh_get_mtime                        (FolderItem        *item);
127
static GSList  *mh_get_uncached_msgs                (GHashTable        *msg_table,
128
                                                 FolderItem        *item);
129
static MsgInfo *mh_parse_msg                        (const gchar        *file,
130
                                                 FolderItem        *item);
131
static void        mh_remove_missing_folder_items        (Folder                *folder);
132
static void        mh_scan_tree_recursive                (FolderItem        *item);
133
134
static gboolean mh_rename_folder_func                (GNode                *node,
135
                                                 gpointer         data);
136
137
static FolderClass mh_class =
138
{
139
        F_MH,
140
141
        mh_folder_new,
142
        mh_folder_destroy,
143
144
        mh_scan_tree,
145
        mh_create_tree,
146
147
        mh_get_msg_list,
148
        mh_get_uncached_msg_list,
149
        mh_fetch_msg,
150
        mh_get_msginfo,
151
        mh_add_msg,
152
        mh_add_msgs,
153
        mh_move_msg,
154
        mh_move_msgs,
155
        mh_copy_msg,
156
        mh_copy_msgs,
157
        mh_remove_msg,
158
        NULL,
159
        mh_remove_all_msg,
160
        mh_is_msg_changed,
161
        mh_close,
162
        mh_scan_folder,
163
164
        mh_create_folder,
165
        mh_rename_folder,
166
        mh_move_folder,
167
        mh_remove_folder,
168
};
169
170
171
FolderClass *mh_get_class(void)
172
{
173
        return &mh_class;
174
}
175
176
static Folder *mh_folder_new(const gchar *name, const gchar *path)
177
{
178
        Folder *folder;
179
180
        folder = (Folder *)g_new0(MHFolder, 1);
181
        mh_folder_init(folder, name, path);
182
183
        return folder;
184
}
185
186
static void mh_folder_destroy(Folder *folder)
187
{
188
        folder_local_folder_destroy(LOCAL_FOLDER(folder));
189
}
190
191
static void mh_folder_init(Folder *folder, const gchar *name, const gchar *path)
192
{
193
        folder->klass = mh_get_class();
194
        folder_local_folder_init(folder, name, path);
195
}
196
197
static GSList *mh_get_msg_list_full(Folder *folder, FolderItem *item,
198
                                    gboolean use_cache, gboolean uncached_only)
199
{
200
        GSList *mlist;
201
        GHashTable *msg_table;
202
        time_t cur_mtime;
203
        GSList *newlist = NULL;
204
#ifdef MEASURE_TIME
205
        GTimer *timer;
206
#endif
207
208
        g_return_val_if_fail(item != NULL, NULL);
209
210
#ifdef MEASURE_TIME
211
        timer = g_timer_new();
212
#endif
213
214
        cur_mtime = mh_get_mtime(item);
215
216
        if (use_cache && item->mtime == cur_mtime) {
217
                debug_print("Folder is not modified.\n");
218
                mlist = procmsg_read_cache(item, FALSE);
219
                if (!mlist) {
220
                        mlist = mh_get_uncached_msgs(NULL, item);
221
                        if (mlist)
222
                                item->cache_dirty = TRUE;
223
                }
224
        } else if (use_cache) {
225
                GSList *cur, *next;
226
                gboolean strict_cache_check = prefs_common.strict_cache_check;
227
228
                if (item->stype == F_QUEUE || item->stype == F_DRAFT)
229
                        strict_cache_check = TRUE;
230
231
                mlist = procmsg_read_cache(item, strict_cache_check);
232
                msg_table = procmsg_msg_hash_table_create(mlist);
233
                newlist = mh_get_uncached_msgs(msg_table, item);
234
                if (newlist)
235
                        item->cache_dirty = TRUE;
236
                if (msg_table)
237
                        g_hash_table_destroy(msg_table);
238
239
                if (!strict_cache_check) {
240
                        /* remove nonexistent messages */
241
                        for (cur = mlist; cur != NULL; cur = next) {
242
                                MsgInfo *msginfo = (MsgInfo *)cur->data;
243
                                next = cur->next;
244
                                if (!MSG_IS_CACHED(msginfo->flags)) {
245
                                        debug_print("removing nonexistent message %d from cache\n", msginfo->msgnum);
246
                                        mlist = g_slist_remove(mlist, msginfo);
247
                                        procmsg_msginfo_free(msginfo);
248
                                        item->cache_dirty = TRUE;
249
                                        item->mark_dirty = TRUE;
250
                                }
251
                        }
252
                }
253
254
                mlist = g_slist_concat(mlist, newlist);
255
        } else {
256
                mlist = mh_get_uncached_msgs(NULL, item);
257
                item->cache_dirty = TRUE;
258
                newlist = mlist;
259
        }
260
261
        procmsg_set_flags(mlist, item);
262
263
        if (!uncached_only)
264
                mlist = procmsg_sort_msg_list(mlist, item->sort_key,
265
                                              item->sort_type);
266
267
        if (item->mark_queue)
268
                item->mark_dirty = TRUE;
269
270
#ifdef MEASURE_TIME
271
        g_timer_stop(timer);
272
        g_print("%s: %s: elapsed time: %f sec\n",
273
                G_STRFUNC, item->path, g_timer_elapsed(timer, NULL));
274
        g_timer_destroy(timer);
275
#endif
276
        debug_print("cache_dirty: %d, mark_dirty: %d\n",
277
                    item->cache_dirty, item->mark_dirty);
278
279
        if (!item->opened) {
280
                item->mtime = cur_mtime;
281
                if (item->cache_dirty)
282
                        procmsg_write_cache_list(item, mlist);
283
                if (item->mark_dirty)
284
                        procmsg_write_flags_list(item, mlist);
285
        }
286
287
        if (uncached_only) {
288
                GSList *cur;
289
290
                if (newlist == NULL) {
291
                        procmsg_msg_list_free(mlist);
292
                        return NULL;
293
                }
294
                if (mlist == newlist)
295
                        return newlist;
296
                for (cur = mlist; cur != NULL; cur = cur->next) {
297
                        if (cur->next == newlist) {
298
                                cur->next = NULL;
299
                                procmsg_msg_list_free(mlist);
300
                                return newlist;
301
                        }
302
                }
303
                procmsg_msg_list_free(mlist);
304
                return NULL;
305
        }
306
307
        return mlist;
308
}
309
310
static GSList *mh_get_msg_list(Folder *folder, FolderItem *item,
311
                               gboolean use_cache)
312
{
313
        return mh_get_msg_list_full(folder, item, use_cache, FALSE);
314
}
315
316
static GSList *mh_get_uncached_msg_list(Folder *folder, FolderItem *item)
317
{
318
        return mh_get_msg_list_full(folder, item, TRUE, TRUE);
319
}
320
321
static gchar *mh_fetch_msg(Folder *folder, FolderItem *item, gint num)
322
{
323
        gchar *path;
324
        gchar *file;
325
326
        g_return_val_if_fail(item != NULL, NULL);
327
        g_return_val_if_fail(num > 0, NULL);
328
329
        if (item->last_num < 0 || num > item->last_num) {
330
                mh_scan_folder(folder, item);
331
                if (item->last_num < 0) return NULL;
332
        }
333
334
        if (num > item->last_num)
335
                return NULL;
336
337
        path = folder_item_get_path(item);
338
        file = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
339
        g_free(path);
340
        if (!is_file_exist(file)) {
341
                g_free(file);
342
                return NULL;
343
        }
344
345
        return file;
346
}
347
348
static MsgInfo *mh_get_msginfo(Folder *folder, FolderItem *item, gint num)
349
{
350
        MsgInfo *msginfo;
351
        gchar *file;
352
353
        g_return_val_if_fail(item != NULL, NULL);
354
        g_return_val_if_fail(num > 0, NULL);
355
356
        file = mh_fetch_msg(folder, item, num);
357
        if (!file) return NULL;
358
359
        msginfo = mh_parse_msg(file, item);
360
        if (msginfo)
361
                msginfo->msgnum = num;
362
363
        g_free(file);
364
365
        return msginfo;
366
}
367
368
static gchar *mh_get_new_msg_filename(FolderItem *dest)
369
{
370
        gchar *destfile;
371
        gchar *destpath;
372
373
        destpath = folder_item_get_path(dest);
374
        g_return_val_if_fail(destpath != NULL, NULL);
375
376
        if (!is_dir_exist(destpath))
377
                make_dir_hier(destpath);
378
379
        for (;;) {
380
                destfile = g_strdup_printf("%s%c%d", destpath, G_DIR_SEPARATOR,
381
                                           dest->last_num + 1);
382
                if (is_file_entry_exist(destfile)) {
383
                        dest->last_num++;
384
                        g_free(destfile);
385
                } else
386
                        break;
387
        }
388
389
        g_free(destpath);
390
391
        return destfile;
392
}
393
394
#define SET_DEST_MSG_FLAGS(fp, dest, n, fl)                                \
395
{                                                                        \
396
        MsgInfo newmsginfo;                                                \
397
                                                                        \
398
        newmsginfo.msgnum = n;                                                \
399
        newmsginfo.flags = fl;                                                \
400
        if (dest->stype == F_OUTBOX ||                                        \
401
            dest->stype == F_QUEUE  ||                                        \
402
            dest->stype == F_DRAFT) {                                        \
403
                MSG_UNSET_PERM_FLAGS(newmsginfo.flags,                        \
404
                                     MSG_NEW|MSG_UNREAD|MSG_DELETED);        \
405
        } else if (dest->stype == F_TRASH) {                                \
406
                MSG_UNSET_PERM_FLAGS(newmsginfo.flags, MSG_DELETED);        \
407
        }                                                                \
408
                                                                        \
409
        if (fp)                                                                \
410
                procmsg_write_flags(&newmsginfo, fp);                        \
411
        else if (dest->opened)                                                \
412
                procmsg_add_flags(dest, n, newmsginfo.flags);                \
413
}
414
415
static gint mh_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
416
                       MsgFlags *flags, gboolean remove_source)
417
{
418
        GSList file_list;
419
        MsgFileInfo fileinfo;
420
421
        g_return_val_if_fail(file != NULL, -1);
422
423
        fileinfo.file = (gchar *)file;
424
        fileinfo.flags = flags;
425
        file_list.data = &fileinfo;
426
        file_list.next = NULL;
427
428
        return mh_add_msgs(folder, dest, &file_list, remove_source, NULL);
429
}
430
431
static gint mh_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
432
                        gboolean remove_source, gint *first)
433
{
434
        gchar *destfile;
435
        GSList *cur;
436
        MsgFileInfo *fileinfo;
437
        gint first_ = 0;
438
        FILE *fp;
439
440
        g_return_val_if_fail(dest != NULL, -1);
441
        g_return_val_if_fail(file_list != NULL, -1);
442
443
        if (dest->last_num < 0) {
444
                mh_scan_folder(folder, dest);
445
                if (dest->last_num < 0) return -1;
446
        }
447
448
        if ((((MsgFileInfo *)file_list->data)->flags == NULL &&
449
            file_list->next == NULL) || dest->opened)
450
                fp = NULL;
451
        else if ((fp = procmsg_open_mark_file(dest, DATA_APPEND)) == NULL)
452
                g_warning("Can't open mark file.\n");
453
454
        for (cur = file_list; cur != NULL; cur = cur->next) {
455
                fileinfo = (MsgFileInfo *)cur->data;
456
457
                destfile = mh_get_new_msg_filename(dest);
458
                if (destfile == NULL) return -1;
459
                if (first_ == 0 || first_ > dest->last_num + 1)
460
                        first_ = dest->last_num + 1;
461
462
#ifdef G_OS_UNIX
463
                if (link(fileinfo->file, destfile) < 0) {
464
#endif
465
                        if (copy_file(fileinfo->file, destfile, TRUE) < 0) {
466
                                g_warning(_("can't copy message %s to %s\n"),
467
                                          fileinfo->file, destfile);
468
                                g_free(destfile);
469
                                return -1;
470
                        }
471
#ifdef G_OS_UNIX
472
                }
473
#endif
474
475
                g_free(destfile);
476
                dest->last_num++;
477
                dest->total++;
478
                dest->updated = TRUE;
479
                dest->mtime = 0;
480
481
                if (fileinfo->flags) {
482
                        if (MSG_IS_RECEIVED(*fileinfo->flags)) {
483
                                if (dest->unmarked_num == 0)
484
                                        dest->new = 0;
485
                                dest->unmarked_num++;
486
                                procmsg_add_mark_queue(dest, dest->last_num,
487
                                                       *fileinfo->flags);
488
                        } else {
489
                                SET_DEST_MSG_FLAGS(fp, dest, dest->last_num,
490
                                                   *fileinfo->flags);
491
                        }
492
                        if (MSG_IS_NEW(*fileinfo->flags))
493
                                dest->new++;
494
                        if (MSG_IS_UNREAD(*fileinfo->flags))
495
                                dest->unread++;
496
                } else {
497
                        if (dest->unmarked_num == 0)
498
                                dest->new = 0;
499
                        dest->unmarked_num++;
500
                        dest->new++;
501
                        dest->unread++;
502
                }
503
        }
504
505
        if (fp) fclose(fp);
506
507
        if (first)
508
                *first = first_;
509
510
        if (remove_source) {
511
                for (cur = file_list; cur != NULL; cur = cur->next) {
512
                        fileinfo = (MsgFileInfo *)cur->data;
513
                        if (g_unlink(fileinfo->file) < 0)
514
                                FILE_OP_ERROR(fileinfo->file, "unlink");
515
                }
516
        }
517
518
        return dest->last_num;
519
}
520
521
static gint mh_do_move_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
522
{
523
        FolderItem *src;
524
        gchar *srcfile;
525
        gchar *destfile;
526
        FILE *fp;
527
        GSList *cur;
528
        MsgInfo *msginfo;
529
530
        g_return_val_if_fail(dest != NULL, -1);
531
        g_return_val_if_fail(msglist != NULL, -1);
532
533
        if (dest->last_num < 0) {
534
                mh_scan_folder(folder, dest);
535
                if (dest->last_num < 0) return -1;
536
        }
537
538
        if (dest->opened)
539
                fp = NULL;
540
        else if ((fp = procmsg_open_mark_file(dest, DATA_APPEND)) == NULL)
541
                g_warning(_("Can't open mark file.\n"));
542
543
        for (cur = msglist; cur != NULL; cur = cur->next) {
544
                msginfo = (MsgInfo *)cur->data;
545
                src = msginfo->folder;
546
547
                if (src == dest) {
548
                        g_warning(_("the src folder is identical to the dest.\n"));
549
                        continue;
550
                }
551
                debug_print("Moving message %s/%d to %s ...\n",
552
                            src->path, msginfo->msgnum, dest->path);
553
554
                destfile = mh_get_new_msg_filename(dest);
555
                if (!destfile) break;
556
                srcfile = procmsg_get_message_file(msginfo);
557
558
                if (move_file(srcfile, destfile, FALSE) < 0) {
559
                        g_free(srcfile);
560
                        g_free(destfile);
561
                        break;
562
                }
563
564
                g_free(srcfile);
565
                g_free(destfile);
566
                src->total--;
567
                src->updated = TRUE;
568
                src->mtime = 0;
569
                dest->last_num++;
570
                dest->total++;
571
                dest->updated = TRUE;
572
                dest->mtime = 0;
573
574
                if (fp) {
575
                        SET_DEST_MSG_FLAGS(fp, dest, dest->last_num,
576
                                           msginfo->flags);
577
                }
578
579
                if (MSG_IS_NEW(msginfo->flags)) {
580
                        src->new--;
581
                        dest->new++;
582
                }
583
                if (MSG_IS_UNREAD(msginfo->flags)) {
584
                        src->unread--;
585
                        dest->unread++;
586
                }
587
588
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID);
589
        }
590
591
        if (fp) fclose(fp);
592
593
        return dest->last_num;
594
}
595
596
static gint mh_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
597
{
598
        GSList msglist;
599
600
        g_return_val_if_fail(msginfo != NULL, -1);
601
602
        msglist.data = msginfo;
603
        msglist.next = NULL;
604
605
        return mh_move_msgs(folder, dest, &msglist);
606
}
607
608
static gint mh_move_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
609
{
610
        MsgInfo *msginfo;
611
        GSList *file_list;
612
        gint ret = 0;
613
        gint first;
614
615
        msginfo = (MsgInfo *)msglist->data;
616
        if (folder == msginfo->folder->folder)
617
                return mh_do_move_msgs(folder, dest, msglist);
618
619
        file_list = procmsg_get_message_file_list(msglist);
620
        g_return_val_if_fail(file_list != NULL, -1);
621
622
        ret = mh_add_msgs(folder, dest, file_list, FALSE, &first);
623
624
        procmsg_message_file_list_free(file_list);
625
626
        if (ret != -1)
627
                ret = folder_item_remove_msgs(msginfo->folder, msglist);
628
629
        return ret;
630
}
631
632
static gint mh_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
633
{
634
        GSList msglist;
635
636
        g_return_val_if_fail(msginfo != NULL, -1);
637
638
        msglist.data = msginfo;
639
        msglist.next = NULL;
640
641
        return mh_copy_msgs(folder, dest, &msglist);
642
}
643
644
static gint mh_copy_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
645
{
646
        gchar *srcfile;
647
        gchar *destfile;
648
        FILE *fp;
649
        GSList *cur;
650
        MsgInfo *msginfo;
651
652
        g_return_val_if_fail(dest != NULL, -1);
653
        g_return_val_if_fail(msglist != NULL, -1);
654
655
        if (dest->last_num < 0) {
656
                mh_scan_folder(folder, dest);
657
                if (dest->last_num < 0) return -1;
658
        }
659
660
        if (dest->opened)
661
                fp = NULL;
662
        else if ((fp = procmsg_open_mark_file(dest, DATA_APPEND)) == NULL)
663
                g_warning(_("Can't open mark file.\n"));
664
665
        for (cur = msglist; cur != NULL; cur = cur->next) {
666
                msginfo = (MsgInfo *)cur->data;
667
668
                if (msginfo->folder == dest) {
669
                        g_warning(_("the src folder is identical to the dest.\n"));
670
                        continue;
671
                }
672
                debug_print(_("Copying message %s/%d to %s ...\n"),
673
                            msginfo->folder->path, msginfo->msgnum, dest->path);
674
675
                destfile = mh_get_new_msg_filename(dest);
676
                if (!destfile) break;
677
                srcfile = procmsg_get_message_file(msginfo);
678
679
                if (copy_file(srcfile, destfile, TRUE) < 0) {
680
                        FILE_OP_ERROR(srcfile, "copy");
681
                        g_free(srcfile);
682
                        g_free(destfile);
683
                        break;
684
                }
685
686
                g_free(srcfile);
687
                g_free(destfile);
688
                dest->last_num++;
689
                dest->total++;
690
                dest->updated = TRUE;
691
                dest->mtime = 0;
692
693
                if (fp) {
694
                        SET_DEST_MSG_FLAGS(fp, dest, dest->last_num,
695
                                           msginfo->flags);
696
                }
697
698
                if (MSG_IS_NEW(msginfo->flags))
699
                        dest->new++;
700
                if (MSG_IS_UNREAD(msginfo->flags))
701
                        dest->unread++;
702
        }
703
704
        if (fp) fclose(fp);
705
706
        return dest->last_num;
707
}
708
709
static gint mh_remove_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo)
710
{
711
        gchar *file;
712
713
        g_return_val_if_fail(item != NULL, -1);
714
715
        file = mh_fetch_msg(folder, item, msginfo->msgnum);
716
        g_return_val_if_fail(file != NULL, -1);
717
718
        if (g_unlink(file) < 0) {
719
                FILE_OP_ERROR(file, "unlink");
720
                g_free(file);
721
                return -1;
722
        }
723
        g_free(file);
724
725
        item->total--;
726
        item->updated = TRUE;
727
        item->mtime = 0;
728
        if (MSG_IS_NEW(msginfo->flags))
729
                item->new--;
730
        if (MSG_IS_UNREAD(msginfo->flags))
731
                item->unread--;
732
        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID);
733
734
        if (msginfo->msgnum == item->last_num)
735
                mh_scan_folder_full(folder, item, FALSE);
736
737
        return 0;
738
}
739
740
static gint mh_remove_all_msg(Folder *folder, FolderItem *item)
741
{
742
        gchar *path;
743
        gint val;
744
745
        g_return_val_if_fail(item != NULL, -1);
746
747
        path = folder_item_get_path(item);
748
        g_return_val_if_fail(path != NULL, -1);
749
        val = remove_all_numbered_files(path);
750
        g_free(path);
751
        if (val == 0) {
752
                item->new = item->unread = item->total = 0;
753
                item->last_num = 0;
754
                item->updated = TRUE;
755
                item->mtime = 0;
756
        }
757
758
        return val;
759
}
760
761
static gboolean mh_is_msg_changed(Folder *folder, FolderItem *item,
762
                                  MsgInfo *msginfo)
763
{
764
        struct stat s;
765
766
        if (g_stat(itos(msginfo->msgnum), &s) < 0 ||
767
            msginfo->size  != s.st_size ||
768
            msginfo->mtime != s.st_mtime)
769
                return TRUE;
770
771
        return FALSE;
772
}
773
774
static gint mh_close(Folder *folder, FolderItem *item)
775
{
776
        return 0;
777
}
778
779
#ifdef G_OS_WIN32
780
struct wfddata {
781
        WIN32_FIND_DATAA wfda;
782
        WIN32_FIND_DATAW wfdw;
783
        DWORD file_attr;
784
        gchar *file_name;
785
};
786
787
static HANDLE find_first_file(const gchar *path, struct wfddata *wfd)
788
{
789
        HANDLE hfind;
790
791
        if (G_WIN32_HAVE_WIDECHAR_API()) {
792
                if (path) {
793
                        gchar *wildcard_path;
794
                        wchar_t *wpath;
795
796
                        wildcard_path = g_strconcat(path, "\\*", NULL);
797
                        wpath = g_utf8_to_utf16(wildcard_path, -1,
798
                                                NULL, NULL, NULL);
799
                        if (wpath) {
800
                                hfind = FindFirstFileW(wpath, &wfd->wfdw);
801
                                g_free(wpath);
802
                        } else
803
                                hfind = INVALID_HANDLE_VALUE;
804
                        g_free(wildcard_path);
805
                } else
806
                        hfind = FindFirstFileW(L"*", &wfd->wfdw);
807
808
                if (hfind != INVALID_HANDLE_VALUE) {
809
                        wfd->file_attr = wfd->wfdw.dwFileAttributes;
810
                        wfd->file_name = g_utf16_to_utf8(wfd->wfdw.cFileName, -1,
811
                                                         NULL, NULL, NULL);
812
                }
813
        } else {
814
                if (path) {
815
                        gchar *wildcard_path;
816
                        gchar *cp_path;
817
818
                        wildcard_path = g_strconcat(path, "\\*", NULL);
819
                        cp_path = g_locale_from_utf8(wildcard_path, -1,
820
                                                     NULL, NULL, NULL);
821
                        if (cp_path) {
822
                                hfind = FindFirstFileA(cp_path, &wfd->wfda);
823
                                g_free(cp_path);
824
                        } else
825
                                hfind = INVALID_HANDLE_VALUE;
826
                        g_free(wildcard_path);
827
                } else
828
                        hfind = FindFirstFileA("*", &wfd->wfda);
829
830
                if (hfind != INVALID_HANDLE_VALUE) {
831
                        wfd->file_attr = wfd->wfda.dwFileAttributes;
832
                        wfd->file_name = g_locale_to_utf8(wfd->wfda.cFileName,
833
                                                          -1, NULL, NULL, NULL);
834
                }
835
        }
836
837
        return hfind;
838
}
839
840
static BOOL find_next_file(HANDLE hfind, struct wfddata *wfd)
841
{
842
        BOOL retval;
843
844
        if (G_WIN32_HAVE_WIDECHAR_API()) {
845
                retval = FindNextFileW(hfind, &wfd->wfdw);
846
                if (retval) {
847
                        wfd->file_attr = wfd->wfdw.dwFileAttributes;
848
                        wfd->file_name = g_utf16_to_utf8(wfd->wfdw.cFileName, -1,
849
                                                         NULL, NULL, NULL);
850
                }
851
        } else {
852
                retval = FindNextFileA(hfind, &wfd->wfda);
853
                if (retval) {
854
                        wfd->file_attr = wfd->wfda.dwFileAttributes;
855
                        wfd->file_name = g_locale_to_utf8(wfd->wfda.cFileName,
856
                                                          -1, NULL, NULL, NULL);
857
                }
858
        }
859
860
        return retval;
861
}
862
#endif
863
864
static gint mh_scan_folder_full(Folder *folder, FolderItem *item,
865
                                gboolean count_sum)
866
{
867
        gchar *path;
868
#ifdef G_OS_WIN32
869
        struct wfddata wfd;
870
        HANDLE hfind;
871
#else
872
        DIR *dp;
873
        struct dirent *d;
874
#endif
875
        gint max = 0;
876
        gint num;
877
        gint n_msg = 0;
878
879
        g_return_val_if_fail(item != NULL, -1);
880
881
        debug_print("mh_scan_folder(): Scanning %s ...\n", item->path);
882
883
        path = folder_item_get_path(item);
884
        g_return_val_if_fail(path != NULL, -1);
885
        if (change_dir(path) < 0) {
886
                g_free(path);
887
                return -1;
888
        }
889
        g_free(path);
890
891
#ifdef G_OS_WIN32
892
        if ((hfind = find_first_file(NULL, &wfd)) == INVALID_HANDLE_VALUE) {
893
                g_warning("failed to open directory\n");
894
#else
895
        if ((dp = opendir(".")) == NULL) {
896
                FILE_OP_ERROR(item->path, "opendir");
897
#endif
898
                return -1;
899
        }
900
901
        if (folder->ui_func)
902
                folder->ui_func(folder, item, folder->ui_func_data);
903
904
#ifdef G_OS_WIN32
905
        do {
906
                if ((wfd.file_attr &
907
                     (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0) {
908
                        if (wfd.file_name) {
909
                                if ((num = to_number(wfd.file_name)) > 0) {
910
                                        n_msg++;
911
                                        if (max < num)
912
                                                max = num;
913
                                }
914
                        }
915
                }
916
917
                if (wfd.file_name) {
918
                        g_free(wfd.file_name);
919
                        wfd.file_name = NULL;
920
                }
921
        } while (find_next_file(hfind, &wfd));
922
923
        FindClose(hfind);
924
#else
925
        while ((d = readdir(dp)) != NULL) {
926
                if ((num = to_number(d->d_name)) > 0 &&
927
                    dirent_is_regular_file(d)) {
928
                        n_msg++;
929
                        if (max < num)
930
                                max = num;
931
                }
932
        }
933
934
        closedir(dp);
935
#endif
936
937
        if (n_msg == 0)
938
                item->new = item->unread = item->total = 0;
939
        else if (count_sum) {
940
                gint new, unread, total, min, max_;
941
942
                procmsg_get_mark_sum
943
                        (item, &new, &unread, &total, &min, &max_, 0);
944
945
                if (n_msg > total) {
946
                        item->unmarked_num = new = n_msg - total;
947
                        unread += n_msg - total;
948
                } else
949
                        item->unmarked_num = 0;
950
951
                item->new = new;
952
                item->unread = unread;
953
                item->total = n_msg;
954
        }
955
956
        item->updated = TRUE;
957
        item->mtime = 0;
958
959
        debug_print("Last number in dir %s = %d\n", item->path, max);
960
        item->last_num = max;
961
962
        return 0;
963
}
964
965
static gint mh_scan_folder(Folder *folder, FolderItem *item)
966
{
967
        return mh_scan_folder_full(folder, item, TRUE);
968
}
969
970
static gint mh_scan_tree(Folder *folder)
971
{
972
        FolderItem *item;
973
        gchar *rootpath;
974
975
        g_return_val_if_fail(folder != NULL, -1);
976
977
        if (!folder->node) {
978
                item = folder_item_new(folder->name, NULL);
979
                item->folder = folder;
980
                folder->node = item->node = g_node_new(item);
981
        } else
982
                item = FOLDER_ITEM(folder->node->data);
983
984
        rootpath = folder_item_get_path(item);
985
        if (change_dir(rootpath) < 0) {
986
                g_free(rootpath);
987
                return -1;
988
        }
989
        g_free(rootpath);
990
991
        mh_create_tree(folder);
992
        mh_remove_missing_folder_items(folder);
993
        mh_scan_tree_recursive(item);
994
995
        return 0;
996
}
997
998
#define MAKE_DIR_IF_NOT_EXIST(dir) \
999
{ \
1000
        if (!is_dir_exist(dir)) { \
1001
                if (is_file_exist(dir)) { \
1002
                        g_warning(_("File `%s' already exists.\n" \
1003
                                    "Can't create folder."), dir); \
1004
                        return -1; \
1005
                } \
1006
                if (make_dir(dir) < 0) \
1007
                        return -1; \
1008
        } \
1009
}
1010
1011
#define MAKE_DIR_HIER_IF_NOT_EXIST(dir) \
1012
{ \
1013
        if (!is_dir_exist(dir)) { \
1014
                if (is_file_exist(dir)) { \
1015
                        g_warning(_("File `%s' already exists.\n" \
1016
                                    "Can't create folder."), dir); \
1017
                        return -1; \
1018
                } \
1019
                if (make_dir_hier(dir) < 0) \
1020
                        return -1; \
1021
        } \
1022
}
1023
1024
static gint mh_create_tree(Folder *folder)
1025
{
1026
        gchar *rootpath;
1027
1028
        g_return_val_if_fail(folder != NULL, -1);
1029
1030
        CHDIR_RETURN_VAL_IF_FAIL(get_mail_base_dir(), -1);
1031
        rootpath = LOCAL_FOLDER(folder)->rootpath;
1032
        MAKE_DIR_HIER_IF_NOT_EXIST(rootpath);
1033
        CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
1034
        MAKE_DIR_IF_NOT_EXIST(INBOX_DIR);
1035
        MAKE_DIR_IF_NOT_EXIST(OUTBOX_DIR);
1036
        MAKE_DIR_IF_NOT_EXIST(QUEUE_DIR);
1037
        MAKE_DIR_IF_NOT_EXIST(DRAFT_DIR);
1038
        MAKE_DIR_IF_NOT_EXIST(TRASH_DIR);
1039
1040
        return 0;
1041
}
1042
1043
#undef MAKE_DIR_IF_NOT_EXIST
1044
#undef MAKE_DIR_HIER_IF_NOT_EXIST
1045
1046
static FolderItem *mh_create_folder(Folder *folder, FolderItem *parent,
1047
                                    const gchar *name)
1048
{
1049
        gchar *path;
1050
        gchar *fs_name;
1051
        gchar *fullpath;
1052
        FolderItem *new_item;
1053
1054
        g_return_val_if_fail(folder != NULL, NULL);
1055
        g_return_val_if_fail(parent != NULL, NULL);
1056
        g_return_val_if_fail(name != NULL, NULL);
1057
1058
        path = folder_item_get_path(parent);
1059
        fs_name = g_filename_from_utf8(name, -1, NULL, NULL, NULL);
1060
        fullpath = g_strconcat(path, G_DIR_SEPARATOR_S,
1061
                               fs_name ? fs_name : name, NULL);
1062
        g_free(fs_name);
1063
        g_free(path);
1064
1065
        if (make_dir_hier(fullpath) < 0) {
1066
                g_free(fullpath);
1067
                return NULL;
1068
        }
1069
1070
        g_free(fullpath);
1071
1072
        /* path is a logical folder path */
1073
        if (parent->path)
1074
                path = g_strconcat(parent->path, "/", name, NULL);
1075
        else
1076
                path = g_strdup(name);
1077
        new_item = folder_item_new(name, path);
1078
        folder_item_append(parent, new_item);
1079
        g_free(path);
1080
1081
        return new_item;
1082
}
1083
1084
static gint mh_move_folder_real(Folder *folder, FolderItem *item,
1085
                                FolderItem *new_parent, const gchar *name)
1086
{
1087
        gchar *rootpath;
1088
        gchar *oldpath;
1089
        gchar *newpath;
1090
        gchar *dirname;
1091
        gchar *new_dir;
1092
        gchar *name_;
1093
        gchar *utf8_name;
1094
        gchar *paths[2];
1095
1096
        g_return_val_if_fail(folder != NULL, -1);
1097
        g_return_val_if_fail(item != NULL, -1);
1098
        g_return_val_if_fail(folder == item->folder, -1);
1099
        g_return_val_if_fail(item->path != NULL, -1);
1100
        g_return_val_if_fail(new_parent != NULL || name != NULL, -1);
1101
        if (new_parent) {
1102
                g_return_val_if_fail(item != new_parent, -1);
1103
                g_return_val_if_fail(item->parent != new_parent, -1);
1104
                g_return_val_if_fail(item->folder == new_parent->folder, -1);
1105
                if (g_node_is_ancestor(item->node, new_parent->node)) {
1106
                        g_warning("folder to be moved is ancestor of new parent\n");
1107
                        return -1;
1108
                }
1109
        }
1110
1111
        oldpath = folder_item_get_path(item);
1112
        if (new_parent) {
1113
                if (name) {
1114
                        name_ = g_filename_from_utf8(name, -1, NULL, NULL,
1115
                                                     NULL);
1116
                        if (!name_)
1117
                                name_ = g_strdup(name);
1118
                        utf8_name = g_strdup(name);
1119
                } else {
1120
                        name_ = g_path_get_basename(oldpath);
1121
                        utf8_name = g_filename_to_utf8(name_, -1, NULL, NULL,
1122
                                                       NULL);
1123
                        if (!utf8_name)
1124
                                utf8_name = g_strdup(name_);
1125
                }
1126
                new_dir = folder_item_get_path(new_parent);
1127
                newpath = g_strconcat(new_dir, G_DIR_SEPARATOR_S, name_, NULL);
1128
                g_free(new_dir);
1129
        } else {
1130
                name_ = g_filename_from_utf8(name, -1, NULL, NULL, NULL);
1131
                utf8_name = g_strdup(name);
1132
                dirname = g_dirname(oldpath);
1133
                newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S,
1134
                                      name_ ? name_ : name, NULL);
1135
                g_free(dirname);
1136
        }
1137
        g_free(name_);
1138
1139
        if (is_file_entry_exist(newpath)) {
1140
                g_warning("%s already exists\n", newpath);
1141
                g_free(oldpath);
1142
                g_free(newpath);
1143
                g_free(utf8_name);
1144
                return -1;
1145
        }
1146
1147
        rootpath = folder_get_path(folder);
1148
        if (change_dir(rootpath) < 0) {
1149
                g_free(rootpath);
1150
                g_free(oldpath);
1151
                g_free(newpath);
1152
                g_free(utf8_name);
1153
                return -1;
1154
        }
1155
        g_free(rootpath);
1156
1157
        debug_print("mh_move_folder: rename(%s, %s)\n", oldpath, newpath);
1158
1159
        if (g_rename(oldpath, newpath) < 0) {
1160
                FILE_OP_ERROR(oldpath, "rename");
1161
                g_free(oldpath);
1162
                g_free(newpath);
1163
                g_free(utf8_name);
1164
                return -1;
1165
        }
1166
1167
        g_free(oldpath);
1168
        g_free(newpath);
1169
1170
        if (new_parent) {
1171
                g_node_unlink(item->node);
1172
                g_node_append(new_parent->node, item->node);
1173
                item->parent = new_parent;
1174
                if (new_parent->path != NULL) {
1175
                        newpath = g_strconcat(new_parent->path, "/", utf8_name,
1176
                                              NULL);
1177
                        g_free(utf8_name);
1178
                } else
1179
                        newpath = utf8_name;
1180
        } else {
1181
                if (strchr(item->path, '/') != NULL) {
1182
                        dirname = g_dirname(item->path);
1183
                        newpath = g_strconcat(dirname, "/", utf8_name, NULL);
1184
                        g_free(dirname);
1185
                        g_free(utf8_name);
1186
                } else
1187
                        newpath = utf8_name;
1188
        }
1189
1190
        if (name) {
1191
                g_free(item->name);
1192
                item->name = g_strdup(name);
1193
        }
1194
1195
        paths[0] = g_strdup(item->path);
1196
        paths[1] = newpath;
1197
        g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1198
                        mh_rename_folder_func, paths);
1199
1200
        g_free(paths[0]);
1201
        g_free(paths[1]);
1202
1203
        return 0;
1204
}
1205
1206
static gint mh_move_folder(Folder *folder, FolderItem *item,
1207
                           FolderItem *new_parent)
1208
{
1209
        return mh_move_folder_real(folder, item, new_parent, NULL);
1210
}
1211
1212
static gint mh_rename_folder(Folder *folder, FolderItem *item,
1213
                             const gchar *name)
1214
{
1215
        return mh_move_folder_real(folder, item, NULL, name);
1216
}
1217
1218
static gint mh_remove_folder(Folder *folder, FolderItem *item)
1219
{
1220
        gchar *path;
1221
1222
        g_return_val_if_fail(folder != NULL, -1);
1223
        g_return_val_if_fail(item != NULL, -1);
1224
        g_return_val_if_fail(item->path != NULL, -1);
1225
1226
        path = folder_item_get_path(item);
1227
        if (remove_dir_recursive(path) < 0) {
1228
                g_warning("can't remove directory `%s'\n", path);
1229
                g_free(path);
1230
                return -1;
1231
        }
1232
1233
        g_free(path);
1234
        folder_item_remove(item);
1235
        return 0;
1236
}
1237
1238
1239
static time_t mh_get_mtime(FolderItem *item)
1240
{
1241
        gchar *path;
1242
        struct stat s;
1243
1244
        path = folder_item_get_path(item);
1245
        if (g_stat(path, &s) < 0) {
1246
                FILE_OP_ERROR(path, "stat");
1247
                return -1;
1248
        } else {
1249
                return MAX(s.st_mtime, s.st_ctime);
1250
        }
1251
}
1252
1253
static GSList *mh_get_uncached_msgs(GHashTable *msg_table, FolderItem *item)
1254
{
1255
        gchar *path;
1256
        GDir *dp;
1257
        const gchar *dir_name;
1258
        GSList *newlist = NULL;
1259
        GSList *last = NULL;
1260
        MsgInfo *msginfo;
1261
        gint n_newmsg = 0;
1262
        gint num;
1263
1264
        g_return_val_if_fail(item != NULL, NULL);
1265
1266
        path = folder_item_get_path(item);
1267
        g_return_val_if_fail(path != NULL, NULL);
1268
        if (change_dir(path) < 0) {
1269
                g_free(path);
1270
                return NULL;
1271
        }
1272
        g_free(path);
1273
1274
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
1275
                FILE_OP_ERROR(item->path, "opendir");
1276
                return NULL;
1277
        }
1278
1279
        debug_print("Searching uncached messages...\n");
1280
1281
        if (msg_table) {
1282
                while ((dir_name = g_dir_read_name(dp)) != NULL) {
1283
                        if ((num = to_number(dir_name)) <= 0) continue;
1284
1285
                        msginfo = g_hash_table_lookup
1286
                                (msg_table, GUINT_TO_POINTER(num));
1287
1288
                        if (msginfo) {
1289
                                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_CACHED);
1290
                        } else {
1291
                                /* not found in the cache (uncached message) */
1292
                                msginfo = mh_parse_msg(dir_name, item);
1293
                                if (!msginfo) continue;
1294
1295
                                if (!newlist)
1296
                                        last = newlist =
1297
                                                g_slist_append(NULL, msginfo);
1298
                                else {
1299
                                        last = g_slist_append(last, msginfo);
1300
                                        last = last->next;
1301
                                }
1302
                                n_newmsg++;
1303
                        }
1304
                }
1305
        } else {
1306
                /* discard all previous cache */
1307
                while ((dir_name = g_dir_read_name(dp)) != NULL) {
1308
                        if (to_number(dir_name) <= 0) continue;
1309
1310
                        msginfo = mh_parse_msg(dir_name, item);
1311
                        if (!msginfo) continue;
1312
1313
                        if (!newlist)
1314
                                last = newlist = g_slist_append(NULL, msginfo);
1315
                        else {
1316
                                last = g_slist_append(last, msginfo);
1317
                                last = last->next;
1318
                        }
1319
                        n_newmsg++;
1320
                }
1321
        }
1322
1323
        g_dir_close(dp);
1324
1325
        if (n_newmsg)
1326
                debug_print("%d uncached message(s) found.\n", n_newmsg);
1327
        else
1328
                debug_print("done.\n");
1329
1330
        /* sort new messages in numerical order */
1331
        if (newlist && item->sort_key == SORT_BY_NONE) {
1332
                debug_print("Sorting uncached messages in numerical order...\n");
1333
                newlist = g_slist_sort
1334
                        (newlist, (GCompareFunc)procmsg_cmp_msgnum_for_sort);
1335
                debug_print("done.\n");
1336
        }
1337
1338
        return newlist;
1339
}
1340
1341
static MsgInfo *mh_parse_msg(const gchar *file, FolderItem *item)
1342
{
1343
        MsgInfo *msginfo;
1344
        MsgFlags flags;
1345
1346
        g_return_val_if_fail(item != NULL, NULL);
1347
        g_return_val_if_fail(file != NULL, NULL);
1348
1349
        flags.perm_flags = MSG_NEW|MSG_UNREAD;
1350
        flags.tmp_flags = 0;
1351
1352
        if (item->stype == F_QUEUE) {
1353
                MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
1354
        } else if (item->stype == F_DRAFT) {
1355
                MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
1356
        }
1357
1358
        msginfo = procheader_parse_file(file, flags, FALSE);
1359
        if (!msginfo) return NULL;
1360
1361
        msginfo->msgnum = atoi(file);
1362
        msginfo->folder = item;
1363
1364
        return msginfo;
1365
}
1366
1367
#if 0
1368
static gboolean mh_is_maildir_one(const gchar *path, const gchar *dir)
1369
{
1370
        gchar *entry;
1371
        gboolean result;
1372
1373
        entry = g_strconcat(path, G_DIR_SEPARATOR_S, dir, NULL);
1374
        result = is_dir_exist(entry);
1375
        g_free(entry);
1376
1377
        return result;
1378
}
1379
1380
/*
1381
 * check whether PATH is a Maildir style mailbox.
1382
 * This is the case if the 3 subdir: new, cur, tmp are existing.
1383
 * This functon assumes that entry is an directory
1384
 */
1385
static gboolean mh_is_maildir(const gchar *path)
1386
{
1387
        return mh_is_maildir_one(path, "new") &&
1388
               mh_is_maildir_one(path, "cur") &&
1389
               mh_is_maildir_one(path, "tmp");
1390
}
1391
#endif
1392
1393
static gboolean mh_remove_missing_folder_items_func(GNode *node, gpointer data)
1394
{
1395
        FolderItem *item;
1396
        gchar *path;
1397
1398
        g_return_val_if_fail(node->data != NULL, FALSE);
1399
1400
        if (G_NODE_IS_ROOT(node))
1401
                return FALSE;
1402
1403
        item = FOLDER_ITEM(node->data);
1404
1405
#if 0
1406
        if (item->path && strchr(item->path, '/')) {
1407
                debug_print("folder '%s' includes Unix path separator. removing...\n", item->path);
1408
                folder_item_remove(item);
1409
                return FALSE;
1410
        }
1411
#endif
1412
1413
        path = folder_item_get_path(item);
1414
        if (!is_dir_exist(path)) {
1415
                debug_print("folder '%s' not found. removing...\n", path);
1416
                folder_item_remove(item);
1417
        }
1418
        g_free(path);
1419
1420
        return FALSE;
1421
}
1422
1423
static void mh_remove_missing_folder_items(Folder *folder)
1424
{
1425
        g_return_if_fail(folder != NULL);
1426
1427
        debug_print("searching missing folders...\n");
1428
1429
        g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
1430
                        mh_remove_missing_folder_items_func, folder);
1431
}
1432
1433
static void mh_scan_tree_recursive(FolderItem *item)
1434
{
1435
        Folder *folder;
1436
#ifdef G_OS_WIN32
1437
        struct wfddata wfd;
1438
        HANDLE hfind;
1439
#else
1440
        DIR *dp;
1441
        struct dirent *d;
1442
        struct stat s;
1443
#endif
1444
        const gchar *dir_name;
1445
        gchar *fs_path;
1446
        gchar *entry;
1447
        gchar *utf8entry;
1448
        gchar *utf8name;
1449
        gint n_msg = 0;
1450
1451
        g_return_if_fail(item != NULL);
1452
        g_return_if_fail(item->folder != NULL);
1453
1454
        if (item->stype == F_VIRTUAL)
1455
                return;
1456
1457
        folder = item->folder;
1458
1459
        fs_path = item->path ?
1460
                g_filename_from_utf8(item->path, -1, NULL, NULL, NULL)
1461
                : g_strdup(".");
1462
        if (!fs_path)
1463
                fs_path = g_strdup(item->path);
1464
#ifdef G_OS_WIN32
1465
        hfind = find_first_file(fs_path, &wfd);
1466
        if (hfind == INVALID_HANDLE_VALUE) {
1467
                g_warning("failed to open directory: %s\n", fs_path);
1468
                g_free(fs_path);
1469
                return;
1470
        }
1471
#else
1472
        dp = opendir(fs_path);
1473
        if (!dp) {
1474
                FILE_OP_ERROR(fs_path, "opendir");
1475
                g_free(fs_path);
1476
                return;
1477
        }
1478
#endif
1479
        g_free(fs_path);
1480
1481
        debug_print("scanning %s ...\n",
1482
                    item->path ? item->path
1483
                    : LOCAL_FOLDER(item->folder)->rootpath);
1484
        if (folder->ui_func)
1485
                folder->ui_func(folder, item, folder->ui_func_data);
1486
1487
#ifdef G_OS_WIN32
1488
        do {
1489
                if (!wfd.file_name) continue;
1490
                if (wfd.file_name[0] == '.') {
1491
                        g_free(wfd.file_name);
1492
                        wfd.file_name = NULL;
1493
                        continue;
1494
                }
1495
                dir_name = utf8name = wfd.file_name;
1496
                wfd.file_name = NULL;
1497
#else
1498
        while ((d = readdir(dp)) != NULL) {
1499
                dir_name = d->d_name;
1500
                if (dir_name[0] == '.') continue;
1501
1502
                utf8name = g_filename_to_utf8(dir_name, -1, NULL, NULL, NULL);
1503
                if (!utf8name)
1504
                        utf8name = g_strdup(dir_name);
1505
#endif
1506
1507
                if (item->path) {
1508
                        utf8entry = g_strconcat(item->path, "/", utf8name,
1509
                                                NULL);
1510
                } else
1511
                        utf8entry = g_strdup(utf8name);
1512
                entry = g_filename_from_utf8(utf8entry, -1, NULL, NULL, NULL);
1513
                if (!entry)
1514
                        entry = g_strdup(utf8entry);
1515
#ifdef G_OS_WIN32
1516
                subst_char(entry, '/', G_DIR_SEPARATOR);
1517
#endif
1518
1519
                if (
1520
#ifdef G_OS_WIN32
1521
                        (wfd.file_attr & FILE_ATTRIBUTE_DIRECTORY) != 0
1522
#else
1523
#if HAVE_DIRENT_D_TYPE
1524
                        d->d_type == DT_DIR ||
1525
                        ((d->d_type == DT_UNKNOWN || d->d_type == DT_LNK) &&
1526
#endif
1527
                        g_stat(entry, &s) == 0 && S_ISDIR(s.st_mode)
1528
#if HAVE_DIRENT_D_TYPE
1529
                        )
1530
#endif
1531
#endif /* G_OS_WIN32 */
1532
                   ) {
1533
                        FolderItem *new_item = NULL;
1534
                        GNode *node;
1535
1536
#ifndef G_OS_WIN32
1537
                        if (g_utf8_validate(utf8name, -1, NULL) == FALSE) {
1538
                                g_warning(_("Directory name\n"
1539
                                            "'%s' is not a valid UTF-8 string.\n"
1540
                                            "Maybe the locale encoding is used for filename.\n"
1541
                                            "If that is the case, you must set the following environmental variable\n"
1542
                                            "(see README for detail):\n"
1543
                                            "\n"
1544
                                            "\tG_FILENAME_ENCODING=@locale\n"),
1545
                                          utf8name);
1546
                                g_free(entry);
1547
                                g_free(utf8entry);
1548
                                g_free(utf8name);
1549
                                continue;
1550
                        }
1551
#endif /* G_OS_WIN32 */
1552
1553
                        node = item->node;
1554
                        for (node = node->children; node != NULL; node = node->next) {
1555
                                FolderItem *cur_item = FOLDER_ITEM(node->data);
1556
                                if (!strcmp2(cur_item->path, utf8entry)) {
1557
                                        new_item = cur_item;
1558
                                        break;
1559
                                }
1560
                        }
1561
                        if (!new_item) {
1562
                                debug_print("new folder '%s' found.\n",
1563
                                            utf8entry);
1564
                                new_item = folder_item_new(utf8name, utf8entry);
1565
                                folder_item_append(item, new_item);
1566
                        }
1567
1568
                        if (!item->path) {
1569
                                if (!folder->inbox &&
1570
                                    !strcmp(dir_name, INBOX_DIR)) {
1571
                                        new_item->stype = F_INBOX;
1572
                                        folder->inbox = new_item;
1573
                                } else if (!folder->outbox &&
1574
                                           !strcmp(dir_name, OUTBOX_DIR)) {
1575
                                        new_item->stype = F_OUTBOX;
1576
                                        folder->outbox = new_item;
1577
                                } else if (!folder->draft &&
1578
                                           !strcmp(dir_name, DRAFT_DIR)) {
1579
                                        new_item->stype = F_DRAFT;
1580
                                        folder->draft = new_item;
1581
                                } else if (!folder->queue &&
1582
                                           !strcmp(dir_name, QUEUE_DIR)) {
1583
                                        new_item->stype = F_QUEUE;
1584
                                        folder->queue = new_item;
1585
                                } else if (!folder->trash &&
1586
                                           !strcmp(dir_name, TRASH_DIR)) {
1587
                                        new_item->stype = F_TRASH;
1588
                                        folder->trash = new_item;
1589
                                }
1590
                        }
1591
1592
                        mh_scan_tree_recursive(new_item);
1593
                } else if (to_number(dir_name) > 0) n_msg++;
1594
1595
                g_free(entry);
1596
                g_free(utf8entry);
1597
                g_free(utf8name);
1598
#ifdef G_OS_WIN32
1599
        } while (find_next_file(hfind, &wfd));
1600
#else
1601
        }
1602
#endif
1603
1604
#ifdef G_OS_WIN32
1605
        FindClose(hfind);
1606
#else
1607
        closedir(dp);
1608
#endif
1609
1610
        if (item->path) {
1611
                gint new, unread, total, min, max;
1612
1613
                procmsg_get_mark_sum
1614
                        (item, &new, &unread, &total, &min, &max, 0);
1615
                if (n_msg > total) {
1616
                        new += n_msg - total;
1617
                        unread += n_msg - total;
1618
                }
1619
                item->new = new;
1620
                item->unread = unread;
1621
                item->total = n_msg;
1622
                item->updated = TRUE;
1623
                item->mtime = 0;
1624
        }
1625
}
1626
1627
static gboolean mh_rename_folder_func(GNode *node, gpointer data)
1628
{
1629
        FolderItem *item = node->data;
1630
        gchar **paths = data;
1631
        const gchar *oldpath = paths[0];
1632
        const gchar *newpath = paths[1];
1633
        gchar *base;
1634
        gchar *new_itempath;
1635
        gint oldpathlen;
1636
1637
        oldpathlen = strlen(oldpath);
1638
        if (strncmp(oldpath, item->path, oldpathlen) != 0) {
1639
                g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
1640
                return TRUE;
1641
        }
1642
1643
        base = item->path + oldpathlen;
1644
        while (*base == '/') base++;
1645
        if (*base == '\0')
1646
                new_itempath = g_strdup(newpath);
1647
        else
1648
                new_itempath = g_strconcat(newpath, "/", base, NULL);
1649
        g_free(item->path);
1650
        item->path = new_itempath;
1651
1652
        return FALSE;
1653
}