Statistics
| Revision:

root / src / mh.c @ 1

History | View | Annotate | Download (28.3 KB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2003 Hiroyuki Yamamoto
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program 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
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 <dirent.h>
28
#include <sys/stat.h>
29
#include <unistd.h>
30
#include <string.h>
31
#include <errno.h>
32

    
33
#undef MEASURE_TIME
34

    
35
#ifdef MEASURE_TIME
36
#  include <sys/time.h>
37
#endif
38

    
39
#include "intl.h"
40
#include "folder.h"
41
#include "mh.h"
42
#include "procmsg.h"
43
#include "procheader.h"
44
#include "utils.h"
45

    
46
static void        mh_folder_init                (Folder                *folder,
47
                                         const gchar        *name,
48
                                         const gchar        *path);
49

    
50
static Folder        *mh_folder_new                (const gchar        *name,
51
                                         const gchar        *path);
52
static void     mh_folder_destroy        (Folder                *folder);
53

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

    
96
static gint    mh_scan_folder_full        (Folder                *folder,
97
                                         FolderItem        *item,
98
                                         gboolean         count_sum);
99
static gint    mh_scan_folder                (Folder                *folder,
100
                                         FolderItem        *item);
101
static gint    mh_scan_tree                (Folder                *folder);
102

    
103
static gint    mh_create_tree                (Folder                *folder);
104
static FolderItem *mh_create_folder        (Folder                *folder,
105
                                         FolderItem        *parent,
106
                                         const gchar        *name);
107
static gint    mh_rename_folder                (Folder                *folder,
108
                                         FolderItem        *item,
109
                                         const gchar        *name);
110
static gint    mh_remove_folder                (Folder                *folder,
111
                                         FolderItem        *item);
112

    
113
static gchar   *mh_get_new_msg_filename                (FolderItem        *dest);
114

    
115
static gint        mh_do_move_msgs                        (Folder                *folder,
116
                                                 FolderItem        *dest,
117
                                                 GSList                *msglist);
118

    
119
static time_t  mh_get_mtime                        (FolderItem        *item);
120
static GSList  *mh_get_uncached_msgs                (GHashTable        *msg_table,
121
                                                 FolderItem        *item);
122
static MsgInfo *mh_parse_msg                        (const gchar        *file,
123
                                                 FolderItem        *item);
124
static void        mh_remove_missing_folder_items        (Folder                *folder);
125
static void        mh_scan_tree_recursive                (FolderItem        *item);
126

    
127
static gboolean mh_rename_folder_func                (GNode                *node,
128
                                                 gpointer         data);
129

    
130
static FolderClass mh_class =
131
{
132
        F_MH,
133

    
134
        mh_folder_new,
135
        mh_folder_destroy,
136

    
137
        mh_scan_tree,
138
        mh_create_tree,
139

    
140
        mh_get_msg_list,
141
        mh_fetch_msg,
142
        mh_get_msginfo,
143
        mh_add_msg,
144
        mh_add_msgs,
145
        mh_move_msg,
146
        mh_move_msgs,
147
        mh_copy_msg,
148
        mh_copy_msgs,
149
        mh_remove_msg,
150
        NULL,
151
        mh_remove_all_msg,
152
        mh_is_msg_changed,
153
        mh_close,
154
        mh_scan_folder,
155

    
156
        mh_create_folder,
157
        mh_rename_folder,
158
        mh_remove_folder,
159
};
160

    
161

    
162
FolderClass *mh_get_class(void)
163
{
164
        return &mh_class;
165
}
166

    
167
static Folder *mh_folder_new(const gchar *name, const gchar *path)
168
{
169
        Folder *folder;
170

    
171
        folder = (Folder *)g_new0(MHFolder, 1);
172
        mh_folder_init(folder, name, path);
173

    
174
        return folder;
175
}
176

    
177
static void mh_folder_destroy(Folder *folder)
178
{
179
        folder_local_folder_destroy(LOCAL_FOLDER(folder));
180
}
181

    
182
static void mh_folder_init(Folder *folder, const gchar *name, const gchar *path)
183
{
184
        folder->klass = mh_get_class();
185
        folder_local_folder_init(folder, name, path);
186
}
187

    
188
static GSList *mh_get_msg_list(Folder *folder, FolderItem *item,
189
                               gboolean use_cache)
190
{
191
        GSList *mlist;
192
        GHashTable *msg_table;
193
        time_t cur_mtime;
194
#ifdef MEASURE_TIME
195
        struct timeval tv_before, tv_after, tv_result;
196

    
197
        gettimeofday(&tv_before, NULL);
198
#endif
199

    
200
        g_return_val_if_fail(item != NULL, NULL);
201

    
202
        cur_mtime = mh_get_mtime(item);
203

    
204
        if (use_cache && item->mtime == cur_mtime) {
205
                debug_print("Folder is not modified.\n");
206
                mlist = procmsg_read_cache(item, FALSE);
207
                if (!mlist)
208
                        mlist = mh_get_uncached_msgs(NULL, item);
209
        } else if (use_cache) {
210
                GSList *newlist;
211

    
212
                mlist = procmsg_read_cache(item, TRUE);
213
                msg_table = procmsg_msg_hash_table_create(mlist);
214

    
215
                newlist = mh_get_uncached_msgs(msg_table, item);
216
                if (msg_table)
217
                        g_hash_table_destroy(msg_table);
218

    
219
                mlist = g_slist_concat(mlist, newlist);
220
        } else
221
                mlist = mh_get_uncached_msgs(NULL, item);
222

    
223
        item->mtime = cur_mtime;
224

    
225
        procmsg_set_flags(mlist, item);
226

    
227
        mlist = procmsg_sort_msg_list(mlist, item->sort_key, item->sort_type);
228

    
229
#ifdef MEASURE_TIME
230
        gettimeofday(&tv_after, NULL);
231

    
232
        timersub(&tv_after, &tv_before, &tv_result);
233
        g_print("mh_get_msg_list: %s: elapsed time: %ld.%06ld sec\n",
234
                item->path, tv_result.tv_sec, tv_result.tv_usec);
235
#endif
236

    
237
        return mlist;
238
}
239

    
240
static gchar *mh_fetch_msg(Folder *folder, FolderItem *item, gint num)
241
{
242
        gchar *path;
243
        gchar *file;
244

    
245
        g_return_val_if_fail(item != NULL, NULL);
246
        g_return_val_if_fail(num > 0, NULL);
247

    
248
        if (item->last_num < 0 || num > item->last_num) {
249
                mh_scan_folder(folder, item);
250
                if (item->last_num < 0) return NULL;
251
        }
252

    
253
        g_return_val_if_fail(num <= item->last_num, NULL);
254

    
255
        path = folder_item_get_path(item);
256
        file = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
257
        g_free(path);
258
        if (!is_file_exist(file)) {
259
                g_free(file);
260
                return NULL;
261
        }
262

    
263
        return file;
264
}
265

    
266
static MsgInfo *mh_get_msginfo(Folder *folder, FolderItem *item, gint num)
267
{
268
        MsgInfo *msginfo;
269
        gchar *file;
270

    
271
        g_return_val_if_fail(item != NULL, NULL);
272
        g_return_val_if_fail(num > 0, NULL);
273

    
274
        file = mh_fetch_msg(folder, item, num);
275
        if (!file) return NULL;
276

    
277
        msginfo = mh_parse_msg(file, item);
278
        if (msginfo)
279
                msginfo->msgnum = num;
280

    
281
        g_free(file);
282

    
283
        return msginfo;
284
}
285

    
286
static gchar *mh_get_new_msg_filename(FolderItem *dest)
287
{
288
        gchar *destfile;
289
        gchar *destpath;
290

    
291
        destpath = folder_item_get_path(dest);
292
        g_return_val_if_fail(destpath != NULL, NULL);
293

    
294
        if (!is_dir_exist(destpath))
295
                make_dir_hier(destpath);
296

    
297
        for (;;) {
298
                destfile = g_strdup_printf("%s%c%d", destpath, G_DIR_SEPARATOR,
299
                                           dest->last_num + 1);
300
                if (is_file_entry_exist(destfile)) {
301
                        dest->last_num++;
302
                        g_free(destfile);
303
                } else
304
                        break;
305
        }
306

    
307
        g_free(destpath);
308

    
309
        return destfile;
310
}
311

    
312
#define SET_DEST_MSG_FLAGS(fp, dest, n, fl)                                \
313
{                                                                        \
314
        MsgInfo newmsginfo;                                                \
315
                                                                        \
316
        newmsginfo.msgnum = n;                                                \
317
        newmsginfo.flags = fl;                                                \
318
        if (dest->stype == F_OUTBOX ||                                        \
319
            dest->stype == F_QUEUE  ||                                        \
320
            dest->stype == F_DRAFT  ||                                        \
321
            dest->stype == F_TRASH)                                        \
322
                MSG_UNSET_PERM_FLAGS(newmsginfo.flags,                        \
323
                                     MSG_NEW|MSG_UNREAD|MSG_DELETED);        \
324
                                                                        \
325
        if (fp)                                                                \
326
                procmsg_write_flags(&newmsginfo, fp);                        \
327
        else if (dest->opened)                                                \
328
                procmsg_add_flags(dest, n, newmsginfo.flags);                \
329
}
330

    
331
static gint mh_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
332
                       MsgFlags *flags, gboolean remove_source)
