Statistics
| Revision:

root / libsylph / mh.c @ 3233

History | View | Annotate | Download (42.4 KB)

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

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

    
24
#include "defs.h"
25

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

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

    
39
#undef MEASURE_TIME
40

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

    
49
#if USE_THREADS
50
G_LOCK_DEFINE_STATIC(mh);
51
#define S_LOCK(name)        G_LOCK(name)
52
#define S_UNLOCK(name)        G_UNLOCK(name)
53
#else
54
#define S_LOCK(name)
55
#define S_UNLOCK(name)
56
#endif
57

    
58
static void        mh_folder_init                (Folder                *folder,
59
                                         const gchar        *name,
60
                                         const gchar        *path);
61

    
62
static Folder        *mh_folder_new                (const gchar        *name,
63
                                         const gchar        *path);
64
static void     mh_folder_destroy        (Folder                *folder);
65

    
66
static GSList  *mh_get_msg_list                (Folder                *folder,
67
                                         FolderItem        *item,
68
                                         gboolean         use_cache);
69
static GSList  *mh_get_uncached_msg_list(Folder                *folder,
70
                                         FolderItem        *item);
71
static gchar   *mh_fetch_msg                (Folder                *folder,
72
                                         FolderItem        *item,
73
                                         gint                 num);
74
static MsgInfo *mh_get_msginfo                (Folder                *folder,
75
                                         FolderItem        *item,
76
                                         gint                 num);
77
static gint     mh_add_msg                (Folder                *folder,
78
                                         FolderItem        *dest,
79
                                         const gchar        *file,
80
                                         MsgFlags        *flags,
81
                                         gboolean         remove_source);
82
static gint     mh_add_msgs                (Folder                *folder,
83
                                         FolderItem        *dest,
84
                                         GSList                *file_list,
85
                                         gboolean         remove_source,
86
                                         gint                *first);
87
static gint     mh_add_msg_msginfo        (Folder                *folder,
88
                                         FolderItem        *dest,
89
                                         MsgInfo        *msginfo,
90
                                         gboolean         remove_source);
91
static gint     mh_add_msgs_msginfo        (Folder                *folder,
92
                                         FolderItem        *dest,
93
                                         GSList                *msglist,
94
                                         gboolean         remove_source,
95
                                         gint                *first);
96
static gint     mh_move_msg                (Folder                *folder,
97
                                         FolderItem        *dest,
98
                                         MsgInfo        *msginfo);
99
static gint     mh_move_msgs                (Folder                *folder,
100
                                         FolderItem        *dest,
101
                                         GSList                *msglist);
102
static gint     mh_copy_msg                (Folder                *folder,
103
                                         FolderItem        *dest,
104
                                         MsgInfo        *msginfo);
105
static gint     mh_copy_msgs                (Folder                *folder,
106
                                         FolderItem        *dest,
107
                                         GSList                *msglist);
108
static gint     mh_remove_msg                (Folder                *folder,
109
                                         FolderItem        *item,
110
                                         MsgInfo        *msginfo);
111
static gint     mh_remove_all_msg        (Folder                *folder,
112
                                         FolderItem        *item);
113
static gboolean mh_is_msg_changed        (Folder                *folder,
114
                                         FolderItem        *item,
115
                                         MsgInfo        *msginfo);
116
static gint    mh_close                        (Folder                *folder,
117
                                         FolderItem        *item);
118

    
119
static gint    mh_scan_folder_full        (Folder                *folder,
120
                                         FolderItem        *item,
121
                                         gboolean         count_sum);
122
static gint    mh_scan_folder                (Folder                *folder,
123
                                         FolderItem        *item);
124
static gint    mh_scan_tree                (Folder                *folder);
125

    
126
static gint    mh_create_tree                (Folder                *folder);
127
static FolderItem *mh_create_folder        (Folder                *folder,
128
                                         FolderItem        *parent,
129
                                         const gchar        *name);
130
static gint    mh_rename_folder                (Folder                *folder,
131
                                         FolderItem        *item,
132
                                         const gchar        *name);
133
static gint    mh_move_folder                (Folder                *folder,
134
                                         FolderItem        *item,
135
                                         FolderItem        *new_parent);
136
static gint    mh_remove_folder                (Folder                *folder,
137
                                         FolderItem        *item);
138

    
139
static gchar   *mh_get_new_msg_filename                (FolderItem        *dest);
140

    
141
static gint        mh_do_move_msgs                        (Folder                *folder,
142
                                                 FolderItem        *dest,
143
                                                 GSList                *msglist);
144

    
145
static time_t  mh_get_mtime                        (FolderItem        *item);
146
static GSList  *mh_get_uncached_msgs                (GHashTable        *msg_table,
147
                                                 FolderItem        *item);
148
static MsgInfo *mh_parse_msg                        (const gchar        *file,
149
                                                 FolderItem        *item);
150
static void        mh_remove_missing_folder_items        (Folder                *folder);
151
static void        mh_scan_tree_recursive                (FolderItem        *item);
152

    
153
static gboolean mh_rename_folder_func                (GNode                *node,
154
                                                 gpointer         data);
155

    
156
static FolderClass mh_class =
157
{
158
        F_MH,
159

    
160
        mh_folder_new,
161
        mh_folder_destroy,
162

    
163
        mh_scan_tree,
164
        mh_create_tree,
165

    
166
        mh_get_msg_list,
167
        mh_get_uncached_msg_list,
168
        mh_fetch_msg,
169
        mh_get_msginfo,
170
        mh_add_msg,
171
        mh_add_msgs,
172
        mh_add_msg_msginfo,
173
        mh_add_msgs_msginfo,
174
        mh_move_msg,
175
        mh_move_msgs,
176
        mh_copy_msg,
177
        mh_copy_msgs,
178
        mh_remove_msg,
179
        NULL,
180
        mh_remove_all_msg,
181
        mh_is_msg_changed,
182
        mh_close,
183
        mh_scan_folder,
184

    
185
        mh_create_folder,
186
        mh_rename_folder,
187
        mh_move_folder,
188
        mh_remove_folder,
189
};
190

    
191

    
192
FolderClass *mh_get_class(void)
193
{
194
        return &mh_class;
195
}
196

    
197
static Folder *mh_folder_new(const gchar *name, const gchar *path)
198
{
199
        Folder *folder;
200

    
201
        folder = (Folder *)g_new0(MHFolder, 1);
202
        mh_folder_init(folder, name, path);
203

    
204
        return folder;
205
}
206

    
207
static void mh_folder_destroy(Folder *folder)
208
{
209
        folder_local_folder_destroy(LOCAL_FOLDER(folder));
210
}
211

    
212
static void mh_folder_init(Folder *folder, const gchar *name, const gchar *path)
213
{
214
        folder->klass = mh_get_class();
215
        folder_local_folder_init(folder, name, path);
216
}
217

    
218
static GSList *mh_get_msg_list_full(Folder *folder, FolderItem *item,
219
                                    gboolean use_cache, gboolean uncached_only)
