bug in cursor up navigation fixed
[claws.git] / src / mh.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2001 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <dirent.h>
28 #include <sys/stat.h>
29 #include <errno.h>
30
31 #undef MEASURE_TIME
32
33 #ifdef MEASURE_TIME
34 #  include <sys/time.h>
35 #endif
36
37 #include "intl.h"
38 #include "folder.h"
39 #include "mh.h"
40 #include "procmsg.h"
41 #include "procheader.h"
42 #include "utils.h"
43
44 static GSList  *mh_get_uncached_msgs            (GHashTable     *msg_table,
45                                                  FolderItem     *item);
46 static MsgInfo *mh_parse_msg                    (const gchar    *file,
47                                                  FolderItem     *item);
48 static void     mh_scan_tree_recursive          (FolderItem     *item);
49
50 static gboolean mh_rename_folder_func           (GNode          *node,
51                                                  gpointer        data);
52
53
54 GSList *mh_get_msg_list(Folder *folder, FolderItem *item, gboolean use_cache)
55 {
56         GSList *mlist;
57         GHashTable *msg_table;
58         gchar *path;
59         struct stat s;
60         gboolean scan_new = TRUE;
61 #ifdef MEASURE_TIME
62         struct timeval tv_before, tv_after, tv_result;
63
64         gettimeofday(&tv_before, NULL);
65 #endif
66
67         g_return_val_if_fail(item != NULL, NULL);
68
69         path = folder_item_get_path(item);
70         if (stat(path, &s) < 0) {
71                 FILE_OP_ERROR(path, "stat");
72         } else {
73                 if (item->mtime == s.st_mtime) {
74                         debug_print("Folder is not modified.\n");
75                         scan_new = FALSE;
76                 } else
77                         item->mtime = s.st_mtime;
78         }
79         g_free(path);
80
81         if (use_cache && !scan_new) {
82                 mlist = procmsg_read_cache(item, FALSE);
83                 if (!mlist)
84                         mlist = mh_get_uncached_msgs(NULL, item);
85         } else if (use_cache) {
86                 GSList *newlist;
87
88                 mlist = procmsg_read_cache(item, TRUE);
89                 msg_table = procmsg_msg_hash_table_create(mlist);
90
91                 newlist = mh_get_uncached_msgs(msg_table, item);
92                 if (msg_table)
93                         g_hash_table_destroy(msg_table);
94
95                 mlist = g_slist_concat(mlist, newlist);
96         } else
97                 mlist = mh_get_uncached_msgs(NULL, item);
98
99         procmsg_set_flags(mlist, item);
100
101 #ifdef MEASURE_TIME
102         gettimeofday(&tv_after, NULL);
103
104         timersub(&tv_after, &tv_before, &tv_result);
105         g_print("mh_get_msg_list: %s: elapsed time: %ld.%06ld sec\n",
106                 item->path, tv_result.tv_sec, tv_result.tv_usec);
107 #endif
108
109         return mlist;
110 }
111
112 gchar *mh_fetch_msg(Folder *folder, FolderItem *item, gint num)
113 {
114         gchar *path;
115         gchar *file;
116
117         g_return_val_if_fail(item != NULL, NULL);
118         g_return_val_if_fail(num > 0 && num <= item->last_num, NULL);
119
120         path = folder_item_get_path(item);
121         file = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
122         g_free(path);
123         if (!is_file_exist(file)) {
124                 g_free(file);
125                 return NULL;
126         }
127
128         return file;
129 }
130
131 gint mh_add_msg(Folder *folder, FolderItem *dest, const gchar *file)
132 {
133         gchar *destpath;
134         gchar *destfile;
135
136         g_return_val_if_fail(dest != NULL, -1);
137         g_return_val_if_fail(file != NULL, -1);
138
139         if (dest->last_num < 0) {
140                 mh_scan_folder(folder, dest);
141                 if (dest->last_num < 0) return -1;
142         }
143
144         destpath = folder_item_get_path(dest);
145         g_return_val_if_fail(destpath != NULL, -1);
146         destfile = g_strdup_printf("%s%c%d", destpath, G_DIR_SEPARATOR,
147                                    dest->last_num + 1);
148         if (copy_file(file, destfile) < 0) {
149                 g_warning(_("can't copy message %s to %s\n"), file, destfile);
150                 g_free(destfile);
151                 return -1;
152         }
153
154         g_free(destfile);
155         dest->last_num++;
156         return dest->last_num;
157 }
158
159 gint mh_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
160 {
161         gchar *destdir;
162         gchar *srcfile;
163         gchar *destfile;
164         FILE *fp;
165
166         g_return_val_if_fail(dest != NULL, -1);
167         g_return_val_if_fail(msginfo != NULL, -1);
168
169         if (msginfo->folder == dest) {
170                 g_warning(_("the src folder is identical to the dest.\n"));
171                 return -1;
172         }
173
174         if (dest->last_num < 0) {
175                 mh_scan_folder(folder, dest);
176                 if (dest->last_num < 0) return -1;
177         }
178
179         destdir = folder_item_get_path(dest);
180         if ((fp = procmsg_open_mark_file(destdir, TRUE)) == NULL)
181                 g_warning(_("Can't open mark file.\n"));
182
183         debug_print(_("Moving message %s%c%d to %s ...\n"),
184                     msginfo->folder->path, G_DIR_SEPARATOR,
185                     msginfo->msgnum, dest->path);
186         srcfile = procmsg_get_message_file_path(msginfo);
187         destfile = g_strdup_printf("%s%c%d", destdir, G_DIR_SEPARATOR,
188                                    dest->last_num + 1);
189         g_free(destdir);
190
191         if (is_file_exist(destfile)) {
192                 g_warning(_("%s already exists."), destfile);
193                 g_free(srcfile);
194                 g_free(destfile);
195                 if (fp) fclose(fp);
196                 return -1;
197         }
198
199         if (rename(srcfile, destfile) < 0) {
200                 if (EXDEV == errno) {
201                         if (copy_file(srcfile, destfile) < 0) {
202                                 g_free(srcfile);
203                                 g_free(destfile);
204                                 return -1;
205                         }
206                         unlink(srcfile);
207                 } else {
208                         FILE_OP_ERROR(srcfile, "rename");
209                         g_free(srcfile);
210                         g_free(destfile);
211                         if (fp) fclose(fp);
212                         return -1;
213                 }
214         }
215
216         g_free(srcfile);
217         g_free(destfile);
218         dest->last_num++;
219
220         if (fp) {
221                 MsgInfo newmsginfo;
222
223                 newmsginfo.msgnum = dest->last_num;
224                 newmsginfo.flags = msginfo->flags;
225                 if (dest->stype == F_OUTBOX ||
226                     dest->stype == F_QUEUE  ||
227                     dest->stype == F_DRAFT  ||
228                     dest->stype == F_TRASH)
229                         MSG_UNSET_FLAGS(newmsginfo.flags,
230                                         MSG_NEW|MSG_UNREAD|MSG_DELETED);
231
232                 procmsg_write_flags(&newmsginfo, fp);
233                 fclose(fp);
234         }
235
236         return dest->last_num;
237 }
238
239 gint mh_move_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
240 {
241         gchar *destdir;
242         gchar *srcfile;
243         gchar *destfile;
244         FILE *fp;
245         GSList *cur;
246         MsgInfo *msginfo;
247
248         g_return_val_if_fail(dest != NULL, -1);
249         g_return_val_if_fail(msglist != NULL, -1);
250
251         if (dest->last_num < 0) {
252                 mh_scan_folder(folder, dest);
253                 if (dest->last_num < 0) return -1;
254         }
255
256         destdir = folder_item_get_path(dest);
257         if ((fp = procmsg_open_mark_file(destdir, TRUE)) == NULL)
258                 g_warning(_("Can't open mark file.\n"));
259
260         for (cur = msglist; cur != NULL; cur = cur->next) {
261                 msginfo = (MsgInfo *)cur->data;
262
263                 if (msginfo->folder == dest) {
264                         g_warning(_("the src folder is identical to the dest.\n"));
265                         continue;
266                 }
267                 debug_print(_("Moving message %s%c%d to %s ...\n"),
268                             msginfo->folder->path, G_DIR_SEPARATOR,
269                             msginfo->msgnum, dest->path);
270
271                 srcfile = procmsg_get_message_file_path(msginfo);
272                 destfile = g_strdup_printf("%s%c%d", destdir, G_DIR_SEPARATOR,
273                                            dest->last_num + 1);
274
275                 if (is_file_exist(destfile)) {
276                         g_warning(_("%s already exists."), destfile);
277                         g_free(srcfile);
278                         g_free(destfile);
279                         break;
280                 }
281
282                 if (rename(srcfile, destfile) < 0) {
283                         if (EXDEV == errno) {
284                                 if (copy_file(srcfile, destfile) < 0) {
285                                         g_free(srcfile);
286                                         g_free(destfile);
287                                         break;
288                                 }
289                                 unlink(srcfile);
290                         } else {
291                                 FILE_OP_ERROR(srcfile, "rename");
292                                 g_free(srcfile);
293                                 g_free(destfile);
294                                 break;
295                         }
296                 }
297
298                 g_free(srcfile);
299                 g_free(destfile);
300                 dest->last_num++;
301
302                 if (fp) {
303                         MsgInfo newmsginfo;
304
305                         newmsginfo.msgnum = dest->last_num;
306                         newmsginfo.flags = msginfo->flags;
307                         if (dest->stype == F_OUTBOX ||
308                             dest->stype == F_QUEUE  ||
309                             dest->stype == F_DRAFT  ||
310                             dest->stype == F_TRASH)
311                                 MSG_UNSET_FLAGS(newmsginfo.flags,
312                                                 MSG_NEW|MSG_UNREAD|MSG_DELETED);
313
314                         procmsg_write_flags(&newmsginfo, fp);
315                 }
316         }
317
318         g_free(destdir);
319         if (fp) fclose(fp);
320
321         return dest->last_num;
322 }
323
324 gint mh_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
325 {
326         gchar *destdir;
327         gchar *srcfile;
328         gchar *destfile;
329         FILE *fp;
330
331         g_return_val_if_fail(dest != NULL, -1);
332         g_return_val_if_fail(msginfo != NULL, -1);
333
334         if (msginfo->folder == dest) {
335                 g_warning(_("the src folder is identical to the dest.\n"));
336                 return -1;
337         }
338
339         if (dest->last_num < 0) {
340                 mh_scan_folder(folder, dest);
341                 if (dest->last_num < 0) return -1;
342         }
343
344         destdir = folder_item_get_path(dest);
345         if ((fp = procmsg_open_mark_file(destdir, TRUE)) == NULL)
346                 g_warning(_("Can't open mark file.\n"));
347
348         debug_print(_("Copying message %s%c%d to %s ...\n"),
349                     msginfo->folder->path, G_DIR_SEPARATOR,
350                     msginfo->msgnum, dest->path);
351         srcfile = procmsg_get_message_file_path(msginfo);
352         destfile = g_strdup_printf("%s%c%d", destdir, G_DIR_SEPARATOR,
353                                    dest->last_num + 1);
354         g_free(destdir);
355
356         if (is_file_exist(destfile)) {
357                 g_warning(_("%s already exists."), destfile);
358                 g_free(srcfile);
359                 g_free(destfile);
360                 if (fp) fclose(fp);
361                 return -1;
362         }
363
364         if (copy_file(srcfile, destfile) < 0) {
365                 FILE_OP_ERROR(srcfile, "copy");
366                 g_free(srcfile);
367                 g_free(destfile);
368                 if (fp) fclose(fp);
369                 return -1;
370         }
371
372         g_free(srcfile);
373         g_free(destfile);
374         dest->last_num++;
375
376         if (fp) {
377                 MsgInfo newmsginfo;
378
379                 newmsginfo.msgnum = dest->last_num;
380                 newmsginfo.flags = msginfo->flags;
381                 if (dest->stype == F_OUTBOX ||
382                     dest->stype == F_QUEUE  ||
383                     dest->stype == F_DRAFT  ||
384                     dest->stype == F_TRASH)
385                         MSG_UNSET_FLAGS(newmsginfo.flags,
386                                         MSG_NEW|MSG_UNREAD|MSG_DELETED);
387                 procmsg_write_flags(&newmsginfo, fp);
388                 fclose(fp);
389         }
390
391         return dest->last_num;
392 }
393
394 gint mh_copy_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
395 {
396         gchar *destdir;
397         gchar *srcfile;
398         gchar *destfile;
399         FILE *fp;
400         GSList *cur;
401         MsgInfo *msginfo;
402
403         g_return_val_if_fail(dest != NULL, -1);
404         g_return_val_if_fail(msglist != NULL, -1);
405
406         if (dest->last_num < 0) {
407                 mh_scan_folder(folder, dest);
408                 if (dest->last_num < 0) return -1;
409         }
410
411         destdir = folder_item_get_path(dest);
412         if ((fp = procmsg_open_mark_file(destdir, TRUE)) == NULL)
413                 g_warning(_("Can't open mark file.\n"));
414
415         for (cur = msglist; cur != NULL; cur = cur->next) {
416                 msginfo = (MsgInfo *)cur->data;
417
418                 if (msginfo->folder == dest) {
419                         g_warning(_("the src folder is identical to the dest.\n"));
420                         continue;
421                 }
422                 debug_print(_("Copying message %s%c%d to %s ...\n"),
423                             msginfo->folder->path, G_DIR_SEPARATOR,
424                             msginfo->msgnum, dest->path);
425
426                 srcfile = procmsg_get_message_file_path(msginfo);
427                 destfile = g_strdup_printf("%s%c%d", destdir, G_DIR_SEPARATOR,
428                                            dest->last_num + 1);
429
430                 if (is_file_exist(destfile)) {
431                         g_warning(_("%s already exists."), destfile);
432                         g_free(srcfile);
433                         g_free(destfile);
434                         break;
435                 }
436
437                 if (copy_file(srcfile, destfile) < 0) {
438                         FILE_OP_ERROR(srcfile, "copy");
439                         g_free(srcfile);
440                         g_free(destfile);
441                         break;
442                 }
443
444                 g_free(srcfile);
445                 g_free(destfile);
446                 dest->last_num++;
447
448                 if (fp) {
449                         MsgInfo newmsginfo;
450
451                         newmsginfo.msgnum = dest->last_num;
452                         newmsginfo.flags = msginfo->flags;
453                         if (dest->stype == F_OUTBOX ||
454                             dest->stype == F_QUEUE  ||
455                             dest->stype == F_DRAFT  ||
456                             dest->stype == F_TRASH)
457                                 MSG_UNSET_FLAGS(newmsginfo.flags,
458                                                 MSG_NEW|MSG_UNREAD|MSG_DELETED);
459                         procmsg_write_flags(&newmsginfo, fp);
460                 }
461         }
462
463         g_free(destdir);
464         if (fp) fclose(fp);
465
466         return dest->last_num;
467 }
468
469 gint mh_remove_msg(Folder *folder, FolderItem *item, gint num)
470 {
471         gchar *file;
472
473         g_return_val_if_fail(item != NULL, -1);
474
475         file = mh_fetch_msg(folder, item, num);
476         g_return_val_if_fail(file != NULL, -1);
477
478         if (unlink(file) < 0) {
479                 FILE_OP_ERROR(file, "unlink");
480                 g_free(file);
481                 return -1;
482         }
483
484         g_free(file);
485         return 0;
486 }
487
488 gint mh_remove_all_msg(Folder *folder, FolderItem *item)
489 {
490         gchar *path;
491         DIR *dp;
492         struct dirent *d;
493
494         g_return_val_if_fail(item != NULL, -1);
495
496         path = folder_item_get_path(item);
497         g_return_val_if_fail(path != NULL, -1);
498         if (change_dir(path) < 0) {
499                 g_free(path);
500                 return -1;
501         }
502         g_free(path);
503
504         if ((dp = opendir(".")) == NULL) {
505                 FILE_OP_ERROR(item->path, "opendir");
506                 return -1;
507         }
508
509         while ((d = readdir(dp)) != NULL) {
510                 if (to_number(d->d_name) < 0) continue;
511                 if (unlink(d->d_name) < 0)
512                         FILE_OP_ERROR(d->d_name, "unlink");
513         }
514
515         closedir(dp);
516         return 0;
517 }
518
519 gboolean mh_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
520 {
521         struct stat s;
522
523         if (stat(itos(msginfo->msgnum), &s) < 0 ||
524             msginfo->size  != s.st_size ||
525             msginfo->mtime != s.st_mtime)
526                 return TRUE;
527
528         return FALSE;
529 }
530
531 void mh_scan_folder(Folder *folder, FolderItem *item)
532 {
533         gchar *path;
534         DIR *dp;
535         struct dirent *d;
536         struct stat s;
537         gint max = 0;
538         gint num;
539         gint n_msg = 0;
540
541         g_return_if_fail(item != NULL);
542
543         path = folder_item_get_path(item);
544         g_return_if_fail(path != NULL);
545         if (change_dir(path) < 0) {
546                 g_free(path);
547                 return;
548         }
549         g_free(path);
550
551         if ((dp = opendir(".")) == NULL) {
552                 FILE_OP_ERROR(item->path, "opendir");
553                 return;
554         }
555
556         if (folder->ui_func)
557                 folder->ui_func(folder, item, folder->ui_func_data);
558
559         while ((d = readdir(dp)) != NULL) {
560                 if ((num = to_number(d->d_name)) >= 0 &&
561                     stat(d->d_name, &s) == 0 &&
562                     S_ISREG(s.st_mode)) {
563                         n_msg++;
564                         if (max < num)
565                                 max = num;
566                 }
567         }
568
569         closedir(dp);
570
571         if (n_msg == 0)
572                 item->new = item->unread = item->total = 0;
573         else {
574                 gint new, unread, total;
575
576                 procmsg_get_mark_sum(".", &new, &unread, &total);
577                 if (n_msg > total) {
578                         new += n_msg - total;
579                         unread += n_msg - total;
580                 }
581                 item->new = new;
582                 item->unread = unread;
583                 item->total = n_msg;
584         }
585
586         debug_print(_("Last number in dir %s = %d\n"), item->path, max);
587         item->last_num = max;
588 }
589
590 void mh_scan_tree(Folder *folder)
591 {
592         FolderItem *item;
593         gchar *rootpath;
594
595         g_return_if_fail(folder != NULL);
596
597         folder_tree_destroy(folder);
598         item = folder_item_new(folder->name, NULL);
599         item->folder = folder;
600         folder->node = g_node_new(item);
601
602         rootpath = folder_item_get_path(item);
603         if (change_dir(rootpath) < 0) {
604                 g_free(rootpath);
605                 return;
606         }
607         g_free(rootpath);
608
609         mh_scan_tree_recursive(item);
610 }
611
612 #define MAKE_DIR_IF_NOT_EXIST(dir) \
613 { \
614         if (!is_dir_exist(dir)) { \
615                 if (is_file_exist(dir)) { \
616                         g_warning(_("File `%s' already exists.\n" \
617                                     "Can't create folder."), dir); \
618                         return -1; \
619                 } \
620                 if (mkdir(dir, S_IRWXU) < 0) { \
621                         FILE_OP_ERROR(dir, "mkdir"); \
622                         return -1; \
623                 } \
624                 if (chmod(dir, S_IRWXU) < 0) \
625                         FILE_OP_ERROR(dir, "chmod"); \
626         } \
627 }
628
629 gint mh_create_tree(Folder *folder)
630 {
631         gchar *rootpath;
632
633         g_return_val_if_fail(folder != NULL, -1);
634
635         CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
636         rootpath = LOCAL_FOLDER(folder)->rootpath;
637         MAKE_DIR_IF_NOT_EXIST(rootpath);
638         CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
639         MAKE_DIR_IF_NOT_EXIST(INBOX_DIR);
640         MAKE_DIR_IF_NOT_EXIST(OUTBOX_DIR);
641         MAKE_DIR_IF_NOT_EXIST(QUEUE_DIR);
642         MAKE_DIR_IF_NOT_EXIST(DRAFT_DIR);
643         MAKE_DIR_IF_NOT_EXIST(TRASH_DIR);
644
645         return 0;
646 }
647
648 #undef MAKE_DIR_IF_NOT_EXIST
649
650 FolderItem *mh_create_folder(Folder *folder, FolderItem *parent,
651                              const gchar *name)
652 {
653         gchar *path;
654         gchar *fullpath;
655         FolderItem *new_item;
656
657         g_return_val_if_fail(folder != NULL, NULL);
658         g_return_val_if_fail(parent != NULL, NULL);
659         g_return_val_if_fail(name != NULL, NULL);
660
661         path = folder_item_get_path(parent);
662         fullpath = g_strconcat(path, G_DIR_SEPARATOR_S, name, NULL);
663         g_free(path);
664
665         if (mkdir(fullpath, S_IRWXU) < 0) {
666                 FILE_OP_ERROR(fullpath, "mkdir");
667                 g_free(fullpath);
668                 return NULL;
669         }
670         if (chmod(fullpath, S_IRWXU) < 0)
671                 FILE_OP_ERROR(fullpath, "chmod");
672
673         g_free(fullpath);
674
675         if (parent->path)
676                 path = g_strconcat(parent->path, G_DIR_SEPARATOR_S, name,
677                                    NULL);
678         else
679                 path = g_strdup(name);
680         new_item = folder_item_new(name, path);
681         folder_item_append(parent, new_item);
682         g_free(path);
683
684         return new_item;
685 }
686
687 gint mh_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
688 {
689         gchar *oldpath;
690         gchar *dirname;
691         gchar *newpath;
692         GNode *node;
693         gchar *paths[2];
694
695         g_return_val_if_fail(folder != NULL, -1);
696         g_return_val_if_fail(item != NULL, -1);
697         g_return_val_if_fail(item->path != NULL, -1);
698         g_return_val_if_fail(name != NULL, -1);
699
700         oldpath = folder_item_get_path(item);
701         dirname = g_dirname(oldpath);
702         newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, name, NULL);
703         g_free(dirname);
704
705         if (rename(oldpath, newpath) < 0) {
706                 FILE_OP_ERROR(oldpath, "rename");
707                 g_free(oldpath);
708                 g_free(newpath);
709                 return -1;
710         }
711
712         g_free(oldpath);
713         g_free(newpath);
714
715         if (strchr(item->path, G_DIR_SEPARATOR) != NULL) {
716                 dirname = g_dirname(item->path);
717                 newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, name, NULL);
718                 g_free(dirname);
719         } else
720                 newpath = g_strdup(name);
721
722         g_free(item->name);
723         item->name = g_strdup(name);
724
725         node = g_node_find(item->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL,
726                            item);
727         paths[0] = g_strdup(item->path);
728         paths[1] = newpath;
729         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
730                         mh_rename_folder_func, paths);
731
732         g_free(paths[0]);
733         g_free(paths[1]);
734         return 0;
735 }
736
737 gint mh_remove_folder(Folder *folder, FolderItem *item)
738 {
739         gchar *path;
740
741         g_return_val_if_fail(folder != NULL, -1);
742         g_return_val_if_fail(item != NULL, -1);
743         g_return_val_if_fail(item->path != NULL, -1);
744
745         path = folder_item_get_path(item);
746         if (remove_dir_recursive(path) < 0) {
747                 g_warning("can't remove directory `%s'\n", path);
748                 g_free(path);
749                 return -1;
750         }
751
752         g_free(path);
753         folder_item_remove(item);
754         return 0;
755 }
756
757
758 static GSList *mh_get_uncached_msgs(GHashTable *msg_table, FolderItem *item)
759 {
760         gchar *path;
761         DIR *dp;
762         struct dirent *d;
763         struct stat s;
764         GSList *newlist = NULL;
765         GSList *last = NULL;
766         MsgInfo *msginfo;
767         gint n_newmsg = 0;
768         gint num;
769
770         g_return_val_if_fail(item != NULL, NULL);
771
772         path = folder_item_get_path(item);
773         g_return_val_if_fail(path != NULL, NULL);
774         if (change_dir(path) < 0) {
775                 g_free(path);
776                 return NULL;
777         }
778         g_free(path);
779
780         if ((dp = opendir(".")) == NULL) {
781                 FILE_OP_ERROR(item->path, "opendir");
782                 return NULL;
783         }
784
785         debug_print(_("\tSearching uncached messages... "));
786
787         if (msg_table) {
788                 while ((d = readdir(dp)) != NULL) {
789                         if ((num = to_number(d->d_name)) < 0) continue;
790                         if (stat(d->d_name, &s) < 0) {
791                                 FILE_OP_ERROR(d->d_name, "stat");
792                                 continue;
793                         }
794                         if (!S_ISREG(s.st_mode)) continue;
795
796                         msginfo = g_hash_table_lookup
797                                 (msg_table, GUINT_TO_POINTER(num));
798
799                         if (!msginfo) {
800                                 /* not found in the cache (uncached message) */
801                                 msginfo = mh_parse_msg(d->d_name, item);
802                                 if (!msginfo) continue;
803
804                                 if (!newlist)
805                                         last = newlist =
806                                                 g_slist_append(NULL, msginfo);
807                                 else {
808                                         last = g_slist_append(last, msginfo);
809                                         last = last->next;
810                                 }
811                                 n_newmsg++;
812                         }
813                 }
814         } else {
815                 /* discard all previous cache */
816                 while ((d = readdir(dp)) != NULL) {
817                         if (to_number(d->d_name) < 0) continue;
818                         if (stat(d->d_name, &s) < 0) {
819                                 FILE_OP_ERROR(d->d_name, "stat");
820                                 continue;
821                         }
822                         if (!S_ISREG(s.st_mode)) continue;
823
824                         msginfo = mh_parse_msg(d->d_name, item);
825                         if (!msginfo) continue;
826
827                         if (!newlist)
828                                 last = newlist = g_slist_append(NULL, msginfo);
829                         else {
830                                 last = g_slist_append(last, msginfo);
831                                 last = last->next;
832                         }
833                         n_newmsg++;
834                 }
835         }
836
837         closedir(dp);
838
839         if (n_newmsg)
840                 debug_print(_("%d uncached message(s) found.\n"), n_newmsg);
841         else
842                 debug_print(_("done.\n"));
843
844         /* sort new messages in numerical order */
845         if (newlist) {
846                 debug_print(_("\tSorting uncached messages in numerical order... "));
847                 newlist = g_slist_sort
848                         (newlist, (GCompareFunc)procmsg_cmp_msgnum_for_sort);
849                 debug_print(_("done.\n"));
850         }
851
852         return newlist;
853 }
854
855 static MsgInfo *mh_parse_msg(const gchar *file, FolderItem *item)
856 {
857         struct stat s;
858         MsgInfo *msginfo;
859         MsgFlags flags = MSG_NEW|MSG_UNREAD;
860
861         g_return_val_if_fail(item != NULL, NULL);
862         g_return_val_if_fail(file != NULL, NULL);
863
864         if (item->stype == F_QUEUE) {
865                 MSG_SET_FLAGS(flags, MSG_QUEUED);
866         } else if (item->stype == F_DRAFT) {
867                 MSG_SET_FLAGS(flags, MSG_DRAFT);
868         }
869
870         msginfo = procheader_parse(file, flags, FALSE);
871         if (!msginfo) return NULL;
872
873         msginfo->msgnum = atoi(file);
874         msginfo->folder = item;
875
876         if (stat(file, &s) < 0) {
877                 FILE_OP_ERROR(file, "stat");
878                 msginfo->size = 0;
879                 msginfo->mtime = 0;
880         } else {
881                 msginfo->size = s.st_size;
882                 msginfo->mtime = s.st_mtime;
883         }
884
885         return msginfo;
886 }
887
888 static gboolean mh_is_maildir_one(const gchar *path, const gchar *dir)
889 {
890         gchar *entry;
891         gboolean result;
892
893         entry = g_strconcat(path, G_DIR_SEPARATOR_S, dir, NULL);
894         result = is_dir_exist(entry);
895         g_free(entry);
896
897         return result;
898 }
899
900 /*
901  * check whether PATH is a Maildir style mailbox.
902  * This is the case if the 3 subdir: new, cur, tmp are existing.
903  * This functon assumes that entry is an directory
904  */
905 static gboolean mh_is_maildir(const gchar *path)
906 {
907         return mh_is_maildir_one(path, "new") &&
908                mh_is_maildir_one(path, "cur") &&
909                mh_is_maildir_one(path, "tmp");
910 }
911
912 static void mh_scan_tree_recursive(FolderItem *item)
913 {
914         DIR *dp;
915         struct dirent *d;
916         struct stat s;
917         gchar *entry;
918         gint n_msg = 0;
919
920         g_return_if_fail(item != NULL);
921         g_return_if_fail(item->folder != NULL);
922
923         dp = opendir(item->path ? item->path : ".");
924         if (!dp) {
925                 FILE_OP_ERROR(item->path ? item->path : ".", "opendir");
926                 return;
927         }
928
929         debug_print("scanning %s ...\n",
930                     item->path ? item->path
931                     : LOCAL_FOLDER(item->folder)->rootpath);
932         if (item->folder->ui_func)
933                 item->folder->ui_func(item->folder, item,
934                                       item->folder->ui_func_data);
935
936         while ((d = readdir(dp)) != NULL) {
937                 if (d->d_name[0] == '.') continue;
938
939                 if (item->path)
940                         entry = g_strconcat(item->path, G_DIR_SEPARATOR_S,
941                                             d->d_name, NULL);
942                 else
943                         entry = g_strdup(d->d_name);
944
945                 if (stat(entry, &s) < 0) {
946                         FILE_OP_ERROR(entry, "stat");
947                         g_free(entry);
948                         continue;
949                 }
950
951                 if (S_ISDIR(s.st_mode)) {
952                         FolderItem *new_item;
953
954                         if (mh_is_maildir(entry)) {
955                                 g_free(entry);
956                                 continue;
957                         }
958
959                         new_item = folder_item_new(d->d_name, entry);
960                         folder_item_append(item, new_item);
961                         if (!item->path) {
962                                 if (!strcmp(d->d_name, "inbox")) {
963                                         new_item->stype = F_INBOX;
964                                         item->folder->inbox = new_item;
965                                 } else if (!strcmp(d->d_name, "outbox")) {
966                                         new_item->stype = F_OUTBOX;
967                                         item->folder->outbox = new_item;
968                                 } else if (!strcmp(d->d_name, "draft")) {
969                                         new_item->stype = F_DRAFT;
970                                         item->folder->draft = new_item;
971                                 } else if (!strcmp(d->d_name, "queue")) {
972                                         new_item->stype = F_QUEUE;
973                                         item->folder->queue = new_item;
974                                 } else if (!strcmp(d->d_name, "trash")) {
975                                         new_item->stype = F_TRASH;
976                                         item->folder->trash = new_item;
977                                 }
978                         }
979                         mh_scan_tree_recursive(new_item);
980                 } else if (to_number(d->d_name) != -1) n_msg++;
981
982                 g_free(entry);
983         }
984
985         closedir(dp);
986
987         if (item->path) {
988                 gint new, unread, total;
989
990                 procmsg_get_mark_sum(item->path, &new, &unread, &total);
991                 if (n_msg > total) {
992                         new += n_msg - total;
993                         unread += n_msg - total;
994                 }
995                 item->new = new;
996                 item->unread = unread;
997                 item->total = n_msg;
998         }
999 }
1000
1001 static gboolean mh_rename_folder_func(GNode *node, gpointer data)
1002 {
1003         FolderItem *item = node->data;
1004         gchar **paths = data;
1005         const gchar *oldpath = paths[0];
1006         const gchar *newpath = paths[1];
1007         gchar *base;
1008         gchar *new_itempath;
1009         gint oldpathlen;
1010
1011         oldpathlen = strlen(oldpath);
1012         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
1013                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
1014                 return TRUE;
1015         }
1016
1017         base = item->path + oldpathlen;
1018         while (*base == G_DIR_SEPARATOR) base++;
1019         if (*base == '\0')
1020                 new_itempath = g_strdup(newpath);
1021         else
1022                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
1023                                            NULL);
1024         g_free(item->path);
1025         item->path = new_itempath;
1026
1027         return FALSE;
1028 }