333
{
334
        GSList file_list;
335
        MsgFileInfo fileinfo;
336

    
337
        g_return_val_if_fail(file != NULL, -1);
338

    
339
        fileinfo.file = (gchar *)file;
340
        fileinfo.flags = flags;
341
        file_list.data = &fileinfo;
342
        file_list.next = NULL;
343

    
344
        return mh_add_msgs(folder, dest, &file_list, remove_source, NULL);
345
}
346

    
347
static gint mh_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
348
                        gboolean remove_source, gint *first)
349
{
350
        gchar *destfile;
351
        GSList *cur;
352
        MsgFileInfo *fileinfo;
353
        gint first_ = 0;
354
        FILE *fp;
355

    
356
        g_return_val_if_fail(dest != NULL, -1);
357
        g_return_val_if_fail(file_list != NULL, -1);
358

    
359
        if (dest->last_num < 0) {
360
                mh_scan_folder(folder, dest);
361
                if (dest->last_num < 0) return -1;
362
        }
363

    
364
        if ((((MsgFileInfo *)file_list->data)->flags == NULL &&
365
            file_list->next == NULL) || dest->opened)
366
                fp = NULL;
367
        else if ((fp = procmsg_open_mark_file(dest, DATA_APPEND)) == NULL)
368
                g_warning("Can't open mark file.\n");
369

    
370
        for (cur = file_list; cur != NULL; cur = cur->next) {
371
                fileinfo = (MsgFileInfo *)cur->data;
372

    
373
                destfile = mh_get_new_msg_filename(dest);
374
                if (destfile == NULL) return -1;
375
                if (first_ == 0 || first_ > dest->last_num + 1)
376
                        first_ = dest->last_num + 1;
377

    
378
                if (link(fileinfo->file, destfile) < 0) {
379
                        if (copy_file(fileinfo->file, destfile, TRUE) < 0) {
380
                                g_warning(_("can't copy message %s to %s\n"),
381
                                          fileinfo->file, destfile);
382
                                g_free(destfile);
383
                                return -1;
384
                        }
385
                }
386

    
387
                g_free(destfile);
388
                dest->last_num++;
389
                dest->total++;
390
                dest->updated = TRUE;
391

    
392
                if (fileinfo->flags) {
393
                        if (MSG_IS_RECEIVED(*fileinfo->flags)) {
394
                                if (dest->unmarked_num == 0)
395
                                        dest->new = 0;
396
                                dest->unmarked_num++;
397
                                procmsg_add_mark_queue(dest, dest->last_num,
398
                                                       *fileinfo->flags);
399
                        } else {
400
                                SET_DEST_MSG_FLAGS(fp, dest, dest->last_num,
401
                                                   *fileinfo->flags);
402
                        }
403
                        if (MSG_IS_NEW(*fileinfo->flags))
404
                                dest->new++;
405
                        if (MSG_IS_UNREAD(*fileinfo->flags))
406
                                dest->unread++;
407
                } else {
408
                        if (dest->unmarked_num == 0)
409
                                dest->new = 0;
410
                        dest->unmarked_num++;
411
                        dest->new++;
412
                        dest->unread++;
413
                }
414
        }
415

    
416
        if (fp) fclose(fp);
417

    
418
        if (first)
419
                *first = first_;
420

    
421
        if (remove_source) {
422
                for (cur = file_list; cur != NULL; cur = cur->next) {
423
                        fileinfo = (MsgFileInfo *)cur->data;
424
                        if (unlink(fileinfo->file) < 0)
425
                                FILE_OP_ERROR(fileinfo->file, "unlink");
426
                }
427
        }
428

    
429
        return dest->last_num;
430
}
431

    
432
static gint mh_do_move_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
433
{
434
        FolderItem *src;
435
        gchar *srcfile;
436
        gchar *destfile;
437
        FILE *fp;
438
        GSList *cur;
439
        MsgInfo *msginfo;
440

    
441
        g_return_val_if_fail(dest != NULL, -1);
442
        g_return_val_if_fail(msglist != 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 (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 = msglist; cur != NULL; cur = cur->next) {
455
                msginfo = (MsgInfo *)cur->data;
456
                src = msginfo->folder;
457

    
458
                if (src == dest) {
459
                        g_warning(_("the src folder is identical to the dest.\n"));
460
                        continue;
461
                }
462
                debug_print("Moving message %s%c%d to %s ...\n",
463
                            src->path, G_DIR_SEPARATOR, msginfo->msgnum,
464
                            dest->path);
465

    
466
                destfile = mh_get_new_msg_filename(dest);
467
                if (!destfile) break;
468
                srcfile = procmsg_get_message_file(msginfo);
469

    
470
                if (move_file(srcfile, destfile, FALSE) < 0) {
471
                        g_free(srcfile);
472
                        g_free(destfile);
473
                        break;
474
                }
475

    
476
                g_free(srcfile);
477
                g_free(destfile);
478
                src->total--;
479
                src->updated = TRUE;
480
                dest->last_num++;
481
                dest->total++;
482
                dest->updated = TRUE;
483

    
484
                if (fp) {
485
                        SET_DEST_MSG_FLAGS(fp, dest, dest->last_num,
486
                                           msginfo->flags);
487
                }
488

    
489
                if (MSG_IS_NEW(msginfo->flags)) {
490
                        src->new--;
491
                        dest->new++;
492
                }
493
                if (MSG_IS_UNREAD(msginfo->flags)) {
494
                        src->unread--;
495
                        dest->unread++;
496
                }
497

    
498
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID);
499
        }
500

    
501
        if (fp) fclose(fp);
502

    
503
        return dest->last_num;
504
}
505

    
506
static gint mh_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
507
{
508
        GSList msglist;
509

    
510
        g_return_val_if_fail(msginfo != NULL, -1);
511

    
512
        msglist.data = msginfo;
513
        msglist.next = NULL;
514

    
515
        return mh_move_msgs(folder, dest, &msglist);
516
}
517

    
518
static gint mh_move_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
519
{
520
        MsgInfo *msginfo;
521
        GSList *file_list;
522
        gint ret = 0;
523
        gint first;
524

    
525
        msginfo = (MsgInfo *)msglist->data;
526
        if (folder == msginfo->folder->folder)
527
                return mh_do_move_msgs(folder, dest, msglist);
528

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

    
532
        ret = mh_add_msgs(folder, dest, file_list, FALSE, &first);
533

    
534
        procmsg_message_file_list_free(file_list);
535

    
536
        if (ret != -1)
537
                ret = folder_item_remove_msgs(msginfo->folder, msglist);
538

    
539
        return ret;
540
}
541

    
542
static gint mh_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
543
{
544
        GSList msglist;
545

    
546
        g_return_val_if_fail(msginfo != NULL, -1);
547

    
548
        msglist.data = msginfo;
549
        msglist.next = NULL;
550

    
551
        return mh_copy_msgs(folder, dest, &msglist);
552
}
553

    
554
static gint mh_copy_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
555
{
556
        gchar *srcfile;
557
        gchar *destfile;
558
        FILE *fp;
559
        GSList *cur;
560
        MsgInfo *msginfo;
561

    
562
        g_return_val_if_fail(dest != NULL, -1);
563
        g_return_val_if_fail(msglist != NULL, -1);
564

    
565
        if (dest->last_num < 0) {
566
                mh_scan_folder(folder, dest);
567
                if (dest->last_num < 0) return -1;
568
        }
569

    
570
        if (dest->opened)
571
                fp = NULL;
572
        else if ((fp = procmsg_open_mark_file(dest, DATA_APPEND)) == NULL)
573
                g_warning(_("Can't open mark file.\n"));
574

    
575
        for (cur = msglist; cur != NULL; cur = cur->next) {
576
                msginfo = (MsgInfo *)cur->data;
577

    
578
                if (msginfo->folder == dest) {
579
                        g_warning(_("the src folder is identical to the dest.\n"));
580
                        continue;
581
                }
582
                debug_print(_("Copying message %s%c%d to %s ...\n"),
583
                            msginfo->folder->path, G_DIR_SEPARATOR,
584
                            msginfo->msgnum, dest->path);
585

    
586
                destfile = mh_get_new_msg_filename(dest);
587
                if (!destfile) break;
588
                srcfile = procmsg_get_message_file(msginfo);
589

    
590
                if (copy_file(srcfile, destfile, TRUE) < 0) {
591
                        FILE_OP_ERROR(srcfile, "copy");
592
                        g_free(srcfile);
593
                        g_free(destfile);
594
                        break;
595
                }
596

    
597
                g_free(srcfile);
598
                g_free(destfile);
599
                dest->last_num++;
600
                dest->total++;
601
                dest->updated = TRUE;
602

    
603
                if (fp) {
604
                        SET_DEST_MSG_FLAGS(fp, dest, dest->last_num,
605
                                           msginfo->flags);
606
                }
607

    
608
                if (MSG_IS_NEW(msginfo->flags))
609
                        dest->new++;
610
                if (MSG_IS_UNREAD(msginfo->flags))
611
                        dest->unread++;
612
        }
613

    
614
        if (fp) fclose(fp);
615

    
616
        return dest->last_num;
617
}
618

    
619
static gint mh_remove_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo)
620
{
621
        gchar *file;
622

    
623
        g_return_val_if_fail(item != NULL, -1);
624

    
625
        file = mh_fetch_msg(folder, item, msginfo->msgnum);
626
        g_return_val_if_fail(file != NULL, -1);
627

    
628
        if (unlink(file) < 0) {
629
                FILE_OP_ERROR(file, "unlink");
630
                g_free(file);
631
                return -1;
632
        }
633
        g_free(file);
634

    
635
        item->total--;
636
        item->updated = TRUE;
637
        if (MSG_IS_NEW(msginfo->flags))
638
                item->new--;
639
        if (MSG_IS_UNREAD(msginfo->flags))
640
                item->unread--;
641
        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID);
