summaryrefslogtreecommitdiff
path: root/libsylph/mh.c
diff options
context:
space:
mode:
authorSimeon Simeonov2018-02-26 11:23:00 +0100
committerSimeon Simeonov2018-02-26 11:23:00 +0100
commit0b3cbf57875fd692e4ba0b336fefa4bee1ed00dc (patch)
treeaf916a30553c78ce9d4f2d8658656a175c5b6921 /libsylph/mh.c
Initial commit for sylpheed 3.7.0
Diffstat (limited to 'libsylph/mh.c')
-rw-r--r--libsylph/mh.c1898
1 files changed, 1898 insertions, 0 deletions
diff --git a/libsylph/mh.c b/libsylph/mh.c
new file mode 100644
index 0000000..c1f0679
--- /dev/null
+++ b/libsylph/mh.c
@@ -0,0 +1,1898 @@
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
50G_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
58static void mh_folder_init (Folder *folder,
59 const gchar *name,
60 const gchar *path);
61
62static Folder *mh_folder_new (const gchar *name,
63 const gchar *path);
64static void mh_folder_destroy (Folder *folder);
65
66static GSList *mh_get_msg_list (Folder *folder,
67 FolderItem *item,
68 gboolean use_cache);
69static GSList *mh_get_uncached_msg_list(Folder *folder,
70 FolderItem *item);
71static gchar *mh_fetch_msg (Folder *folder,
72 FolderItem *item,
73 gint num);
74static MsgInfo *mh_get_msginfo (Folder *folder,
75 FolderItem *item,
76 gint num);
77static gint mh_add_msg (Folder *folder,
78 FolderItem *dest,
79 const gchar *file,
80 MsgFlags *flags,
81 gboolean remove_source);
82static gint mh_add_msgs (Folder *folder,
83 FolderItem *dest,
84 GSList *file_list,
85 gboolean remove_source,
86 gint *first);
87static gint mh_add_msg_msginfo (Folder *folder,
88 FolderItem *dest,
89 MsgInfo *msginfo,
90 gboolean remove_source);
91static gint mh_add_msgs_msginfo (Folder *folder,
92 FolderItem *dest,
93 GSList *msglist,
94 gboolean remove_source,
95 gint *first);
96static gint mh_move_msg (Folder *folder,
97 FolderItem *dest,
98 MsgInfo *msginfo);
99static gint mh_move_msgs (Folder *folder,
100 FolderItem *dest,
101 GSList *msglist);
102static gint mh_copy_msg (Folder *folder,
103 FolderItem *dest,
104 MsgInfo *msginfo);
105static gint mh_copy_msgs (Folder *folder,
106 FolderItem *dest,
107 GSList *msglist);
108static gint mh_remove_msg (Folder *folder,
109 FolderItem *item,
110 MsgInfo *msginfo);
111static gint mh_remove_all_msg (Folder *folder,
112 FolderItem *item);
113static gboolean mh_is_msg_changed (Folder *folder,
114 FolderItem *item,
115 MsgInfo *msginfo);
116static gint mh_close (Folder *folder,
117 FolderItem *item);
118
119static gint mh_scan_folder_full (Folder *folder,
120 FolderItem *item,
121 gboolean count_sum);
122static gint mh_scan_folder (Folder *folder,
123 FolderItem *item);
124static gint mh_scan_tree (Folder *folder);
125
126static gint mh_create_tree (Folder *folder);
127static FolderItem *mh_create_folder (Folder *folder,
128 FolderItem *parent,
129 const gchar *name);
130static gint mh_rename_folder (Folder *folder,
131 FolderItem *item,
132 const gchar *name);
133static gint mh_move_folder (Folder *folder,
134 FolderItem *item,
135 FolderItem *new_parent);
136static gint mh_remove_folder (Folder *folder,
137 FolderItem *item);
138
139static gchar *mh_get_new_msg_filename (FolderItem *dest);
140
141static gint mh_do_move_msgs (Folder *folder,
142 FolderItem *dest,
143 GSList *msglist);
144
145static time_t mh_get_mtime (FolderItem *item);
146static GSList *mh_get_uncached_msgs (GHashTable *msg_table,
147 FolderItem *item);
148static MsgInfo *mh_parse_msg (const gchar *file,
149 FolderItem *item);
150static void mh_remove_missing_folder_items (Folder *folder);
151static void mh_scan_tree_recursive (FolderItem *item);
152
153static gboolean mh_rename_folder_func (GNode *node,
154 gpointer data);
155
156static 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
192FolderClass *mh_get_class(void)
193{
194 return &mh_class;
195}
196
197static 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
207static void mh_folder_destroy(Folder *folder)
208{
209 folder_local_folder_destroy(LOCAL_FOLDER(folder));
210}
211
212static 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
218static 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
339static 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
345static GSList *mh_get_uncached_msg_list(Folder *folder, FolderItem *item)
346{
347 return mh_get_msg_list_full(folder, item, TRUE, TRUE);
348}
349
350static 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
378static 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
398static 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
445static 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
461static 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
563static 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
576static 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
682static 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
762static 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
774static 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
792static 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
804static 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
870static 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
909static 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
937static gboolean mh_is_msg_changed(Folder *folder, FolderItem *item,
938 MsgInfo *msginfo)
939{
940 GStatBuf 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
951static gint mh_close(Folder *folder, FolderItem *item)
952{
953 return 0;
954}
955
956#ifdef G_OS_WIN32
957struct wfddata {
958 WIN32_FIND_DATAA wfda;
959 WIN32_FIND_DATAW wfdw;
960 DWORD file_attr;
961 gchar *file_name;
962};
963
964static 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
1017static 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
1041static 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
1154static gint mh_scan_folder(Folder *folder, FolderItem *item)
1155{
1156 return mh_scan_folder_full(folder, item, TRUE);
1157}
1158
1159static 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
1217static 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
1240static 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
1282static 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
1420static 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
1426static 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
1432static 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
1460static time_t mh_get_mtime(FolderItem *item)
1461{
1462 gchar *path;
1463 GStatBuf 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
1476static 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
1576static 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
1603static 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 */
1620static 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
1628static 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
1658static 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
1670static 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 GStatBuf 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
1872static 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}