220
{
221
        GSList *mlist;
222
        GHashTable *msg_table;
223
        time_t cur_mtime;
224
        GSList *newlist = NULL;
225
#ifdef MEASURE_TIME
226
        GTimer *timer;
227
#endif
228

    
229
        g_return_val_if_fail(item != NULL, NULL);
230

    
231
        S_LOCK(mh);
232

    
233
#ifdef MEASURE_TIME
234
        timer = g_timer_new();
235
#endif
236

    
237
        cur_mtime = mh_get_mtime(item);
238

    
239
        if (use_cache && item->mtime == cur_mtime) {
240
                debug_print("Folder is not modified.\n");
241
                mlist = procmsg_read_cache(item, FALSE);
242
                if (!mlist) {
243
                        mlist = mh_get_uncached_msgs(NULL, item);
244
                        if (mlist)
245
                                item->cache_dirty = TRUE;
246
                }
247
        } else if (use_cache) {
248
                GSList *cur, *next;
249
                gboolean strict_cache_check = prefs_common.strict_cache_check;
250

    
251
                if (item->stype == F_QUEUE || item->stype == F_DRAFT)
252
                        strict_cache_check = TRUE;
253

    
254
                mlist = procmsg_read_cache(item, strict_cache_check);
255
                msg_table = procmsg_msg_hash_table_create(mlist);
256
                newlist = mh_get_uncached_msgs(msg_table, item);
257
                if (newlist)
258
                        item->cache_dirty = TRUE;
259
                if (msg_table)
260
                        g_hash_table_destroy(msg_table);
261

    
262
                if (!strict_cache_check) {
263
                        /* remove nonexistent messages */
264
                        for (cur = mlist; cur != NULL; cur = next) {
265
                                MsgInfo *msginfo = (MsgInfo *)cur->data;
266
                                next = cur->next;
267
                                if (!MSG_IS_CACHED(msginfo->flags)) {
268
                                        debug_print("removing nonexistent message %d from cache\n", msginfo->msgnum);
269
                                        mlist = g_slist_remove(mlist, msginfo);
270
                                        procmsg_msginfo_free(msginfo);
271
                                        item->cache_dirty = TRUE;
272
                                        item->mark_dirty = TRUE;
273
                                }
274
                        }
275
                }
276

    
277
                mlist = g_slist_concat(mlist, newlist);
278
        } else {
279
                mlist = mh_get_uncached_msgs(NULL, item);
280
                item->cache_dirty = TRUE;
281
                newlist = mlist;
282
        }
283

    
284
        procmsg_set_flags(mlist, item);
285

    
286
        if (!uncached_only)
287
                mlist = procmsg_sort_msg_list(mlist, item->sort_key,
288
                                              item->sort_type);
289

    
290
        if (item->mark_queue)
291
                item->mark_dirty = TRUE;
292

    
293
#ifdef MEASURE_TIME
294
        g_timer_stop(timer);
295
        g_print("%s: %s: elapsed time: %f sec\n",
296
                G_STRFUNC, item->path, g_timer_elapsed(timer, NULL));
297
        g_timer_destroy(timer);
298
#endif
299
        debug_print("cache_dirty: %d, mark_dirty: %d\n",
300
                    item->cache_dirty, item->mark_dirty);
301

    
302
        if (!item->opened) {
303
                item->mtime = cur_mtime;
304
                if (item->cache_dirty)
305
                        procmsg_write_cache_list(item, mlist);
306
                if (item->mark_dirty)
307
                        procmsg_write_flags_list(item, mlist);
308
        }
309

    
310
        if (uncached_only) {
311
                GSList *cur;
312

    
313
                if (newlist == NULL) {
314
                        procmsg_msg_list_free(mlist);
315
                        S_UNLOCK(mh);
316
                        return NULL;
317
                }
318
                if (mlist == newlist) {
319
                        S_UNLOCK(mh);
320
                        return newlist;
321
                }
322
                for (cur = mlist; cur != NULL; cur = cur->next) {
323
                        if (cur->next == newlist) {
324
                                cur->next = NULL;
325
                                procmsg_msg_list_free(mlist);
326
                                S_UNLOCK(mh);
327
                                return newlist;
328
                        }
329
                }
330
                procmsg_msg_list_free(mlist);
331
                S_UNLOCK(mh);
332
                return NULL;
333
        }
334

    
335
        S_UNLOCK(mh);
336
        return mlist;
337
}
338

    
339
static GSList *mh_get_msg_list(Folder *folder, FolderItem *item,
340
                               gboolean use_cache)
341
{
342
        return mh_get_msg_list_full(folder, item, use_cache, FALSE);
343
}
344

    
345
static GSList *mh_get_uncached_msg_list(Folder *folder, FolderItem *item)
346
{
347
        return mh_get_msg_list_full(folder, item, TRUE, TRUE);
348
}
349

    
350
static gchar *mh_fetch_msg(Folder *folder, FolderItem *item, gint num)
351
{
352
        gchar *path;
353
        gchar *file;
354
        gchar buf[16];
355

    
356
        g_return_val_if_fail(item != NULL, NULL);
357
        g_return_val_if_fail(num > 0, NULL);
358

    
359
        if (item->last_num < 0 || num > item->last_num) {
360
                mh_scan_folder(folder, item);
361
                if (item->last_num < 0) return NULL;
362
        }
363

    
364
        if (num > item->last_num)
365
                return NULL;
366

    
367
        path = folder_item_get_path(item);
368
        file = g_strconcat(path, G_DIR_SEPARATOR_S, utos_buf(buf, num), NULL);
369
        g_free(path);
370
        if (!is_file_exist(file)) {
371
                g_free(file);
372
                return NULL;
373
        }
374

    
375
        return file;
376
}
377

    
378
static MsgInfo *mh_get_msginfo(Folder *folder, FolderItem *item, gint num)
379
{
380
        MsgInfo *msginfo;
381
        gchar *file;
382

    
383
        g_return_val_if_fail(item != NULL, NULL);
384
        g_return_val_if_fail(num > 0, NULL);
385

    
386
        file = mh_fetch_msg(folder, item, num);
387
        if (!file) return NULL;
388

    
389
        msginfo = mh_parse_msg(file, item);
390
        if (msginfo)
391
                msginfo->msgnum = num;
392

    
393
        g_free(file);
394

    
395
        return msginfo;
396
}
397

    
398
static gchar *mh_get_new_msg_filename(FolderItem *dest)
399
{
400
        gchar *destfile;
401
        gchar *destpath;
402

    
403
        destpath = folder_item_get_path(dest);
404
        g_return_val_if_fail(destpath != NULL, NULL);
405

    
406
        if (!is_dir_exist(destpath))
407
                make_dir_hier(destpath);
408

    
409
        for (;;) {
410
                destfile = g_strdup_printf("%s%c%d", destpath, G_DIR_SEPARATOR,
411
                                           dest->last_num + 1);
412
                if (is_file_entry_exist(destfile)) {
413
                        dest->last_num++;
414
                        g_free(destfile);
415
                } else
416
                        break;
417
        }
418

    
419
        g_free(destpath);
420

    
421
        return destfile;
422
}
423

    
424
#define SET_DEST_MSG_FLAGS(fp, dest, n, fl)                                \
425
{                                                                        \
426
        MsgInfo newmsginfo;                                                \
427
                                                                        \
428
        newmsginfo.msgnum = n;                                                \
429
        newmsginfo.flags = fl;                                                \
430
        if (dest->stype == F_OUTBOX ||                                        \
431
            dest->stype == F_QUEUE  ||                                        \
432
            dest->stype == F_DRAFT) {                                        \
433
                MSG_UNSET_PERM_FLAGS(newmsginfo.flags,                        \
434
                                     MSG_NEW|MSG_UNREAD|MSG_DELETED);        \
435
        } else if (dest->stype == F_TRASH) {                                \
436
                MSG_UNSET_PERM_FLAGS(newmsginfo.flags, MSG_DELETED);        \
437
        }                                                                \
438
                                                                        \
439
        if (fp)                                                                \
440
                procmsg_write_flags(&newmsginfo, fp);                        \
441
        else                                                                 \
442
                procmsg_add_mark_queue(dest, n, newmsginfo.flags);        \
443
}
444

    
445
static gint mh_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
446
                       MsgFlags *flags, gboolean remove_source)
447
{
448
        GSList file_list;
449
        MsgFileInfo fileinfo;
450

    
451
        g_return_val_if_fail(file != NULL, -1);
452

    
453
        fileinfo.file = (gchar *)file;
454
        fileinfo.flags = flags;
455
        file_list.data = &fileinfo;
456
        file_list.next = NULL;
457

    
458
        return mh_add_msgs(folder, dest, &file_list, remove_source, NULL);
459
}
460

    
461
static gint mh_add_msgs(Folder *folder, FolderItem *dest, GSList *file_list,
462
                        gboolean remove_source, gint *first)