642

    
643
        if (msginfo->msgnum == item->last_num)
644
                item->last_num = mh_scan_folder_full(folder, item, FALSE);
645

    
646
        return 0;
647
}
648

    
649
static gint mh_remove_all_msg(Folder *folder, FolderItem *item)
650
{
651
        gchar *path;
652
        gint val;
653

    
654
        g_return_val_if_fail(item != NULL, -1);
655

    
656
        path = folder_item_get_path(item);
657
        g_return_val_if_fail(path != NULL, -1);
658
        val = remove_all_numbered_files(path);
659
        g_free(path);
660
        if (val == 0) {
661
                item->new = item->unread = item->total = 0;
662
                item->last_num = 0;
663
                item->updated = TRUE;
664
        }
665

    
666
        return val;
667
}
668

    
669
static gboolean mh_is_msg_changed(Folder *folder, FolderItem *item,
670
                                  MsgInfo *msginfo)
671
{
672
        struct stat s;
673

    
674
        if (stat(itos(msginfo->msgnum), &s) < 0 ||
675
            msginfo->size  != s.st_size ||
676
            msginfo->mtime != s.st_mtime)
677
                return TRUE;
678

    
679
        return FALSE;
680
}
681

    
682
static gint mh_close(Folder *folder, FolderItem *item)
683
{
684
        return 0;
685
}
686

    
687
static gint mh_scan_folder_full(Folder *folder, FolderItem *item,
688
                                gboolean count_sum)
