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