463
{
464
        gchar *destfile;
465
        GSList *cur;
466
        MsgFileInfo *fileinfo;
467
        MsgInfo *msginfo;
468
        gint first_ = 0;
469
        FILE *fp = NULL;
470

    
471
        g_return_val_if_fail(dest != NULL, -1);
472
        g_return_val_if_fail(file_list != NULL, -1);
473

    
474
        if (dest->last_num < 0) {
475
                mh_scan_folder(folder, dest);
476
                if (dest->last_num < 0) return -1;
477
        }
478

    
479
        S_LOCK(mh);
480

    
481
        if (!dest->opened) {
482
                if ((fp = procmsg_open_mark_file(dest, DATA_APPEND)) == NULL)
483
                        g_warning("mh_add_msgs: can't open mark file.");
484
        }
485

    
486
        for (cur = file_list; cur != NULL; cur = cur->next) {
487
                MsgFlags flags = {MSG_NEW|MSG_UNREAD, 0};
488

    
489
                fileinfo = (MsgFileInfo *)cur->data;
490
                if (fileinfo->flags)
491
                        flags = *fileinfo->flags;
492
                msginfo = procheader_parse_file(fileinfo->file, flags, 0);
493
                if (!msginfo) {
494
                        if (fp) fclose(fp);
495
                        S_UNLOCK(mh);
496
                        return -1;
497
                }
498

    
499
                destfile = mh_get_new_msg_filename(dest);
500
                if (destfile == NULL) {
501
                        S_UNLOCK(mh);
502
                        return -1;
503
                }
504
                if (first_ == 0 || first_ > dest->last_num + 1)
505
                        first_ = dest->last_num + 1;
506

    
507
                if (syl_link(fileinfo->file, destfile) < 0) {
508
                        if (copy_file(fileinfo->file, destfile, TRUE) < 0) {
509
                                g_warning(_("can't copy message %s to %s\n"),
510
                                          fileinfo->file, destfile);
511
                                g_free(destfile);
512
                                if (fp) fclose(fp);
513
                                S_UNLOCK(mh);
514
                                return -1;
515
                        }
516
                }
517

    
518
                if (syl_app_get())
519
                        g_signal_emit_by_name(syl_app_get(), "add-msg", dest, destfile, dest->last_num + 1);
520

    
521
                g_free(destfile);
522
                dest->last_num++;
523
                dest->total++;
524
                dest->updated = TRUE;
525
                dest->mtime = 0;
526

    
527
                if (MSG_IS_RECEIVED(flags)) {
528
                        /* resets new flags of existing messages on
529
                           received mode */
530
                        if (dest->unmarked_num == 0)
531
                                dest->new = 0;
532
                        dest->unmarked_num++;
533
                        procmsg_add_mark_queue(dest, dest->last_num, flags);
534
                } else {
535
                        SET_DEST_MSG_FLAGS(fp, dest, dest->last_num, flags);
536
                }
537
                procmsg_add_cache_queue(dest, dest->last_num, msginfo);
538
                if (MSG_IS_NEW(flags))
539
                        dest->new++;
540
                if (MSG_IS_UNREAD(flags))
541
                        dest->unread++;
542
        }
543

    
544
        if (fp)
545
                fclose(fp);
546

    
547
        if (first)
548
                *first = first_;
549

    
550
        if (remove_source) {
551
                for (cur = file_list; cur != NULL; cur = cur->next) {
552
                        fileinfo = (MsgFileInfo *)cur->data;
553
                        if (g_unlink(fileinfo->file) < 0)
554
                                FILE_OP_ERROR(fileinfo->file, "unlink");
555
                }
556
        }
557

    
558
        S_UNLOCK(mh);
559
        return dest->last_num;
560
}
561

    
562

    
563
static gint mh_add_msg_msginfo(Folder *folder, FolderItem *dest,
564
                               MsgInfo *msginfo, gboolean remove_source)
565
{
566
        GSList msglist;
567

    
568
        g_return_val_if_fail(msginfo != NULL, -1);
569

    
570
        msglist.data = msginfo;
571
        msglist.next = NULL;
572

    
573
        return mh_add_msgs_msginfo(folder, dest, &msglist, remove_source, NULL);
574
}
575

    
576
static gint mh_add_msgs_msginfo(Folder *folder, FolderItem *dest,
577
                                GSList *msglist, gboolean remove_source,
578
                                gint *first)
579
{
580
        GSList *cur;
581
        MsgInfo *msginfo;
582
        gchar *srcfile;
583
        gchar *destfile;
584
        gint first_ = 0;
585
        FILE *fp = NULL;
586

    
587
        g_return_val_if_fail(dest != NULL, -1);
588
        g_return_val_if_fail(msglist != NULL, -1);
589

    
590
        if (dest->last_num < 0) {
591
                mh_scan_folder(folder, dest);
592
                if (dest->last_num < 0) return -1;
593
        }
594

    
595
        S_LOCK(mh);
596

    
597
        if (!dest->opened) {
598
                if ((fp = procmsg_open_mark_file(dest, DATA_APPEND)) == NULL)
599
                        g_warning("mh_add_msgs_msginfo: can't open mark file.");
600
        }
601

    
602
        for (cur = msglist; cur != NULL; cur = cur->next) {
603
                msginfo = (MsgInfo *)cur->data;
604

    
605
                destfile = mh_get_new_msg_filename(dest);
606
                if (!destfile) {
607
                        if (fp) fclose(fp);
608
                        S_UNLOCK(mh);
609
                        return -1;
610
                }
611
                if (first_ == 0 || first_ > dest->last_num + 1)
612
                        first_ = dest->last_num + 1;
613

    
614
                srcfile = procmsg_get_message_file(msginfo);
615
                if (!srcfile) {
616
                        if (fp) fclose(fp);
617
                        g_free(destfile);
618
                        S_UNLOCK(mh);
619
                        return -1;
620
                }
621
                if (syl_link(srcfile, destfile) < 0) {
622
                        if (copy_file(srcfile, destfile, TRUE) < 0) {
623
                                g_warning("mh_add_msgs_msginfo: can't copy message %s to %s", srcfile, destfile);
624
                                g_free(srcfile);
625
                                g_free(destfile);
626
                                if (fp) fclose(fp);
627
                                S_UNLOCK(mh);
628
                                return -1;
629
                        }
630
                }
631

    
632
                if (syl_app_get())
633
                        g_signal_emit_by_name(syl_app_get(), "add-msg", dest, destfile, dest->last_num + 1);
634

    
635
                g_free(srcfile);
636
                g_free(destfile);
637
                dest->last_num++;
638
                dest->total++;
639
                dest->updated = TRUE;
640
                dest->mtime = 0;
641

    
642
                if (MSG_IS_RECEIVED(msginfo->flags)) {
643
                        /* resets new flags of existing messages on
644
                           received mode */
645
                        if (dest->unmarked_num == 0)
646
                                dest->new = 0;
647
                        dest->unmarked_num++;
648
                        procmsg_add_mark_queue(dest, dest->last_num,
649
                                               msginfo->flags);
650
                } else {
651
                        SET_DEST_MSG_FLAGS(fp, dest, dest->last_num,
652
                                           msginfo->flags);
653
                }
654
                procmsg_add_cache_queue(dest, dest->last_num, msginfo);
655
                if (MSG_IS_NEW(msginfo->flags))
656
                        dest->new++;
657
                if (MSG_IS_UNREAD(msginfo->flags))
658
                        dest->unread++;
659
        }
660

    
661
        if (fp)
662
                fclose(fp);
663

    
664
        if (first)
665
                *first = first_;
666

    
667
        if (remove_source) {
668
                for (cur = msglist; cur != NULL; cur = cur->next) {
669
                        msginfo = (MsgInfo *)cur->data;
670
                        srcfile = procmsg_get_message_file(msginfo);
671
                        if (g_unlink(srcfile) < 0)
672
                                FILE_OP_ERROR(srcfile, "unlink");
673
                        g_free(srcfile);
674
                }
675
        }
676

    
677
        S_UNLOCK(mh);
678
        return dest->last_num;
679
}
680

    
681

    
682
static gint mh_do_move_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
683
{
684
        FolderItem *src;
685
        gchar *srcfile;
686
        gchar *destfile;
687
        GSList *cur;
688
        MsgInfo *msginfo;
689

    
690
        g_return_val_if_fail(dest != NULL, -1);
691
        g_return_val_if_fail(msglist != NULL, -1);
692

    
693
        if (dest->last_num < 0) {
694
                mh_scan_folder(folder, dest);
695
                if (dest->last_num < 0) return -1;
696
        }
697

    
698
        S_LOCK(mh);
699

    
700
        for (cur = msglist; cur != NULL; cur = cur->next) {
701
                msginfo = (MsgInfo *)cur->data;
702
                src = msginfo->folder;
703

    
704
                if (src == dest) {
705
                        g_warning(_("the src folder is identical to the dest.\n"));
706
                        continue;
707
                }
708
                debug_print("Moving message %s/%d to %s ...\n",
709
                            src->path, msginfo->msgnum, dest->path);
710

    
711
                destfile = mh_get_new_msg_filename(dest);
712
                if (!destfile) break;
713
                srcfile = procmsg_get_message_file(msginfo);
714

    
715
                /* g_signal_emit_by_name(syl_app_get(), "remove-msg", src, srcfile, msginfo->msgnum); */
716

    
717
                if (move_file(srcfile, destfile, FALSE) < 0) {
718
                        g_free(srcfile);
719
                        g_free(destfile);
720
                        break;
721
                }
722

    
723
                if (syl_app_get()) {
724
                        g_signal_emit_by_name(syl_app_get(), "add-msg", dest, destfile, dest->last_num + 1);
725
                        g_signal_emit_by_name(syl_app_get(), "remove-msg", src, srcfile, msginfo->msgnum);
726
                }
727

    
728
                g_free(srcfile);
729
                g_free(destfile);
730
                src->total--;
731
                src->updated = TRUE;
732
                src->mtime = 0;
733
                dest->last_num++;
734
                dest->total++;
735
                dest->updated = TRUE;
736
                dest->mtime = 0;
737

    
738
                SET_DEST_MSG_FLAGS(NULL, dest, dest->last_num, msginfo->flags);
739
                procmsg_add_cache_queue(dest, dest->last_num, msginfo);
740

    
741
                if (MSG_IS_NEW(msginfo->flags)) {
742
                        src->new--;
743
                        dest->new++;
744
                }
745
                if (MSG_IS_UNREAD(msginfo->flags)) {
746
                        src->unread--;
747
                        dest->unread++;
748
                }
749

    
750
                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID);
751
        }