689
{
690
        gchar *path;
691
        DIR *dp;
692
        struct dirent *d;
693
        gint max = 0;
694
        gint num;
695
        gint n_msg = 0;
696

    
697
        g_return_val_if_fail(item != NULL, -1);
698

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

    
701
        path = folder_item_get_path(item);
702
        g_return_val_if_fail(path != NULL, -1);
703
        if (change_dir(path) < 0) {
704
                g_free(path);
705
                return -1;
706
        }
707
        g_free(path);
708

    
709
        if ((dp = opendir(".")) == NULL) {
710
                FILE_OP_ERROR(item->path, "opendir");
711
                return -1;
712
        }
713

    
714
        if (folder->ui_func)
715
                folder->ui_func(folder, item, folder->ui_func_data);
716

    
717
        while ((d = readdir(dp)) != NULL) {
718
                if ((num = to_number(d->d_name)) >= 0 &&
719
                    dirent_is_regular_file(d)) {
720
                        n_msg++;
721
                        if (max < num)
722
                                max = num;
723
                }
724
        }
725

    
726
        closedir(dp);
727

    
728
        if (n_msg == 0)
729
                item->new = item->unread = item->total = 0;
730
        else if (count_sum) {
731
                gint new, unread, total, min, max_;
732

    
733
                procmsg_get_mark_sum
734
                        (item, &new, &unread, &total, &min, &max_, 0);
735

    
736
                if (n_msg > total) {
737
                        item->unmarked_num = new = n_msg - total;
738
                        unread += n_msg - total;
739
                } else
740
                        item->unmarked_num = 0;
741

    
742
                item->new = new;
743
                item->unread = unread;
744
                item->total = n_msg;
745
        }
746

    
747
        item->updated = TRUE;
748

    
749
        debug_print(_("Last number in dir %s = %d\n"), item->path, max);
750
        item->last_num = max;
751

    
752
        return 0;
753
}
754

    
755
static gint mh_scan_folder(Folder *folder, FolderItem *item)
756
{
757
        return mh_scan_folder_full(folder, item, TRUE);
758
}
759

    
760
static gint mh_scan_tree(Folder *folder)
761
{
762
        FolderItem *item;
763
        gchar *rootpath;
764

    
765
        g_return_val_if_fail(folder != NULL, -1);
766

    
767
        if (!folder->node) {
768
                item = folder_item_new(folder->name, NULL);
769
                item->folder = folder;
770
                folder->node = item->node = g_node_new(item);
771
        } else
772
                item = FOLDER_ITEM(folder->node->data);
773

    
774
        rootpath = folder_item_get_path(item);
775
        if (change_dir(rootpath) < 0) {
776
                g_free(rootpath);
777
                return -1;
778
        }
779
        g_free(rootpath);
780

    
781
        mh_create_tree(folder);
782
        mh_remove_missing_folder_items(folder);
783
        mh_scan_tree_recursive(item);
784

    
785
        return 0;
786
}
787

    
788
#define MAKE_DIR_IF_NOT_EXIST(dir) \
789
{ \
790
        if (!is_dir_exist(dir)) { \
791
                if (is_file_exist(dir)) { \
792
                        g_warning(_("File `%s' already exists.\n" \
793
                                    "Can't create folder."), dir); \
794
                        return -1; \
795
                } \
796
                if (make_dir(dir) < 0) \
797
                        return -1; \
798
        } \
799
}
800

    
801
static gint mh_create_tree(Folder *folder)
802
{
803
        gchar *rootpath;
804

    
805
        g_return_val_if_fail(folder != NULL, -1);
806

    
807
        CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
808
        rootpath = LOCAL_FOLDER(folder)->rootpath;
809
        MAKE_DIR_IF_NOT_EXIST(rootpath);
810
        CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
811
        MAKE_DIR_IF_NOT_EXIST(INBOX_DIR);
812
        MAKE_DIR_IF_NOT_EXIST(OUTBOX_DIR);
813
        MAKE_DIR_IF_NOT_EXIST(QUEUE_DIR);
814
        MAKE_DIR_IF_NOT_EXIST(DRAFT_DIR);
815
        MAKE_DIR_IF_NOT_EXIST(TRASH_DIR);
816

    
817
        return 0;
818
}
819

    
820
#undef MAKE_DIR_IF_NOT_EXIST
821

    
822
static FolderItem *mh_create_folder(Folder *folder, FolderItem *parent,
823
                                    const gchar *name)
824
{
825
        gchar *path;
826
        gchar *fs_name;
827
        gchar *fullpath;
828
        FolderItem *new_item;
829

    
830
        g_return_val_if_fail(folder != NULL, NULL);
831
        g_return_val_if_fail(parent != NULL, NULL);
832
        g_return_val_if_fail(name != NULL, NULL);
833

    
834
        path = folder_item_get_path(parent);
835
        fs_name = g_filename_from_utf8(name, -1, NULL, NULL, NULL);
836
        fullpath = g_strconcat(path, G_DIR_SEPARATOR_S,
837
                               fs_name ? fs_name : name, NULL);
838
        g_free(fs_name);
839
        g_free(path);
840

    
841
        if (make_dir(fullpath) < 0) {
842
                g_free(fullpath);
843
                return NULL;
844
        }
845

    
846
        g_free(fullpath);
847

    
848
        if (parent->path)
849
                path = g_strconcat(parent->path, G_DIR_SEPARATOR_S, name,
850
                                   NULL);
851
        else
852
                path = g_strdup(name);
853
        new_item = folder_item_new(name, path);
854
        folder_item_append(parent, new_item);
855
        g_free(path);
856

    
857
        return new_item;
858
}
859

    
860
static gint mh_rename_folder(Folder *folder, FolderItem *item,
861
                             const gchar *name)