752

    
753
        if (!dest->opened) {
754
                procmsg_flush_mark_queue(dest, NULL);
755
                procmsg_flush_cache_queue(dest, NULL);
756
        }
757

    
758
        S_UNLOCK(mh);
759
        return dest->last_num;
760
}
761

    
762
static gint mh_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
763
{
764
        GSList msglist;
765

    
766
        g_return_val_if_fail(msginfo != NULL, -1);
767

    
768
        msglist.data = msginfo;
769
        msglist.next = NULL;
770

    
771
        return mh_move_msgs(folder, dest, &msglist);
772
}
773

    
774
static gint mh_move_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
775
{
776
        MsgInfo *msginfo;
777
        gint ret = 0;
778
        gint first;
779

    
780
        msginfo = (MsgInfo *)msglist->data;
781
        if (folder == msginfo->folder->folder)
782
                return mh_do_move_msgs(folder, dest, msglist);
783

    
784
        ret = mh_add_msgs_msginfo(folder, dest, msglist, FALSE, &first);
785

    
786
        if (ret != -1)
787
                ret = folder_item_remove_msgs(msginfo->folder, msglist);
788

    
789
        return ret;
790
}
791

    
792
static gint mh_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
793
{
794
        GSList msglist;
795

    
796
        g_return_val_if_fail(msginfo != NULL, -1);
797

    
798
        msglist.data = msginfo;
799
        msglist.next = NULL;
800

    
801
        return mh_copy_msgs(folder, dest, &msglist);
802
}
803

    
804
static gint mh_copy_msgs(Folder *folder, FolderItem *dest, GSList *msglist)
805
{
806
        gchar *srcfile;
807
        gchar *destfile;
808
        GSList *cur;
809
        MsgInfo *msginfo;
810

    
811
        g_return_val_if_fail(dest != NULL, -1);
812
        g_return_val_if_fail(msglist != NULL, -1);
813

    
814
        if (dest->last_num < 0) {
815
                mh_scan_folder(folder, dest);
816
                if (dest->last_num < 0) return -1;
817
        }
818

    
819
        S_LOCK(mh);
820

    
821
        for (cur = msglist; cur != NULL; cur = cur->next) {
822
                msginfo = (MsgInfo *)cur->data;
823

    
824
                if (msginfo->folder == dest) {
825
                        g_warning(_("the src folder is identical to the dest.\n"));
826
                        continue;
827
                }
828
                debug_print(_("Copying message %s/%d to %s ...\n"),
829
                            msginfo->folder->path, msginfo->msgnum, dest->path);
830

    
831
                destfile = mh_get_new_msg_filename(dest);
832
                if (!destfile) break;
833
                srcfile = procmsg_get_message_file(msginfo);
834

    
835
                if (copy_file(srcfile, destfile, TRUE) < 0) {
836
                        FILE_OP_ERROR(srcfile, "copy");
837
                        g_free(srcfile);
838
                        g_free(destfile);
839
                        break;
840
                }
841

    
842
                if (syl_app_get())
843
                        g_signal_emit_by_name(syl_app_get(), "add-msg", dest, destfile, dest->last_num + 1);
844

    
845
                g_free(srcfile);
846
                g_free(destfile);
847
                dest->last_num++;
848
                dest->total++;
849
                dest->updated = TRUE;
850
                dest->mtime = 0;
851

    
852
                SET_DEST_MSG_FLAGS(NULL, dest, dest->last_num, msginfo->flags);
853
                procmsg_add_cache_queue(dest, dest->last_num, msginfo);
854

    
855
                if (MSG_IS_NEW(msginfo->flags))
856
                        dest->new++;
857
                if (MSG_IS_UNREAD(msginfo->flags))
858
                        dest->unread++;
859
        }
860

    
861
        if (!dest->opened) {
862
                procmsg_flush_mark_queue(dest, NULL);
863
                procmsg_flush_cache_queue(dest, NULL);
864
        }
865

    
866
        S_UNLOCK(mh);
867
        return dest->last_num;
868
}
869

    
870
static gint mh_remove_msg(Folder *folder, FolderItem *item, MsgInfo *msginfo)
871
{
872
        gchar *file;
873

    
874
        g_return_val_if_fail(item != NULL, -1);
875

    
876
        file = mh_fetch_msg(folder, item, msginfo->msgnum);
877
        g_return_val_if_fail(file != NULL, -1);
878

    
879
        if (syl_app_get())
880
                g_signal_emit_by_name(syl_app_get(), "remove-msg", item, file, msginfo->msgnum);
881

    
882
        S_LOCK(mh);
883

    
884
        if (g_unlink(file) < 0) {
885
                FILE_OP_ERROR(file, "unlink");
886
                g_free(file);
887
                S_UNLOCK(mh);
888
                return -1;
889
        }
890
        g_free(file);
891

    
892
        item->total--;
893
        item->updated = TRUE;
894
        item->mtime = 0;
895
        if (MSG_IS_NEW(msginfo->flags))
896
                item->new--;
897
        if (MSG_IS_UNREAD(msginfo->flags))
898
                item->unread--;
899
        MSG_SET_TMP_FLAGS(msginfo->flags, MSG_INVALID);
900

    
901
        S_UNLOCK(mh);
902

    
903
        if (msginfo->msgnum == item->last_num)
904
                mh_scan_folder_full(folder, item, FALSE);
905

    
906
        return 0;
907
}
908

    
909
static gint mh_remove_all_msg(Folder *folder, FolderItem *item)
910
{
911
        gchar *path;
912
        gint val;
913

    
914
        g_return_val_if_fail(item != NULL, -1);
915

    
916
        path = folder_item_get_path(item);
917
        g_return_val_if_fail(path != NULL, -1);
918
        if (syl_app_get())
919
                g_signal_emit_by_name(syl_app_get(), "remove-all-msg", item);
920

    
921
        S_LOCK(mh);
922

    
923
        val = remove_all_numbered_files(path);
924
        g_free(path);
925
        if (val == 0) {
926
                item->new = item->unread = item->total = 0;
927
                item->last_num = 0;
928
                item->updated = TRUE;
929
                item->mtime = 0;
930
        }
931

    
932
        S_UNLOCK(mh);
933

    
934
        return val;
935
}
936

    
937
static gboolean mh_is_msg_changed(Folder *folder, FolderItem *item,
938
                                  MsgInfo *msginfo)