862
{
863
        gchar *fs_name;
864
        gchar *oldpath;
865
        gchar *dirname;
866
        gchar *newpath;
867
        gchar *paths[2];
868

    
869
        g_return_val_if_fail(folder != NULL, -1);
870
        g_return_val_if_fail(item != NULL, -1);
871
        g_return_val_if_fail(item->path != NULL, -1);
872
        g_return_val_if_fail(name != NULL, -1);
873

    
874
        oldpath = folder_item_get_path(item);
875
        dirname = g_dirname(oldpath);
876
        fs_name = g_filename_from_utf8(name, -1, NULL, NULL, NULL);
877
        newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S,
878
                              fs_name ? fs_name : name, NULL);
879
        g_free(fs_name);
880
        g_free(dirname);
881

    
882
        if (rename(oldpath, newpath) < 0) {
883
                FILE_OP_ERROR(oldpath, "rename");
884
                g_free(oldpath);
885
                g_free(newpath);
886
                return -1;
887
        }
888

    
889
        g_free(oldpath);
890
        g_free(newpath);
891

    
892
        if (strchr(item->path, G_DIR_SEPARATOR) != NULL) {
893
                dirname = g_dirname(item->path);
894
                newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, name, NULL);
895
                g_free(dirname);
896
        } else
897
                newpath = g_strdup(name);
898

    
899
        g_free(item->name);
900
        item->name = g_strdup(name);
901

    
902
        paths[0] = g_strdup(item->path);
903
        paths[1] = newpath;
904
        g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
905
                        mh_rename_folder_func, paths);
906

    
907
        g_free(paths[0]);
908
        g_free(paths[1]);
909
        return 0;
910
}
911

    
912
static gint mh_remove_folder(Folder *folder, FolderItem *item)
913
{
914
        gchar *path;
915

    
916
        g_return_val_if_fail(folder != NULL, -1);
917
        g_return_val_if_fail(item != NULL, -1);
918
        g_return_val_if_fail(item->path != NULL, -1);
919

    
920
        path = folder_item_get_path(item);
921
        if (remove_dir_recursive(path) < 0) {
922
                g_warning("can't remove directory `%s'\n", path);
923
                g_free(path);
924
                return -1;
925
        }
926

    
927
        g_free(path);
928
        folder_item_remove(item);
929
        return 0;
930
}
931

    
932

    
933
static time_t mh_get_mtime(FolderItem *item)
934
{
935
        gchar *path;
936
        struct stat s;
937

    
938
        path = folder_item_get_path(item);
939
        if (stat(path, &s) < 0) {
940
                FILE_OP_ERROR(path, "stat");
941
                return -1;
942
        } else {
943
                return MAX(s.st_mtime, s.st_ctime);
944
        }
945
}
946

    
947
static GSList *mh_get_uncached_msgs(GHashTable *msg_table, FolderItem *item)
948
{
949
        gchar *path;
950
        DIR *dp;
951
        struct dirent *d;
952
        GSList *newlist = NULL;
953
        GSList *last = NULL;
954
        MsgInfo *msginfo;
955
        gint n_newmsg = 0;
956
        gint num;
957

    
958
        g_return_val_if_fail(item != NULL, NULL);
959

    
960
        path = folder_item_get_path(item);
961
        g_return_val_if_fail(path != NULL, NULL);
962
        if (change_dir(path) < 0) {
963
                g_free(path);
964
                return NULL;
965
        }
966
        g_free(path);
967

    
968
        if ((dp = opendir(".")) == NULL) {
969
                FILE_OP_ERROR(item->path, "opendir");
970
                return NULL;
971
        }
972

    
973
        debug_print("Searching uncached messages...\n");
974

    
975
        if (msg_table) {
976
                while ((d = readdir(dp)) != NULL) {
977
                        if ((num = to_number(d->d_name)) < 0) continue;
978

    
979
                        msginfo = g_hash_table_lookup
980
                                (msg_table, GUINT_TO_POINTER(num));
981

    
982
                        if (!msginfo) {
983
                                /* not found in the cache (uncached message) */
984
                                msginfo = mh_parse_msg(d->d_name, item);
985
                                if (!msginfo) continue;
986

    
987
                                if (!newlist)
988
                                        last = newlist =
989
                                                g_slist_append(NULL, msginfo);
990
                                else {
991
                                        last = g_slist_append(last, msginfo);
992
                                        last = last->next;
993
                                }
994
                                n_newmsg++;
995
                        }
996
                }
997
        } else {
998
                /* discard all previous cache */
999
                while ((d = readdir(dp)) != NULL) {
1000
                        if (to_number(d->d_name) < 0) continue;
1001

    
1002
                        msginfo = mh_parse_msg(d->d_name, item);
1003
                        if (!msginfo) continue;
1004

    
1005
                        if (!newlist)
1006
                                last = newlist = g_slist_append(NULL, msginfo);
1007
                        else {
1008
                                last = g_slist_append(last, msginfo);
1009
                                last = last->next;
1010
                        }
1011
                        n_newmsg++;
1012
                }
1013
        }
1014

    
1015
        closedir(dp);
1016

    
1017
        if (n_newmsg)
1018
                debug_print("%d uncached message(s) found.\n", n_newmsg);
1019
        else
1020
                debug_print("done.\n");
1021

    
1022
        /* sort new messages in numerical order */
1023
        if (newlist && item->sort_key == SORT_BY_NONE) {
1024
                debug_print("Sorting uncached messages in numerical order...\n");
1025
                newlist = g_slist_sort
1026
                        (newlist, (GCompareFunc)procmsg_cmp_msgnum_for_sort);
1027
                debug_print("done.\n");
1028
        }
1029

    
1030
        return newlist;
1031
}
1032

    
1033
static MsgInfo *mh_parse_msg(const gchar *file, FolderItem *item)
1034
{
1035
        MsgInfo *msginfo;
1036
        MsgFlags flags;
1037

    
1038
        g_return_val_if_fail(item != NULL, NULL);
1039
        g_return_val_if_fail(file != NULL, NULL);
1040

    
1041
        flags.perm_flags = MSG_NEW|MSG_UNREAD;
1042
        flags.tmp_flags = 0;
1043

    
1044
        if (item->stype == F_QUEUE) {
1045
                MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
1046
        } else if (item->stype == F_DRAFT) {
1047
                MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
1048
        }
1049

    
1050
        msginfo = procheader_parse_file(file, flags, FALSE);
1051
        if (!msginfo) return NULL;
1052

    
1053
        msginfo->msgnum = atoi(file);
1054
        msginfo->folder = item;
1055

    
1056
        return msginfo;
1057
}
1058

    
1059
#if 0
1060
static gboolean mh_is_maildir_one(const gchar *path, const gchar *dir)
1061
{
1062
        gchar *entry;
1063
        gboolean result;
1064

1065
        entry = g_strconcat(path, G_DIR_SEPARATOR_S, dir, NULL);
1066
        result = is_dir_exist(entry);
1067
        g_free(entry);
1068

1069
        return result;
1070
}
1071

1072
/*
1073
 * check whether PATH is a Maildir style mailbox.
1074
 * This is the case if the 3 subdir: new, cur, tmp are existing.
1075
 * This functon assumes that entry is an directory
1076
 */
1077
static gboolean mh_is_maildir(const gchar *path)
1078
{
1079
        return mh_is_maildir_one(path, "new") &&
1080
               mh_is_maildir_one(path, "cur") &&
1081
               mh_is_maildir_one(path, "tmp");
1082
}
1083
#endif
1084

    
1085
static gboolean mh_remove_missing_folder_items_func(GNode *node, gpointer data)
1086
{
1087
        FolderItem *item;
1088
        gchar *path;
1089

    
1090
        g_return_val_if_fail(node->data != NULL, FALSE);
1091

    
1092
        if (G_NODE_IS_ROOT(node))
1093
                return FALSE;
1094

    
1095
        item = FOLDER_ITEM(node->data);
1096

    
1097
        path = folder_item_get_path(item);
1098
        if (!is_dir_exist(path)) {
1099
                debug_print("folder '%s' not found. removing...\n", path);
1100
                folder_item_remove(item);
1101
        }
1102
        g_free(path);
1103

    
1104
        return FALSE;
1105
}
1106

    
1107
static void mh_remove_missing_folder_items(Folder *folder)
1108
{
1109
        g_return_if_fail(folder != NULL);
1110

    
1111
        debug_print("searching missing folders...\n");
1112

    
1113
        g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
1114
                        mh_remove_missing_folder_items_func, folder);