939
{
940
        struct stat s;
941
        gchar buf[16];
942

    
943
        if (g_stat(utos_buf(buf, msginfo->msgnum), &s) < 0 ||
944
            msginfo->size  != s.st_size ||
945
            msginfo->mtime != s.st_mtime)
946
                return TRUE;
947

    
948
        return FALSE;
949
}
950

    
951
static gint mh_close(Folder *folder, FolderItem *item)
952
{
953
        return 0;
954
}
955

    
956
#ifdef G_OS_WIN32
957
struct wfddata {
958
        WIN32_FIND_DATAA wfda;
959
        WIN32_FIND_DATAW wfdw;
960
        DWORD file_attr;
961
        gchar *file_name;
962
};
963

    
964
static HANDLE find_first_file(const gchar *path, struct wfddata *wfd)
965
{
966
        HANDLE hfind;
967

    
968
        if (G_WIN32_HAVE_WIDECHAR_API()) {
969
                if (path) {
970
                        gchar *wildcard_path;
971
                        wchar_t *wpath;
972

    
973
                        wildcard_path = g_strconcat(path, "\\*", NULL);
974
                        wpath = g_utf8_to_utf16(wildcard_path, -1,
975
                                                NULL, NULL, NULL);
976
                        if (wpath) {
977
                                hfind = FindFirstFileW(wpath, &wfd->wfdw);
978
                                g_free(wpath);
979
                        } else
980
                                hfind = INVALID_HANDLE_VALUE;
981
                        g_free(wildcard_path);
982
                } else
983
                        hfind = FindFirstFileW(L"*", &wfd->wfdw);
984

    
985
                if (hfind != INVALID_HANDLE_VALUE) {
986
                        wfd->file_attr = wfd->wfdw.dwFileAttributes;
987
                        wfd->file_name = g_utf16_to_utf8(wfd->wfdw.cFileName, -1,
988
                                                         NULL, NULL, NULL);
989
                }
990
        } else {
991
                if (path) {
992
                        gchar *wildcard_path;
993
                        gchar *cp_path;
994

    
995
                        wildcard_path = g_strconcat(path, "\\*", NULL);
996
                        cp_path = g_locale_from_utf8(wildcard_path, -1,
997
                                                     NULL, NULL, NULL);
998
                        if (cp_path) {
999
                                hfind = FindFirstFileA(cp_path, &wfd->wfda);
1000
                                g_free(cp_path);
1001
                        } else
1002
                                hfind = INVALID_HANDLE_VALUE;
1003
                        g_free(wildcard_path);
1004
                } else
1005
                        hfind = FindFirstFileA("*", &wfd->wfda);
1006

    
1007
                if (hfind != INVALID_HANDLE_VALUE) {
1008
                        wfd->file_attr = wfd->wfda.dwFileAttributes;
1009
                        wfd->file_name = g_locale_to_utf8(wfd->wfda.cFileName,
1010
                                                          -1, NULL, NULL, NULL);
1011
                }
1012
        }
1013

    
1014
        return hfind;
1015
}
1016

    
1017
static BOOL find_next_file(HANDLE hfind, struct wfddata *wfd)
1018
{
1019
        BOOL retval;
1020

    
1021
        if (G_WIN32_HAVE_WIDECHAR_API()) {
1022
                retval = FindNextFileW(hfind, &wfd->wfdw);
1023
                if (retval) {
1024
                        wfd->file_attr = wfd->wfdw.dwFileAttributes;
1025
                        wfd->file_name = g_utf16_to_utf8(wfd->wfdw.cFileName, -1,
1026
                                                         NULL, NULL, NULL);
1027
                }
1028
        } else {
1029
                retval = FindNextFileA(hfind, &wfd->wfda);
1030
                if (retval) {
1031
                        wfd->file_attr = wfd->wfda.dwFileAttributes;
1032
                        wfd->file_name = g_locale_to_utf8(wfd->wfda.cFileName,
1033
                                                          -1, NULL, NULL, NULL);
1034
                }
1035
        }
1036

    
1037
        return retval;
1038
}
1039
#endif
1040

    
1041
static gint mh_scan_folder_full(Folder *folder, FolderItem *item,
1042
                                gboolean count_sum)