1115
}
1116

    
1117
static void mh_scan_tree_recursive(FolderItem *item)
1118
{
1119
        Folder *folder;
1120
        DIR *dp;
1121
        struct dirent *d;
1122
        struct stat s;
1123
        gchar *fs_path;
1124
        gchar *entry;
1125
        gchar *utf8entry;
1126
        gchar *utf8name;
1127
        gint n_msg = 0;
1128

    
1129
        g_return_if_fail(item != NULL);
1130
        g_return_if_fail(item->folder != NULL);
1131

    
1132
        folder = item->folder;
1133

    
1134
        fs_path = item->path ?
1135
                g_filename_from_utf8(item->path, -1, NULL, NULL, NULL)
1136
                : g_strdup(".");
1137
        if (!fs_path)
1138
                fs_path = g_strdup(item->path);
1139
        dp = opendir(fs_path);
1140
        if (!dp) {
1141
                FILE_OP_ERROR(fs_path, "opendir");
1142
                g_free(fs_path);
1143
                return;
1144
        }
1145
        g_free(fs_path);
1146

    
1147
        debug_print("scanning %s ...\n",
1148
                    item->path ? item->path
1149
                    : LOCAL_FOLDER(item->folder)->rootpath);
1150
        if (folder->ui_func)
1151
                folder->ui_func(folder, item, folder->ui_func_data);
1152

    
1153
        while ((d = readdir(dp)) != NULL) {
1154
                if (d->d_name[0] == '.') continue;
1155

    
1156
                utf8name = g_filename_to_utf8(d->d_name, -1, NULL, NULL, NULL);
1157
                if (!utf8name)
1158
                        utf8name = g_strdup(d->d_name);
1159

    
1160
                if (item->path)
1161
                        utf8entry = g_strconcat(item->path, G_DIR_SEPARATOR_S,
1162
                                                utf8name, NULL);
1163
                else
1164
                        utf8entry = g_strdup(utf8name);
1165
                entry = g_filename_from_utf8(utf8entry, -1, NULL, NULL, NULL);
1166
                if (!entry)
1167
                        entry = g_strdup(utf8entry);
1168

    
1169
                if (
1170
#ifdef HAVE_DIRENT_D_TYPE
1171
                        d->d_type == DT_DIR ||
1172
                        (d->d_type == DT_UNKNOWN &&
1173
#endif
1174
                        stat(entry, &s) == 0 && S_ISDIR(s.st_mode)
1175
#ifdef HAVE_DIRENT_D_TYPE
1176
                        )
1177
#endif
1178
                   ) {
1179
                        FolderItem *new_item = NULL;
1180
                        GNode *node;
1181

    
1182
#if 0
1183
                        if (mh_is_maildir(entry)) {
1184
                                g_free(entry);
1185
                                g_free(utf8entry);
1186
                                g_free(utf8name);
1187
                                continue;
1188
                        }
1189
#endif
1190

    
1191
                        node = item->node;
1192
                        for (node = node->children; node != NULL; node = node->next) {
1193
                                FolderItem *cur_item = FOLDER_ITEM(node->data);
1194
                                if (!strcmp2(cur_item->path, utf8entry)) {
1195
                                        new_item = cur_item;
1196
                                        break;
1197
                                }
1198
                        }
1199
                        if (!new_item) {
1200
                                debug_print("new folder '%s' found.\n", entry);
1201
                                new_item = folder_item_new(utf8name, utf8entry);
1202
                                folder_item_append(item, new_item);
1203
                        }
1204

    
1205
                        if (!item->path) {
1206
                                if (!folder->inbox &&
1207
                                    !strcmp(d->d_name, INBOX_DIR)) {
1208
                                        new_item->stype = F_INBOX;
1209
                                        folder->inbox = new_item;
1210
                                } else if (!folder->outbox &&
1211
                                           !strcmp(d->d_name, OUTBOX_DIR)) {
1212
                                        new_item->stype = F_OUTBOX;
1213
                                        folder->outbox = new_item;
1214
                                } else if (!folder->draft &&
1215
                                           !strcmp(d->d_name, DRAFT_DIR)) {
1216
                                        new_item->stype = F_DRAFT;
1217
                                        folder->draft = new_item;
1218
                                } else if (!folder->queue &&
1219
                                           !strcmp(d->d_name, QUEUE_DIR)) {
1220
                                        new_item->stype = F_QUEUE;
1221
                                        folder->queue = new_item;
1222
                                } else if (!folder->trash &&
1223
                                           !strcmp(d->d_name, TRASH_DIR)) {
1224
                                        new_item->stype = F_TRASH;
1225
                                        folder->trash = new_item;
1226
                                }
1227
                        }
1228

    
1229
                        mh_scan_tree_recursive(new_item);
1230
                } else if (to_number(d->d_name) != -1) n_msg++;
1231

    
1232
                g_free(entry);
1233
                g_free(utf8entry);
1234
                g_free(utf8name);
1235
        }
1236

    
1237
        closedir(dp);
1238

    
1239
        if (item->path) {
1240
                gint new, unread, total, min, max;
1241

    
1242
                procmsg_get_mark_sum
1243
                        (item, &new, &unread, &total, &min, &max, 0);
1244
                if (n_msg > total) {
1245
                        new += n_msg - total;
1246
                        unread += n_msg - total;
1247
                }
1248
                item->new = new;
1249
                item->unread = unread;
1250
                item->total = n_msg;
1251
                item->updated = TRUE;
1252
        }
1253
}
1254

    
1255
static gboolean mh_rename_folder_func(GNode *node, gpointer data)
1256
{
1257
        FolderItem *item = node->data;
1258
        gchar **paths = data;
1259
        const gchar *oldpath = paths[0];
1260
        const gchar *newpath = paths[1];
1261
        gchar *base;
1262
        gchar *new_itempath;
1263
        gint oldpathlen;
1264

    
1265
        oldpathlen = strlen(oldpath);
1266
        if (strncmp(oldpath, item->path, oldpathlen) != 0) {
1267
                g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
1268
                return TRUE;
1269
        }
1270

    
1271
        base = item->path + oldpathlen;
1272
        while (*base == G_DIR_SEPARATOR) base++;
1273
        if (*base == '\0')
1274
                new_itempath = g_strdup(newpath);
1275
        else
1276
                new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
1277
                                           NULL);
1278
        g_free(item->path);
1279
        item->path = new_itempath;
1280

    
1281
        return FALSE;
1282
}