1043
{
1044
        gchar *path;
1045
#ifdef G_OS_WIN32
1046
        struct wfddata wfd;
1047
        HANDLE hfind;
1048
#else
1049
        DIR *dp;
1050
        struct dirent *d;
1051
#endif
1052
        gint max = 0;
1053
        gint num;
1054
        gint n_msg = 0;
1055

    
1056
        g_return_val_if_fail(item != NULL, -1);
1057

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

    
1060
        S_LOCK(mh);
1061

    
1062
        path = folder_item_get_path(item);
1063
        if (!path) {
1064
                S_UNLOCK(mh);
1065
                return -1;
1066
        }
1067
        if (change_dir(path) < 0) {
1068
                g_free(path);
1069
                S_UNLOCK(mh);
1070
                return -1;
1071
        }
1072
        g_free(path);
1073

    
1074
#ifdef G_OS_WIN32
1075
        if ((hfind = find_first_file(NULL, &wfd)) == INVALID_HANDLE_VALUE) {
1076
                g_warning("failed to open directory\n");
1077
#else
1078
        if ((dp = opendir(".")) == NULL) {
1079
                FILE_OP_ERROR(item->path, "opendir");
1080
#endif
1081
                S_UNLOCK(mh);
1082
                return -1;
1083
        }
1084

    
1085
        if (folder->ui_func)
1086
                folder->ui_func(folder, item, folder->ui_func_data);
1087

    
1088
#ifdef G_OS_WIN32
1089
        do {
1090
                if ((wfd.file_attr &
1091
                     (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0) {
1092
                        if (wfd.file_name) {
1093
                                if ((num = to_number(wfd.file_name)) > 0) {
1094
                                        n_msg++;
1095
                                        if (max < num)
1096
                                                max = num;
1097
                                }
1098
                        }
1099
                }
1100

    
1101
                if (wfd.file_name) {
1102
                        g_free(wfd.file_name);
1103
                        wfd.file_name = NULL;
1104
                }
1105
        } while (find_next_file(hfind, &wfd));
1106

    
1107
        FindClose(hfind);
1108
#else
1109
        while ((d = readdir(dp)) != NULL) {
1110
                if ((num = to_number(d->d_name)) > 0 &&
1111
                    dirent_is_regular_file(d)) {
1112
                        n_msg++;
1113
                        if (max < num)
1114
                                max = num;
1115
                }
1116
        }
1117

    
1118
        closedir(dp);
1119
#endif
1120

    
1121
        if (n_msg == 0)
1122
                item->new = item->unread = item->total = 0;
1123
        else if (count_sum) {
1124
                gint new, unread, total, min, max_;
1125

    
1126
                procmsg_get_mark_sum
1127
                        (item, &new, &unread, &total, &min, &max_, 0);
1128

    
1129
                if (n_msg > total) {
1130
                        item->unmarked_num = new = n_msg - total;
1131
                        unread += n_msg - total;
1132
                } else
1133
                        item->unmarked_num = 0;
1134

    
1135
                item->new = new;
1136
                item->unread = unread;
1137
                item->total = n_msg;
1138

    
1139
                if (item->cache_queue && !item->opened) {
1140
                        procmsg_flush_cache_queue(item, NULL);
1141
                }
1142
        }
1143

    
1144
        item->updated = TRUE;
1145
        item->mtime = 0;
1146

    
1147
        debug_print("Last number in dir %s = %d\n", item->path, max);
1148
        item->last_num = max;
1149

    
1150
        S_UNLOCK(mh);
1151
        return 0;
1152
}
1153

    
1154
static gint mh_scan_folder(Folder *folder, FolderItem *item)
1155
{
1156
        return mh_scan_folder_full(folder, item, TRUE);
1157
}
1158

    
1159
static gint mh_scan_tree(Folder *folder)
1160
{
1161
        FolderItem *item;
1162
        gchar *rootpath;
1163

    
1164
        g_return_val_if_fail(folder != NULL, -1);
1165

    
1166
        S_LOCK(mh);
1167

    
1168
        if (!folder->node) {
1169
                item = folder_item_new(folder->name, NULL);
1170
                item->folder = folder;
1171
                folder->node = item->node = g_node_new(item);
1172
        } else
1173
                item = FOLDER_ITEM(folder->node->data);
1174

    
1175
        rootpath = folder_item_get_path(item);
1176
        if (change_dir(rootpath) < 0) {
1177
                g_free(rootpath);
1178
                S_UNLOCK(mh);
1179
                return -1;
1180
        }
1181
        g_free(rootpath);
1182

    
1183
        mh_create_tree(folder);
1184
        mh_remove_missing_folder_items(folder);
1185
        mh_scan_tree_recursive(item);
1186

    
1187
        S_UNLOCK(mh);
1188
        return 0;
1189
}
1190

    
1191
#define MAKE_DIR_IF_NOT_EXIST(dir) \
1192
{ \
1193
        if (!is_dir_exist(dir)) { \
1194
                if (is_file_exist(dir)) { \
1195
                        g_warning(_("File `%s' already exists.\n" \
1196
                                    "Can't create folder."), dir); \
1197
                        return -1; \
1198
                } \
1199
                if (make_dir(dir) < 0) \
1200
                        return -1; \
1201
        } \
1202
}
1203

    
1204
#define MAKE_DIR_HIER_IF_NOT_EXIST(dir) \
1205
{ \
1206
        if (!is_dir_exist(dir)) { \
1207
                if (is_file_exist(dir)) { \
1208
                        g_warning(_("File `%s' already exists.\n" \
1209
                                    "Can't create folder."), dir); \
1210
                        return -1; \
1211
                } \
1212
                if (make_dir_hier(dir) < 0) \
1213
                        return -1; \
1214
        } \
1215
}
1216

    
1217
static gint mh_create_tree(Folder *folder)
1218
{
1219
        gchar *rootpath;
1220

    
1221
        g_return_val_if_fail(folder != NULL, -1);
1222

    
1223
        CHDIR_RETURN_VAL_IF_FAIL(get_mail_base_dir(), -1);
1224
        rootpath = LOCAL_FOLDER(folder)->rootpath;
1225
        MAKE_DIR_HIER_IF_NOT_EXIST(rootpath);
1226
        CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
1227
        MAKE_DIR_IF_NOT_EXIST(INBOX_DIR);
1228
        MAKE_DIR_IF_NOT_EXIST(OUTBOX_DIR);
1229
        MAKE_DIR_IF_NOT_EXIST(QUEUE_DIR);
1230
        MAKE_DIR_IF_NOT_EXIST(DRAFT_DIR);
1231
        MAKE_DIR_IF_NOT_EXIST(TRASH_DIR);
1232
        MAKE_DIR_IF_NOT_EXIST(JUNK_DIR);
1233

    
1234
        return 0;
1235
}
1236

    
1237
#undef MAKE_DIR_IF_NOT_EXIST
1238
#undef MAKE_DIR_HIER_IF_NOT_EXIST
1239

    
1240
static FolderItem *mh_create_folder(Folder *folder, FolderItem *parent,
1241
                                    const gchar *name)
1242
{
1243
        gchar *path;
1244
        gchar *fs_name;
1245
        gchar *fullpath;
1246
        FolderItem *new_item;
1247

    
1248
        g_return_val_if_fail(folder != NULL, NULL);
1249
        g_return_val_if_fail(parent != NULL, NULL);
1250
        g_return_val_if_fail(name != NULL, NULL);
1251

    
1252
        S_LOCK(mh);
1253

    
1254
        path = folder_item_get_path(parent);
1255
        fs_name = g_filename_from_utf8(name, -1, NULL, NULL, NULL);
1256
        fullpath = g_strconcat(path, G_DIR_SEPARATOR_S,
1257
                               fs_name ? fs_name : name, NULL);
1258
        g_free(fs_name);
1259
        g_free(path);
1260

    
1261
        if (make_dir_hier(fullpath) < 0) {
1262
                g_free(fullpath);
1263
                S_UNLOCK(mh);
1264
                return NULL;
1265
        }
1266

    
1267
        g_free(fullpath);
1268

    
1269
        /* path is a logical folder path */
1270
        if (parent->path)
1271
                path = g_strconcat(parent->path, "/", name, NULL);
1272
        else
1273
                path = g_strdup(name);
1274
        new_item = folder_item_new(name, path);
1275
        folder_item_append(parent, new_item);
1276
        g_free(path);
1277

    
1278
        S_UNLOCK(mh);
1279
        return new_item;
1280
}
1281

    
1282
static gint mh_move_folder_real(Folder *folder, FolderItem *item,
1283
                                FolderItem *new_parent, const gchar *name)
1284
{
1285
        gchar *rootpath;
1286
        gchar *oldpath;
1287
        gchar *newpath;
1288
        gchar *dirname;
1289
        gchar *new_dir;
1290
        gchar *name_;
1291
        gchar *utf8_name;
1292
        gchar *paths[2];
1293
        gchar *old_id, *new_id;
1294

    
1295
        g_return_val_if_fail(folder != NULL, -1);
1296
        g_return_val_if_fail(item != NULL, -1);
1297
        g_return_val_if_fail(folder == item->folder, -1);
1298
        g_return_val_if_fail(item->path != NULL, -1);
1299
        g_return_val_if_fail(new_parent != NULL || name != NULL, -1);
1300
        if (new_parent) {
1301
                g_return_val_if_fail(item != new_parent, -1);
1302
                g_return_val_if_fail(item->parent != new_parent, -1);
1303
                g_return_val_if_fail(item->folder == new_parent->folder, -1);
1304
                if (g_node_is_ancestor(item->node, new_parent->node)) {
1305
                        g_warning("folder to be moved is ancestor of new parent\n");
1306
                        return -1;
1307
                }
1308
        }
1309

    
1310
        S_LOCK(mh);
1311

    
1312
        oldpath = folder_item_get_path(item);
1313
        if (new_parent) {
1314
                if (name) {
1315
                        name_ = g_filename_from_utf8(name, -1, NULL, NULL,
1316
                                                     NULL);
1317
                        if (!name_)
1318
                                name_ = g_strdup(name);
1319
                        utf8_name = g_strdup(name);
1320
                } else {
1321
                        name_ = g_path_get_basename(oldpath);
1322
                        utf8_name = g_filename_to_utf8(name_, -1, NULL, NULL,
1323
                                                       NULL);
1324
                        if (!utf8_name)
1325
                                utf8_name = g_strdup(name_);
1326
                }
1327
                new_dir = folder_item_get_path(new_parent);
1328
                newpath = g_strconcat(new_dir, G_DIR_SEPARATOR_S, name_, NULL);
1329
                g_free(new_dir);
1330
        } else {
1331
                name_ = g_filename_from_utf8(name, -1, NULL, NULL, NULL);
1332
                utf8_name = g_strdup(name);
1333
                dirname = g_dirname(oldpath);
1334
                newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S,
1335
                                      name_ ? name_ : name, NULL);
1336
                g_free(dirname);
1337
        }
1338
        g_free(name_);
1339

    
1340
        if (is_file_entry_exist(newpath)) {
1341
                g_warning("%s already exists\n", newpath);
1342
                g_free(oldpath);
1343
                g_free(newpath);
1344
                g_free(utf8_name);
1345
                S_UNLOCK(mh);
1346
                return -1;
1347
        }
1348

    
1349
        rootpath = folder_get_path(folder);
1350
        if (change_dir(rootpath) < 0) {
1351
                g_free(rootpath);
1352
                g_free(oldpath);
1353
                g_free(newpath);
1354
                g_free(utf8_name);
1355
                S_UNLOCK(mh);
1356
                return -1;
1357
        }
1358
        g_free(rootpath);
1359

    
1360
        debug_print("mh_move_folder: rename(%s, %s)\n", oldpath, newpath);
1361

    
1362
        if (g_rename(oldpath, newpath) < 0) {
1363
                FILE_OP_ERROR(oldpath, "rename");
1364
                g_free(oldpath);
1365
                g_free(newpath);
1366
                g_free(utf8_name);
1367
                S_UNLOCK(mh);
1368
                return -1;
1369
        }
1370

    
1371
        g_free(oldpath);
1372
        g_free(newpath);
1373

    
1374
        old_id = folder_item_get_identifier(item);
1375

    
1376
        if (new_parent) {
1377
                g_node_unlink(item->node);
1378
                g_node_append(new_parent->node, item->node);
1379
                item->parent = new_parent;
1380
                if (new_parent->path != NULL) {
1381
                        newpath = g_strconcat(new_parent->path, "/", utf8_name,
1382
                                              NULL);
1383
                        g_free(utf8_name);
1384
                } else
1385
                        newpath = utf8_name;
1386
        } else {
1387
                if (strchr(item->path, '/') != NULL) {
1388
                        dirname = g_dirname(item->path);
1389
                        newpath = g_strconcat(dirname, "/", utf8_name, NULL);
1390
                        g_free(dirname);
1391
                        g_free(utf8_name);
1392
                } else
1393
                        newpath = utf8_name;
1394
        }
1395

    
1396
        if (name) {
1397
                g_free(item->name);
1398
                item->name = g_strdup(name);
1399
        }
1400

    
1401
        paths[0] = g_strdup(item->path);
1402
        paths[1] = newpath;
1403
        g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1404
                        mh_rename_folder_func, paths);
1405

    
1406
        g_free(paths[0]);
1407
        g_free(paths[1]);
1408

    
1409
        new_id = folder_item_get_identifier(item);
1410
        if (syl_app_get())
1411
                g_signal_emit_by_name(syl_app_get(), "move-folder", item,
1412
                                      old_id, new_id);
1413
        g_free(new_id);
1414
        g_free(old_id);
1415

    
1416
        S_UNLOCK(mh);
1417
        return 0;
1418
}
1419

    
1420
static gint mh_move_folder(Folder *folder, FolderItem *item,
1421
                           FolderItem *new_parent)
1422
{
1423
        return mh_move_folder_real(folder, item, new_parent, NULL);
1424
}
1425

    
1426
static gint mh_rename_folder(Folder *folder, FolderItem *item,
1427
                             const gchar *name)
1428
{
1429
        return mh_move_folder_real(folder, item, NULL, name);
1430
}
1431

    
1432
static gint mh_remove_folder(Folder *folder, FolderItem *item)
1433
{
1434
        gchar *path;
1435

    
1436
        g_return_val_if_fail(folder != NULL, -1);
1437
        g_return_val_if_fail(item != NULL, -1);
1438
        g_return_val_if_fail(item->path != NULL, -1);
1439

    
1440
        S_LOCK(mh);
1441

    
1442
        path = folder_item_get_path(item);
1443
        if (remove_dir_recursive(path) < 0) {
1444
                g_warning("can't remove directory `%s'\n", path);
1445
                g_free(path);
1446
                S_UNLOCK(mh);
1447
                return -1;
1448
        }
1449

    
1450
        g_free(path);
1451
        if (syl_app_get())
1452
                g_signal_emit_by_name(syl_app_get(), "remove-folder", item);
1453
        folder_item_remove(item);
1454

    
1455
        S_UNLOCK(mh);
1456
        return 0;
1457
}
1458

    
1459

    
1460
static time_t mh_get_mtime(FolderItem *item)
1461
{
1462
        gchar *path;
1463
        struct stat s;
1464

    
1465
        path = folder_item_get_path(item);
1466
        if (g_stat(path, &s) < 0) {
1467
                FILE_OP_ERROR(path, "stat");
1468
                g_free(path);
1469
                return -1;
1470
        } else {
1471
                g_free(path);
1472
                return MAX(s.st_mtime, s.st_ctime);
1473
        }
1474
}
1475

    
1476
static GSList *mh_get_uncached_msgs(GHashTable *msg_table, FolderItem *item)
1477
{
1478
        gchar *path;
1479
        GDir *dp;
1480
        const gchar *dir_name;
1481
        GSList *newlist = NULL;
1482
        GSList *last = NULL;
1483
        MsgInfo *msginfo;
1484
        gint n_newmsg = 0;
1485
        gint num;
1486
        Folder *folder;
1487

    
1488
        g_return_val_if_fail(item != NULL, NULL);
1489
        g_return_val_if_fail(item->folder != NULL, NULL);
1490

    
1491
        folder = item->folder;
1492

    
1493
        path = folder_item_get_path(item);
1494
        g_return_val_if_fail(path != NULL, NULL);
1495
        if (change_dir(path) < 0) {
1496
                g_free(path);
1497
                return NULL;
1498
        }
1499
        g_free(path);
1500

    
1501
        if ((dp = g_dir_open(".", 0, NULL)) == NULL) {
1502
                FILE_OP_ERROR(item->path, "opendir");
1503
                return NULL;
1504
        }
1505

    
1506
        debug_print("Searching uncached messages...\n");
1507

    
1508
        if (msg_table) {
1509
                gint count = 0;
1510

    
1511
                while ((dir_name = g_dir_read_name(dp)) != NULL) {
1512
                        if ((num = to_number(dir_name)) <= 0) continue;
1513

    
1514
                        msginfo = g_hash_table_lookup
1515
                                (msg_table, GUINT_TO_POINTER(num));
1516

    
1517
                        if (msginfo) {
1518
                                MSG_SET_TMP_FLAGS(msginfo->flags, MSG_CACHED);
1519
                        } else {
1520
                                /* not found in the cache (uncached message) */
1521
                                msginfo = mh_parse_msg(dir_name, item);
1522
                                if (!msginfo) continue;
1523

    
1524
                                if (!newlist)
1525
                                        last = newlist =
1526
                                                g_slist_append(NULL, msginfo);
1527
                                else {
1528
                                        last = g_slist_append(last, msginfo);
1529
                                        last = last->next;
1530
                                }
1531
                                n_newmsg++;
1532
                        }
1533

    
1534
                        count++;
1535
                        if (folder->ui_func)
1536
                                folder->ui_func(folder, item, folder->ui_func_data ? folder->ui_func_data : GINT_TO_POINTER(count));
1537
                }
1538
        } else {
1539
                /* discard all previous cache */
1540
                while ((dir_name = g_dir_read_name(dp)) != NULL) {
1541
                        if (to_number(dir_name) <= 0) continue;
1542

    
1543
                        msginfo = mh_parse_msg(dir_name, item);
1544
                        if (!msginfo) continue;
1545

    
1546
                        if (!newlist)
1547
                                last = newlist = g_slist_append(NULL, msginfo);
1548
                        else {
1549
                                last = g_slist_append(last, msginfo);
1550
                                last = last->next;
1551
                        }
1552
                        n_newmsg++;
1553
                        if (folder->ui_func)
1554
                                folder->ui_func(folder, item, folder->ui_func_data ? folder->ui_func_data : GINT_TO_POINTER(n_newmsg));
1555
                }
1556
        }
1557

    
1558
        g_dir_close(dp);
1559

    
1560
        if (n_newmsg)
1561
                debug_print("%d uncached message(s) found.\n", n_newmsg);
1562
        else
1563
                debug_print("done.\n");
1564

    
1565
        /* sort new messages in numerical order */
1566
        if (newlist && item->sort_key == SORT_BY_NONE) {
1567
                debug_print("Sorting uncached messages in numerical order...\n");
1568
                newlist = g_slist_sort
1569
                        (newlist, (GCompareFunc)procmsg_cmp_msgnum_for_sort);
1570
                debug_print("done.\n");
1571
        }
1572

    
1573
        return newlist;
1574
}
1575

    
1576
static MsgInfo *mh_parse_msg(const gchar *file, FolderItem *item)
1577
{
1578
        MsgInfo *msginfo;
1579
        MsgFlags flags;
1580

    
1581
        g_return_val_if_fail(item != NULL, NULL);
1582
        g_return_val_if_fail(file != NULL, NULL);
1583

    
1584
        flags.perm_flags = MSG_NEW|MSG_UNREAD;
1585
        flags.tmp_flags = 0;
1586

    
1587
        if (item->stype == F_QUEUE) {
1588
                MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
1589
        } else if (item->stype == F_DRAFT) {
1590
                MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
1591
        }
1592

    
1593
        msginfo = procheader_parse_file(file, flags, FALSE);
1594
        if (!msginfo) return NULL;
1595

    
1596
        msginfo->msgnum = atoi(file);
1597
        msginfo->folder = item;
1598

    
1599
        return msginfo;
1600
}
1601

    
1602
#if 0
1603
static gboolean mh_is_maildir_one(const gchar *path, const gchar *dir)
1604
{
1605
        gchar *entry;
1606
        gboolean result;
1607

1608
        entry = g_strconcat(path, G_DIR_SEPARATOR_S, dir, NULL);
1609
        result = is_dir_exist(entry);
1610
        g_free(entry);
1611

1612
        return result;
1613
}
1614

1615
/*
1616
 * check whether PATH is a Maildir style mailbox.
1617
 * This is the case if the 3 subdir: new, cur, tmp are existing.
1618
 * This functon assumes that entry is an directory
1619
 */
1620
static gboolean mh_is_maildir(const gchar *path)
1621
{
1622
        return mh_is_maildir_one(path, "new") &&
1623
               mh_is_maildir_one(path, "cur") &&
1624
               mh_is_maildir_one(path, "tmp");
1625
}
1626
#endif
1627

    
1628
static gboolean mh_remove_missing_folder_items_func(GNode *node, gpointer data)
1629
{
1630
        FolderItem *item;
1631
        gchar *path;
1632

    
1633
        g_return_val_if_fail(node->data != NULL, FALSE);
1634

    
1635
        if (G_NODE_IS_ROOT(node))
1636
                return FALSE;
1637

    
1638
        item = FOLDER_ITEM(node->data);
1639

    
1640
#if 0
1641
        if (item->path && strchr(item->path, '/')) {
1642
                debug_print("folder '%s' includes Unix path separator. removing...\n", item->path);
1643
                folder_item_remove(item);
1644
                return FALSE;
1645
        }
1646
#endif
1647

    
1648
        path = folder_item_get_path(item);
1649
        if (!is_dir_exist(path)) {
1650
                debug_print("folder '%s' not found. removing...\n", path);
1651
                folder_item_remove(item);
1652
        }
1653
        g_free(path);
1654

    
1655
        return FALSE;
1656
}
1657

    
1658
static void mh_remove_missing_folder_items(Folder *folder)
1659
{
1660
        g_return_if_fail(folder != NULL);
1661

    
1662
        debug_print("searching missing folders...\n");
1663

    
1664
        g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
1665
                        mh_remove_missing_folder_items_func, folder);
1666
}
1667

    
1668
#define MAX_RECURSION_LEVEL        64
1669

    
1670
static void mh_scan_tree_recursive(FolderItem *item)
1671
{
1672
        Folder *folder;
1673
#ifdef G_OS_WIN32
1674
        struct wfddata wfd;
1675
        HANDLE hfind;
1676
#else
1677
        DIR *dp;
1678
        struct dirent *d;
1679
        struct stat s;
1680
#endif
1681
        const gchar *dir_name;
1682
        gchar *fs_path;
1683
        gchar *entry;
1684
        gchar *utf8entry;
1685
        gchar *utf8name;
1686
        gint n_msg = 0;
1687

    
1688
        g_return_if_fail(item != NULL);
1689
        g_return_if_fail(item->folder != NULL);
1690

    
1691
        if (item->stype == F_VIRTUAL)
1692
                return;
1693

    
1694
        folder = item->folder;
1695

    
1696
        if (g_node_depth(item->node) >= MAX_RECURSION_LEVEL) {
1697
                g_warning("mh_scan_tree_recursive(): max recursion level (%u) reached.", MAX_RECURSION_LEVEL);
1698
                return;
1699
        }
1700

    
1701
        debug_print("scanning %s ...\n",
1702
                    item->path ? item->path : LOCAL_FOLDER(folder)->rootpath);
1703
        if (folder->ui_func)
1704
                folder->ui_func(folder, item, folder->ui_func_data);
1705

    
1706
        fs_path = item->path ?
1707
                g_filename_from_utf8(item->path, -1, NULL, NULL, NULL)
1708
                : g_strdup(".");
1709
        if (!fs_path)
1710
                fs_path = g_strdup(item->path);
1711
#ifdef G_OS_WIN32
1712
        hfind = find_first_file(fs_path, &wfd);
1713
        if (hfind == INVALID_HANDLE_VALUE) {
1714
                g_warning("failed to open directory: %s\n", fs_path);
1715
                g_free(fs_path);
1716
                return;
1717
        }
1718
#else
1719
        dp = opendir(fs_path);
1720
        if (!dp) {
1721
                FILE_OP_ERROR(fs_path, "opendir");
1722
                g_free(fs_path);
1723
                return;
1724
        }
1725
#endif
1726
        g_free(fs_path);
1727

    
1728
#ifdef G_OS_WIN32
1729
        do {
1730
                if (!wfd.file_name) continue;
1731
                if (wfd.file_name[0] == '.') {
1732
                        g_free(wfd.file_name);
1733
                        wfd.file_name = NULL;
1734
                        continue;
1735
                }
1736
                dir_name = utf8name = wfd.file_name;
1737
                wfd.file_name = NULL;
1738
#else
1739
        while ((d = readdir(dp)) != NULL) {
1740
                dir_name = d->d_name;
1741
                if (dir_name[0] == '.') continue;
1742

    
1743
                utf8name = g_filename_to_utf8(dir_name, -1, NULL, NULL, NULL);
1744
                if (!utf8name)
1745
                        utf8name = g_strdup(dir_name);
1746
#endif
1747

    
1748
                if (item->path) {
1749
                        utf8entry = g_strconcat(item->path, "/", utf8name,
1750
                                                NULL);
1751
                } else
1752
                        utf8entry = g_strdup(utf8name);
1753
                entry = g_filename_from_utf8(utf8entry, -1, NULL, NULL, NULL);
1754
                if (!entry)
1755
                        entry = g_strdup(utf8entry);
1756
#ifdef G_OS_WIN32
1757
                subst_char(entry, '/', G_DIR_SEPARATOR);
1758
#endif
1759

    
1760
                if (
1761
#ifdef G_OS_WIN32
1762
                        (wfd.file_attr & FILE_ATTRIBUTE_DIRECTORY) != 0
1763
#else
1764
#if HAVE_DIRENT_D_TYPE
1765
                        d->d_type == DT_DIR ||
1766
                        ((d->d_type == DT_UNKNOWN || d->d_type == DT_LNK) &&
1767
#endif
1768
                        g_stat(entry, &s) == 0 && S_ISDIR(s.st_mode)
1769
#if HAVE_DIRENT_D_TYPE
1770
                        )
1771
#endif
1772
#endif /* G_OS_WIN32 */
1773
                   ) {
1774
                        FolderItem *new_item = NULL;
1775
                        GNode *node;
1776

    
1777
#ifndef G_OS_WIN32
1778
                        if (g_utf8_validate(utf8name, -1, NULL) == FALSE) {
1779
                                g_warning(_("Directory name\n"
1780
                                            "'%s' is not a valid UTF-8 string.\n"
1781
                                            "Maybe the locale encoding is used for filename.\n"
1782
                                            "If that is the case, you must set the following environmental variable\n"
1783
                                            "(see README for detail):\n"
1784
                                            "\n"
1785
                                            "\tG_FILENAME_ENCODING=@locale\n"),
1786
                                          utf8name);
1787
                                g_free(entry);
1788
                                g_free(utf8entry);
1789
                                g_free(utf8name);
1790
                                continue;
1791
                        }
1792
#endif /* G_OS_WIN32 */
1793

    
1794
                        node = item->node;
1795
                        for (node = node->children; node != NULL; node = node->next) {
1796
                                FolderItem *cur_item = FOLDER_ITEM(node->data);
1797
                                if (!strcmp2(cur_item->path, utf8entry)) {
1798
                                        new_item = cur_item;
1799
                                        break;
1800
                                }
1801
                        }
1802
                        if (!new_item) {
1803
                                debug_print("new folder '%s' found.\n",
1804
                                            utf8entry);
1805
                                new_item = folder_item_new(utf8name, utf8entry);
1806
                                folder_item_append(item, new_item);
1807
                        }
1808

    
1809
                        if (!item->path) {
1810
                                if (!folder->inbox &&
1811
                                    !strcmp(dir_name, INBOX_DIR)) {
1812
                                        new_item->stype = F_INBOX;
1813
                                        folder->inbox = new_item;
1814
                                } else if (!folder->outbox &&
1815
                                           !strcmp(dir_name, OUTBOX_DIR)) {
1816
                                        new_item->stype = F_OUTBOX;
1817
                                        folder->outbox = new_item;
1818
                                } else if (!folder->draft &&
1819
                                           !strcmp(dir_name, DRAFT_DIR)) {
1820
                                        new_item->stype = F_DRAFT;
1821
                                        folder->draft = new_item;
1822
                                } else if (!folder->queue &&
1823
                                           !strcmp(dir_name, QUEUE_DIR)) {
1824
                                        new_item->stype = F_QUEUE;
1825
                                        folder->queue = new_item;
1826
                                } else if (!folder->trash &&
1827
                                           !strcmp(dir_name, TRASH_DIR)) {
1828
                                        new_item->stype = F_TRASH;
1829
                                        folder->trash = new_item;
1830
                                } else if (!folder_get_junk(folder) &&
1831
                                           !strcmp(dir_name, JUNK_DIR)) {
1832
                                        new_item->stype = F_JUNK;
1833
                                        folder_set_junk(folder, new_item);
1834
                                }
1835
                        }
1836

    
1837
                        mh_scan_tree_recursive(new_item);
1838
                } else if (to_number(dir_name) > 0) n_msg++;
1839

    
1840
                g_free(entry);
1841
                g_free(utf8entry);
1842
                g_free(utf8name);
1843
#ifdef G_OS_WIN32
1844
        } while (find_next_file(hfind, &wfd));
1845
#else
1846
        }
1847
#endif
1848

    
1849
#ifdef G_OS_WIN32
1850
        FindClose(hfind);
1851
#else
1852
        closedir(dp);
1853
#endif
1854

    
1855
        if (item->path) {
1856
                gint new, unread, total, min, max;
1857

    
1858
                procmsg_get_mark_sum
1859
                        (item, &new, &unread, &total, &min, &max, 0);
1860
                if (n_msg > total) {
1861
                        new += n_msg - total;
1862
                        unread += n_msg - total;
1863
                }
1864
                item->new = new;
1865
                item->unread = unread;
1866
                item->total = n_msg;
1867
                item->updated = TRUE;
1868
                item->mtime = 0;
1869
        }
1870
}
1871

    
1872
static gboolean mh_rename_folder_func(GNode *node, gpointer data)
1873
{
1874
        FolderItem *item = node->data;
1875
        gchar **paths = data;
1876
        const gchar *oldpath = paths[0];
1877
        const gchar *newpath = paths[1];
1878
        gchar *base;
1879
        gchar *new_itempath;
1880
        gint oldpathlen;
1881

    
1882
        oldpathlen = strlen(oldpath);
1883
        if (strncmp(oldpath, item->path, oldpathlen) != 0) {
1884
                g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
1885
                return TRUE;
1886
        }
1887

    
1888
        base = item->path + oldpathlen;
1889
        while (*base == '/') base++;
1890
        if (*base == '\0')
1891
                new_itempath = g_strdup(newpath);
1892
        else
1893
                new_itempath = g_strconcat(newpath, "/", base, NULL);
1894
        g_free(item->path);
1895
        item->path = new_itempath;
1896

    
1897
        return FALSE;
1898
}