German translation
[claws.git] / src / mh.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2002 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <dirent.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32
33 #undef MEASURE_TIME
34
35 #ifdef MEASURE_TIME
36 #  include <sys/time.h>
37 #endif
38
39 #include "intl.h"
40 #include "folder.h"
41 #include "mh.h"
42 #include "procmsg.h"
43 #include "procheader.h"
44 #include "utils.h"
45
46 static void     mh_folder_init                  (Folder         *folder,
47                                                  const gchar    *name,
48                                                  const gchar    *path);
49
50 GSList  *mh_get_msg_list        (Folder         *folder,
51                                  FolderItem     *item,
52                                  gboolean        use_cache);
53 gchar   *mh_fetch_msg           (Folder         *folder,
54                                  FolderItem     *item,
55                                  gint            num);
56 MsgInfo   *mh_get_msginfo       (Folder         *folder,
57                                  FolderItem     *item,
58                                  gint            num);
59 gint     mh_add_msg             (Folder         *folder,
60                                  FolderItem     *dest,
61                                  const gchar    *file,
62                                  gboolean        remove_source);
63 gint     mh_move_msg            (Folder         *folder,
64                                  FolderItem     *dest,
65                                  MsgInfo        *msginfo);
66 gint     mh_move_msgs_with_dest (Folder         *folder,
67                                  FolderItem     *dest,
68                                  GSList         *msglist);
69 gint     mh_copy_msg            (Folder         *folder,
70                                  FolderItem     *dest,
71                                  MsgInfo        *msginfo);
72 gint     mh_copy_msgs_with_dest (Folder         *folder,
73                                  FolderItem     *dest,
74                                  GSList         *msglist);
75 gint     mh_remove_msg          (Folder         *folder,
76                                  FolderItem     *item,
77                                  gint            num);
78 gint     mh_remove_all_msg      (Folder         *folder,
79                                  FolderItem     *item);
80 gboolean mh_is_msg_changed      (Folder         *folder,
81                                  FolderItem     *item,
82                                  MsgInfo        *msginfo);
83
84 gint    mh_scan_folder          (Folder         *folder,
85                                  FolderItem     *item);
86 gint mh_get_num_list            (Folder         *folder,
87                                  FolderItem     *item,
88                                  GSList        **list);
89 void    mh_scan_tree            (Folder         *folder);
90
91 gint    mh_create_tree          (Folder         *folder);
92 FolderItem *mh_create_folder    (Folder         *folder,
93                                  FolderItem     *parent,
94                                  const gchar    *name);
95 gint    mh_rename_folder        (Folder         *folder,
96                                  FolderItem     *item,
97                                  const gchar    *name);
98 gint    mh_remove_folder        (Folder         *folder,
99                                  FolderItem     *item);
100
101 gchar   *mh_get_new_msg_filename                (FolderItem     *dest);
102
103 static GSList  *mh_get_uncached_msgs            (GHashTable     *msg_table,
104                                                  FolderItem     *item);
105 static MsgInfo *mh_parse_msg                    (const gchar    *file,
106                                                  FolderItem     *item);
107 static void     mh_scan_tree_recursive          (FolderItem     *item);
108
109 static gboolean mh_rename_folder_func           (GNode          *node,
110                                                  gpointer        data);
111
112
113 Folder *mh_folder_new(const gchar *name, const gchar *path)
114 {
115         Folder *folder;
116
117         folder = (Folder *)g_new0(MHFolder, 1);
118         mh_folder_init(folder, name, path);
119
120         return folder;
121 }
122
123 void mh_folder_destroy(Folder *folder)
124 {
125         folder_local_folder_destroy(LOCAL_FOLDER(folder));
126 }
127
128 static void mh_folder_init(Folder *folder, const gchar *name, const gchar *path)
129 {
130         folder->type = F_MH;
131
132         folder_local_folder_init(folder, name, path);
133
134 /*      folder->get_msg_list        = mh_get_msg_list; */
135         folder->fetch_msg           = mh_fetch_msg;
136         folder->get_msginfo         = mh_get_msginfo;
137         folder->add_msg             = mh_add_msg;
138         folder->move_msg            = mh_move_msg;
139         folder->move_msgs_with_dest = mh_move_msgs_with_dest;
140         folder->copy_msg            = mh_copy_msg;
141         folder->copy_msgs_with_dest = mh_copy_msgs_with_dest;
142         folder->remove_msg          = mh_remove_msg;
143         folder->remove_all_msg      = mh_remove_all_msg;
144         folder->is_msg_changed      = mh_is_msg_changed;
145 /*      folder->scan                = mh_scan_folder; */
146         folder->get_num_list        = mh_get_num_list;
147         folder->scan_tree           = mh_scan_tree;
148         folder->create_tree         = mh_create_tree;
149         folder->create_folder       = mh_create_folder;
150         folder->rename_folder       = mh_rename_folder;
151         folder->remove_folder       = mh_remove_folder;
152         folder->destroy             = mh_folder_destroy;
153 }
154
155 void mh_get_last_num(Folder *folder, FolderItem *item)
156 {
157         gchar *path;
158         DIR *dp;
159         struct dirent *d;
160         struct stat s;
161         gint max = 0;
162         gint num;
163
164         g_return_if_fail(item != NULL);
165
166         debug_print("mh_get_last_num(): Scanning %s ...\n", item->path);
167
168         path = folder_item_get_path(item);
169         g_return_if_fail(path != NULL);
170         if (change_dir(path) < 0) {
171                 g_free(path);
172                 return;
173         }
174         g_free(path);
175
176         if ((dp = opendir(".")) == NULL) {
177                 FILE_OP_ERROR(item->path, "opendir");
178                 return;
179         }
180
181         while ((d = readdir(dp)) != NULL) {
182                 if ((num = to_number(d->d_name)) >= 0 &&
183                     stat(d->d_name, &s) == 0 &&
184                     S_ISREG(s.st_mode)) {
185                         if (max < num)
186                                 max = num;
187                 }
188         }
189         closedir(dp);
190
191         debug_print("Last number in dir %s = %d\n", item->path, max);
192         item->last_num = max;
193 }
194
195 gint mh_get_num_list(Folder *folder, FolderItem *item, GSList **list)
196 {
197
198         gchar *path;
199         DIR *dp;
200         struct dirent *d;
201         struct stat s;
202         gint num, nummsgs = 0;
203
204         g_return_val_if_fail(item != NULL, -1);
205
206         debug_print("mh_get_last_num(): Scanning %s ...\n", item->path);
207
208         path = folder_item_get_path(item);
209         g_return_val_if_fail(path != NULL, -1);
210         if (change_dir(path) < 0) {
211                 g_free(path);
212                 return -1;
213         }
214         g_free(path);
215
216         if ((dp = opendir(".")) == NULL) {
217                 FILE_OP_ERROR(item->path, "opendir");
218                 return -1;
219         }
220
221         while ((d = readdir(dp)) != NULL) {
222                 if ((num = to_number(d->d_name)) >= 0 &&
223                     stat(d->d_name, &s) == 0 &&
224                     S_ISREG(s.st_mode)) {
225                         *list = g_slist_prepend(*list, GINT_TO_POINTER(num));
226                     nummsgs++;
227                 }
228         }
229         closedir(dp);
230
231         return nummsgs;
232 }
233
234 GSList *mh_get_msg_list(Folder *folder, FolderItem *item, gboolean use_cache)
235 {
236         GSList *mlist;
237         GHashTable *msg_table;
238         gchar *path;
239         struct stat s;
240         gboolean scan_new = TRUE;
241 #ifdef MEASURE_TIME
242         struct timeval tv_before, tv_after, tv_result;
243
244         gettimeofday(&tv_before, NULL);
245 #endif
246
247         g_return_val_if_fail(item != NULL, NULL);
248
249         path = folder_item_get_path(item);
250         if (stat(path, &s) < 0) {
251                 FILE_OP_ERROR(path, "stat");
252         } else {
253                 time_t mtime;
254
255                 mtime = MAX(s.st_mtime, s.st_ctime);
256                 if (item->mtime == mtime) {
257                         debug_print("Folder is not modified.\n");
258                         scan_new = FALSE;
259                 } else
260                         item->mtime = mtime;
261         }
262         g_free(path);
263
264         if (use_cache && !scan_new) {
265                 mlist = procmsg_read_cache(item, FALSE);
266                 if (!mlist)
267                         mlist = mh_get_uncached_msgs(NULL, item);
268         } else if (use_cache) {
269                 GSList *newlist;
270
271                 mlist = procmsg_read_cache(item, TRUE);
272                 msg_table = procmsg_msg_hash_table_create(mlist);
273
274                 newlist = mh_get_uncached_msgs(msg_table, item);
275                 if (msg_table)
276                         g_hash_table_destroy(msg_table);
277
278                 mlist = g_slist_concat(mlist, newlist);
279         } else
280                 mlist = mh_get_uncached_msgs(NULL, item);
281
282         procmsg_set_flags(mlist, item);
283
284 #ifdef MEASURE_TIME
285         gettimeofday(&tv_after, NULL);
286
287         timersub(&tv_after, &tv_before, &tv_result);
288         g_print("mh_get_msg_list: %s: elapsed time: %ld.%06ld sec\n",
289                 item->path, tv_result.tv_sec, tv_result.tv_usec);
290 #endif
291
292         return mlist;
293 }
294
295 gchar *mh_fetch_msg(Folder *folder, FolderItem *item, gint num)
296 {
297         gchar *path;
298         gchar *file;
299
300         g_return_val_if_fail(item != NULL, NULL);
301         g_return_val_if_fail(num > 0, NULL);
302
303         path = folder_item_get_path(item);
304         file = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
305         g_free(path);
306         if (!is_file_exist(file)) {
307                 g_free(file);
308                 return NULL;
309         }
310
311         return file;
312 }
313
314 MsgInfo *mh_get_msginfo(Folder *folder, FolderItem *item, gint num)
315 {
316         MsgInfo *msginfo;
317         gchar *file;
318
319         g_return_val_if_fail(item != NULL, NULL);
320         g_return_val_if_fail(num > 0, NULL);
321
322         file = mh_fetch_msg(folder, item, num);
323         if (!file) return NULL;
324
325         msginfo = mh_parse_msg(file, item);
326         if (msginfo)
327                 msginfo->msgnum = num;
328
329         g_free(file);
330
331         return msginfo;
332 }
333
334 gchar *mh_get_new_msg_filename(FolderItem *dest)
335 {
336         gchar *destfile;
337         gchar *destpath;
338
339         destpath = folder_item_get_path(dest);
340         g_return_val_if_fail(destpath != NULL, NULL);
341
342         if (!is_dir_exist(destpath))
343                 make_dir_hier(destpath);
344
345         for (;;) {
346                 destfile = g_strdup_printf("%s%c%d", destpath, G_DIR_SEPARATOR,
347                                            dest->last_num + 1);
348                 if (is_file_entry_exist(destfile)) {
349                         dest->last_num++;
350                         g_free(destfile);
351                 } else
352                         break;
353         }
354
355         g_free(destpath);
356
357         return destfile;
358 }
359
360 #define SET_DEST_MSG_FLAGS(fp, dest, msginfo) \
361 { \
362         MsgInfo newmsginfo; \
363  \
364         newmsginfo.msgnum = dest->last_num; \
365         newmsginfo.flags = msginfo->flags; \
366         if (dest->stype == F_OUTBOX || \
367             dest->stype == F_QUEUE  || \
368             dest->stype == F_DRAFT  || \
369             dest->stype == F_TRASH) \
370                 MSG_UNSET_PERM_FLAGS(newmsginfo.flags, \
371                                      MSG_NEW|MSG_UNREAD|MSG_DELETED); \
372  \
373         procmsg_write_flags(&newmsginfo, fp); \
374 }
375
376 gint mh_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
377                 gboolean remove_source)
378 {
379         gchar *destfile;
380
381         g_return_val_if_fail(dest != NULL, -1);
382         g_return_val_if_fail(file != NULL, -1);
383
384         if (dest->last_num < 0) {
385                 mh_get_last_num(folder, dest);
386                 if (dest->last_num < 0) return -1;
387         }
388
389         destfile = mh_get_new_msg_filename(dest);
390         g_return_val_if_fail(destfile != NULL, -1);
391
392         if (link(file, destfile) < 0) {
393                 if (copy_file(file, destfile, TRUE) < 0) {
394                         g_warning("can't copy message %s to %s\n",
395                                   file, destfile);
396                         g_free(destfile);
397                         return -1;
398                 }
399         }
400
401         if (remove_source) {
402                 if (unlink(file) < 0)
403                         FILE_OP_ERROR(file, "unlink");
404         }
405
406         g_free(destfile);
407         dest->last_num++;
408         return dest->last_num;
409 }
410
411 static gint mh_do_move(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
412 {
413         gchar *srcfile;
414         gchar *destfile;
415         gint filemode = 0;
416         PrefsFolderItem *prefs;
417
418         g_return_val_if_fail(dest != NULL, -1);
419         g_return_val_if_fail(msginfo != NULL, -1);
420
421         if (msginfo->folder == dest) {
422                 g_warning("the src folder is identical to the dest.\n");
423                 return -1;
424         }
425
426         if (dest->last_num < 0) {
427                 mh_get_last_num(folder, dest);
428                 if (dest->last_num < 0) return -1;
429         }
430
431         prefs = dest->prefs;
432
433         destfile = mh_get_new_msg_filename(dest);
434         if (!destfile) return -1;
435
436         srcfile = procmsg_get_message_file(msginfo);
437         
438         debug_print("Moving message %s%c%d to %s ...\n",
439                     msginfo->folder->path, G_DIR_SEPARATOR,
440                     msginfo->msgnum, dest->path);
441
442         if (move_file(srcfile, destfile, FALSE) < 0) {
443                 g_free(srcfile);
444                 g_free(destfile);
445                 return -1;
446         }
447
448         if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
449                 if (chmod(destfile, prefs->folder_chmod) < 0)
450                         FILE_OP_ERROR(destfile, "chmod");
451
452                 /* for mark file */
453                 filemode = prefs->folder_chmod;
454                 if (filemode & S_IRGRP) filemode |= S_IWGRP;
455                 if (filemode & S_IROTH) filemode |= S_IWOTH;
456         }
457
458         g_free(srcfile);
459         g_free(destfile);
460         dest->last_num++;
461
462         return dest->last_num;
463 }
464
465 gint mh_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
466 {
467         gchar *srcfile;
468         gint ret = 0;
469  
470         g_return_val_if_fail(folder != NULL, -1);
471         g_return_val_if_fail(dest != NULL, -1);
472         g_return_val_if_fail(msginfo != NULL, -1);
473         g_return_val_if_fail(msginfo->folder != NULL, -1);
474  
475         if (folder == msginfo->folder->folder)
476                 return mh_do_move(folder, dest, msginfo);
477  
478         srcfile = procmsg_get_message_file(msginfo);
479         if (!srcfile) return -1;
480  
481         ret = mh_add_msg(folder, dest, srcfile, FALSE);
482         g_free(srcfile);
483  
484         if (ret != -1) {
485                 gchar *destdir;
486                 FILE *fp;
487  
488                 destdir = folder_item_get_path(dest);
489                 if ((fp = procmsg_open_mark_file(destdir, TRUE)) == NULL)
490                         g_warning("Can't open mark file.\n");
491                 else {
492                         SET_DEST_MSG_FLAGS(fp, dest, msginfo);
493                         fclose(fp);
494                 }
495                 g_free(destdir);
496  
497                 ret = folder_item_remove_msg(msginfo->folder, msginfo->msgnum);
498         }
499  
500         return ret;
501 }
502
503 static gint mh_do_move_msgs_with_dest(Folder *folder, FolderItem *dest,
504                                       GSList *msglist)
505 {
506         gchar *srcfile;
507         gchar *destfile;
508         GSList *cur;
509         MsgInfo *msginfo;
510         PrefsFolderItem *prefs;
511
512         g_return_val_if_fail(dest != NULL, -1);
513         g_return_val_if_fail(msglist != NULL, -1);
514
515         if (dest->last_num < 0) {
516                 mh_get_last_num(folder, dest);
517                 if (dest->last_num < 0) return -1;
518         }
519
520         prefs = dest->prefs;
521
522         for (cur = msglist; cur != NULL; cur = cur->next) {
523                 msginfo = (MsgInfo *)cur->data;
524
525                 if (msginfo->folder == dest) {
526                         g_warning("the src folder is identical to the dest.\n");
527                         continue;
528                 }
529                 debug_print("Moving message %s%c%d to %s ...\n",
530                             msginfo->folder->path, G_DIR_SEPARATOR,
531                             msginfo->msgnum, dest->path);
532
533                 destfile = mh_get_new_msg_filename(dest);
534                 if (!destfile) return -1;
535                 srcfile = procmsg_get_message_file(msginfo);
536
537                 if (move_file(srcfile, destfile, FALSE) < 0) {
538                         g_free(srcfile);
539                         g_free(destfile);
540                         break;
541                 }
542
543                 g_free(srcfile);
544                 g_free(destfile);
545                 dest->last_num++;
546         }
547
548         return dest->last_num;
549 }
550
551 gint mh_move_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
552 {
553         MsgInfo *msginfo;
554         GSList *cur;
555         gint ret = 0;
556
557         msginfo = (MsgInfo *)msglist->data;
558         if (folder == msginfo->folder->folder)
559                 return mh_do_move_msgs_with_dest(folder, dest, msglist);
560
561         for (cur = msglist; cur != NULL; cur = cur->next) {
562                 msginfo = (MsgInfo *)cur->data;
563                 ret = mh_move_msg(folder, dest, msginfo);
564                 if (ret == -1) break;
565         }
566
567         return ret;
568 }
569
570 gint mh_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
571 {
572         gchar *srcfile;
573         gchar *destfile;
574         gint filemode = 0;
575         PrefsFolderItem *prefs;
576
577         g_return_val_if_fail(dest != NULL, -1);
578         g_return_val_if_fail(msginfo != NULL, -1);
579
580         if (msginfo->folder == dest) {
581                 g_warning("the src folder is identical to the dest.\n");
582                 return -1;
583         }
584
585         if (dest->last_num < 0) {
586                 mh_get_last_num(folder, dest);
587                 if (dest->last_num < 0) return -1;
588         }
589
590         prefs = dest->prefs;
591
592         srcfile = procmsg_get_message_file(msginfo);
593         destfile = mh_get_new_msg_filename(dest);
594         if (!destfile) {
595                 g_free(srcfile);
596                 return -1;
597         }
598         
599         debug_print("Copying message %s%c%d to %s ...\n",
600                     msginfo->folder->path, G_DIR_SEPARATOR,
601                     msginfo->msgnum, dest->path);
602         
603
604         if ((MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags))
605         &&  dest->stype != F_QUEUE && dest->stype != F_DRAFT) {
606                 if (procmsg_remove_special_headers(srcfile, destfile) !=0) {
607                         g_free(srcfile);
608                         g_free(destfile);
609                         return -1;
610                 }
611         } else if (copy_file(srcfile, destfile, TRUE) < 0) {
612                 FILE_OP_ERROR(srcfile, "copy");
613                 g_free(srcfile);
614                 g_free(destfile);
615                 return -1;
616         }
617
618
619         if (prefs && prefs->enable_folder_chmod && prefs->folder_chmod) {
620                 if (chmod(destfile, prefs->folder_chmod) < 0)
621                         FILE_OP_ERROR(destfile, "chmod");
622
623                 /* for mark file */
624                 filemode = prefs->folder_chmod;
625                 if (filemode & S_IRGRP) filemode |= S_IWGRP;
626                 if (filemode & S_IROTH) filemode |= S_IWOTH;
627         }
628
629         g_free(srcfile);
630         g_free(destfile);
631         dest->last_num++;
632
633         return dest->last_num;
634 }
635
636 /*
637 gint mh_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
638 {
639         Folder * src_folder;
640         gchar * filename;
641         gint num;
642         gchar * destdir;
643         FILE * fp;
644
645         src_folder = msginfo->folder->folder;
646         
647         g_return_val_if_fail(src_folder->fetch_msg != NULL, -1);
648         
649         filename = src_folder->fetch_msg(src_folder,
650                                          msginfo->folder,
651                                          msginfo->msgnum);
652         if (filename == NULL)
653                 return -1;
654
655         num = folder->add_msg(folder, dest, filename, FALSE);
656
657         destdir = folder_item_get_path(dest);
658
659         if (fp) {
660                 MsgInfo newmsginfo;
661
662                 newmsginfo.msgnum = dest->last_num;
663                 newmsginfo.flags = msginfo->flags;
664                 if (dest->stype == F_OUTBOX ||
665                     dest->stype == F_QUEUE  ||
666                     dest->stype == F_DRAFT  ||
667                     dest->stype == F_TRASH)
668                         MSG_UNSET_FLAGS(newmsginfo.flags,
669                                         MSG_NEW|MSG_UNREAD|MSG_DELETED);
670
671                 procmsg_write_flags(&newmsginfo, fp);
672                 fclose(fp);
673         }
674         
675         return num;
676 }
677 */
678
679 gint mh_copy_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
680 {
681         gchar *srcfile;
682         gchar *destfile;
683         GSList *cur;
684         MsgInfo *msginfo;
685
686         g_return_val_if_fail(dest != NULL, -1);
687         g_return_val_if_fail(msglist != NULL, -1);
688
689         if (dest->last_num < 0) {
690                 mh_get_last_num(folder, dest);
691                 if (dest->last_num < 0) return -1;
692         }
693
694         for (cur = msglist; cur != NULL; cur = cur->next) {
695                 msginfo = (MsgInfo *)cur->data;
696
697                 if (msginfo->folder == dest) {
698                         g_warning("the src folder is identical to the dest.\n");
699                         continue;
700                 }
701                 debug_print("Copying message %s%c%d to %s ...\n",
702                             msginfo->folder->path, G_DIR_SEPARATOR,
703                             msginfo->msgnum, dest->path);
704
705                 destfile = mh_get_new_msg_filename(dest);
706                 if (!destfile) break;
707                 srcfile = procmsg_get_message_file(msginfo);
708
709                 if (copy_file(srcfile, destfile, TRUE) < 0) {
710                         FILE_OP_ERROR(srcfile, "copy");
711                         g_free(srcfile);
712                         g_free(destfile);
713                         break;
714                 }
715
716                 g_free(srcfile);
717                 g_free(destfile);
718                 dest->last_num++;
719         }
720
721         return dest->last_num;
722 }
723
724 gint mh_remove_msg(Folder *folder, FolderItem *item, gint num)
725 {
726         gchar *file;
727
728         g_return_val_if_fail(item != NULL, -1);
729
730         file = mh_fetch_msg(folder, item, num);
731         g_return_val_if_fail(file != NULL, -1);
732
733         if (unlink(file) < 0) {
734                 FILE_OP_ERROR(file, "unlink");
735                 g_free(file);
736                 return -1;
737         }
738
739         g_free(file);
740         return 0;
741 }
742
743 gint mh_remove_all_msg(Folder *folder, FolderItem *item)
744 {
745         gchar *path;
746         gint val;
747
748         g_return_val_if_fail(item != NULL, -1);
749
750         path = folder_item_get_path(item);
751         g_return_val_if_fail(path != NULL, -1);
752         val = remove_all_numbered_files(path);
753         g_free(path);
754
755         return val;
756 }
757
758 gboolean mh_is_msg_changed(Folder *folder, FolderItem *item, MsgInfo *msginfo)
759 {
760         struct stat s;
761
762         if (stat(itos(msginfo->msgnum), &s) < 0 ||
763             msginfo->size  != s.st_size ||
764             msginfo->mtime != s.st_mtime)
765                 return TRUE;
766
767         return FALSE;
768 }
769
770 gint mh_scan_folder(Folder *folder, FolderItem *item)
771 {
772         gchar *path;
773         DIR *dp;
774         struct dirent *d;
775         struct stat s;
776         gint max = 0;
777         gint num;
778
779         g_return_val_if_fail(item != NULL, -1);
780
781         debug_print("mh_scan_folder(): Scanning %s ...\n", item->path);
782
783         path = folder_item_get_path(item);
784         g_return_val_if_fail(path != NULL, -1);
785         if (change_dir(path) < 0) {
786                 g_free(path);
787                 return -1;
788         }
789         g_free(path);
790
791         if ((dp = opendir(".")) == NULL) {
792                 FILE_OP_ERROR(item->path, "opendir");
793                 return -1;
794         }
795
796         if (folder->ui_func)
797                 folder->ui_func(folder, item, folder->ui_func_data);
798
799         while ((d = readdir(dp)) != NULL) {
800                 if ((num = to_number(d->d_name)) >= 0 &&
801                     stat(d->d_name, &s) == 0 &&
802                     S_ISREG(s.st_mode)) {
803 /*
804                         n_msg++;
805 */
806                         if (max < num)
807                                 max = num;
808                 }
809         }
810         closedir(dp);
811
812 /*
813         if (n_msg == 0)
814                 item->new = item->unread = item->total = 0;
815         else {
816                 gint new, unread, total, min, max;
817
818                 procmsg_get_mark_sum(".", &new, &unread, &total, &min, &max, 0);
819                 if (n_msg > total) {
820                         new += n_msg - total;
821                         unread += n_msg - total;
822                 }
823                 item->new = new;
824                 item->unread = unread;
825                 item->total = n_msg;
826         }
827 */
828         debug_print("Last number in dir %s = %d\n", item->path, max);
829         item->last_num = max;
830
831         return 0;
832 }
833
834 void mh_scan_tree(Folder *folder)
835 {
836         FolderItem *item;
837         gchar *rootpath;
838
839         g_return_if_fail(folder != NULL);
840
841         item = folder_item_new(folder, folder->name, NULL);
842         item->folder = folder;
843         folder->node = g_node_new(item);
844
845         rootpath = folder_item_get_path(item);
846         if (change_dir(rootpath) < 0) {
847                 g_free(rootpath);
848                 return;
849         }
850         g_free(rootpath);
851
852         mh_create_tree(folder);
853         mh_scan_tree_recursive(item);
854 }
855
856 #define MAKE_DIR_IF_NOT_EXIST(dir) \
857 { \
858         if (!is_dir_exist(dir)) { \
859                 if (is_file_exist(dir)) { \
860                         g_warning("File `%s' already exists.\n" \
861                                     "Can't create folder.", dir); \
862                         return -1; \
863                 } \
864                 if (make_dir(dir) < 0) \
865                         return -1; \
866         } \
867 }
868
869 gint mh_create_tree(Folder *folder)
870 {
871         gchar *rootpath;
872
873         g_return_val_if_fail(folder != NULL, -1);
874
875         CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
876         rootpath = LOCAL_FOLDER(folder)->rootpath;
877         MAKE_DIR_IF_NOT_EXIST(rootpath);
878         CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
879         MAKE_DIR_IF_NOT_EXIST(INBOX_DIR);
880         MAKE_DIR_IF_NOT_EXIST(OUTBOX_DIR);
881         MAKE_DIR_IF_NOT_EXIST(QUEUE_DIR);
882         MAKE_DIR_IF_NOT_EXIST(DRAFT_DIR);
883         MAKE_DIR_IF_NOT_EXIST(TRASH_DIR);
884
885         return 0;
886 }
887
888 #undef MAKE_DIR_IF_NOT_EXIST
889
890 FolderItem *mh_create_folder(Folder *folder, FolderItem *parent,
891                              const gchar *name)
892 {
893         gchar *path;
894         gchar *fullpath;
895         FolderItem *new_item;
896         gchar *mh_sequences_filename;
897         FILE *mh_sequences_file;
898
899         g_return_val_if_fail(folder != NULL, NULL);
900         g_return_val_if_fail(parent != NULL, NULL);
901         g_return_val_if_fail(name != NULL, NULL);
902
903         path = folder_item_get_path(parent);
904         if (!is_dir_exist(path)) 
905                 if (make_dir_hier(path) != 0)
906                         return NULL;
907                 
908         fullpath = g_strconcat(path, G_DIR_SEPARATOR_S, name, NULL);
909         g_free(path);
910
911         if (make_dir(fullpath) < 0) {
912                 g_free(fullpath);
913                 return NULL;
914         }
915
916         g_free(fullpath);
917
918         if (parent->path)
919                 path = g_strconcat(parent->path, G_DIR_SEPARATOR_S, name,
920                                    NULL);
921         else
922                 path = g_strdup(name);
923         new_item = folder_item_new(folder, name, path);
924         folder_item_append(parent, new_item);
925
926         g_free(path);
927
928         path = folder_item_get_path(new_item);
929         mh_sequences_filename = g_strconcat(path, G_DIR_SEPARATOR_S,
930                                             ".mh_sequences", NULL);
931         if ((mh_sequences_file = fopen(mh_sequences_filename, "a+b")) != NULL) {
932                 fclose(mh_sequences_file);
933         }
934         g_free(mh_sequences_filename);
935         g_free(path);
936
937         return new_item;
938 }
939
940 gint mh_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
941 {
942         gchar *oldpath;
943         gchar *dirname;
944         gchar *newpath;
945         GNode *node;
946         gchar *paths[2];
947
948         g_return_val_if_fail(folder != NULL, -1);
949         g_return_val_if_fail(item != NULL, -1);
950         g_return_val_if_fail(item->path != NULL, -1);
951         g_return_val_if_fail(name != NULL, -1);
952
953         oldpath = folder_item_get_path(item);
954         if (!is_dir_exist(oldpath))
955                 make_dir_hier(oldpath);
956
957         dirname = g_dirname(oldpath);
958         newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, name, NULL);
959         g_free(dirname);
960
961         if (rename(oldpath, newpath) < 0) {
962                 FILE_OP_ERROR(oldpath, "rename");
963                 g_free(oldpath);
964                 g_free(newpath);
965                 return -1;
966         }
967
968         g_free(oldpath);
969         g_free(newpath);
970
971         if (strchr(item->path, G_DIR_SEPARATOR) != NULL) {
972                 dirname = g_dirname(item->path);
973                 newpath = g_strconcat(dirname, G_DIR_SEPARATOR_S, name, NULL);
974                 g_free(dirname);
975         } else
976                 newpath = g_strdup(name);
977
978         g_free(item->name);
979         item->name = g_strdup(name);
980
981         node = g_node_find(item->folder->node, G_PRE_ORDER, G_TRAVERSE_ALL,
982                            item);
983         paths[0] = g_strdup(item->path);
984         paths[1] = newpath;
985         g_node_traverse(node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
986                         mh_rename_folder_func, paths);
987
988         g_free(paths[0]);
989         g_free(paths[1]);
990         return 0;
991 }
992
993 gint mh_remove_folder(Folder *folder, FolderItem *item)
994 {
995         gchar *path;
996
997         g_return_val_if_fail(folder != NULL, -1);
998         g_return_val_if_fail(item != NULL, -1);
999         g_return_val_if_fail(item->path != NULL, -1);
1000
1001         path = folder_item_get_path(item);
1002         if (remove_dir_recursive(path) < 0) {
1003                 g_warning("can't remove directory `%s'\n", path);
1004                 g_free(path);
1005                 return -1;
1006         }
1007
1008         g_free(path);
1009         folder_item_remove(item);
1010         return 0;
1011 }
1012
1013
1014 static GSList *mh_get_uncached_msgs(GHashTable *msg_table, FolderItem *item)
1015 {
1016         gchar *path;
1017         DIR *dp;
1018         struct dirent *d;
1019         struct stat s;
1020         GSList *newlist = NULL;
1021         GSList *last = NULL;
1022         MsgInfo *msginfo;
1023         gint n_newmsg = 0;
1024         gint num;
1025
1026         g_return_val_if_fail(item != NULL, NULL);
1027
1028         path = folder_item_get_path(item);
1029         g_return_val_if_fail(path != NULL, NULL);
1030         if (change_dir(path) < 0) {
1031                 g_free(path);
1032                 return NULL;
1033         }
1034         g_free(path);
1035
1036         if ((dp = opendir(".")) == NULL) {
1037                 FILE_OP_ERROR(item->path, "opendir");
1038                 return NULL;
1039         }
1040
1041         debug_print("Searching uncached messages...\n");
1042
1043         if (msg_table) {
1044                 while ((d = readdir(dp)) != NULL) {
1045                         if ((num = to_number(d->d_name)) < 0) continue;
1046                         if (stat(d->d_name, &s) < 0) {
1047                                 FILE_OP_ERROR(d->d_name, "stat");
1048                                 continue;
1049                         }
1050                         if (!S_ISREG(s.st_mode)) continue;
1051
1052                         msginfo = g_hash_table_lookup
1053                                 (msg_table, GUINT_TO_POINTER(num));
1054
1055                         if (!msginfo) {
1056                                 /* not found in the cache (uncached message) */
1057                                 msginfo = mh_parse_msg(d->d_name, item);
1058                                 if (!msginfo) continue;
1059
1060                                 if (!newlist)
1061                                         last = newlist =
1062                                                 g_slist_append(NULL, msginfo);
1063                                 else {
1064                                         last = g_slist_append(last, msginfo);
1065                                         last = last->next;
1066                                 }
1067                                 n_newmsg++;
1068                         }
1069                 }
1070         } else {
1071                 /* discard all previous cache */
1072                 while ((d = readdir(dp)) != NULL) {
1073                         if (to_number(d->d_name) < 0) continue;
1074                         if (stat(d->d_name, &s) < 0) {
1075                                 FILE_OP_ERROR(d->d_name, "stat");
1076                                 continue;
1077                         }
1078                         if (!S_ISREG(s.st_mode)) continue;
1079
1080                         msginfo = mh_parse_msg(d->d_name, item);
1081                         if (!msginfo) continue;
1082
1083                         if (!newlist)
1084                                 last = newlist = g_slist_append(NULL, msginfo);
1085                         else {
1086                                 last = g_slist_append(last, msginfo);
1087                                 last = last->next;
1088                         }
1089                         n_newmsg++;
1090                 }
1091         }
1092
1093         closedir(dp);
1094
1095         if (n_newmsg)
1096                 debug_print("%d uncached message(s) found.\n", n_newmsg);
1097         else
1098                 debug_print("done.\n");
1099
1100         /* sort new messages in numerical order */
1101         if (newlist) {
1102                 debug_print("Sorting uncached messages in numerical order...\n");
1103                 newlist = g_slist_sort
1104                         (newlist, (GCompareFunc)procmsg_cmp_msgnum_for_sort);
1105                 debug_print("done.\n");
1106         }
1107
1108         return newlist;
1109 }
1110
1111 static MsgInfo *mh_parse_msg(const gchar *file, FolderItem *item)
1112 {
1113         struct stat s;
1114         MsgInfo *msginfo;
1115         MsgFlags flags;
1116
1117         flags.perm_flags = MSG_NEW|MSG_UNREAD;
1118         flags.tmp_flags = 0;
1119
1120         g_return_val_if_fail(item != NULL, NULL);
1121         g_return_val_if_fail(file != NULL, NULL);
1122
1123         if (item->stype == F_QUEUE) {
1124                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
1125         } else if (item->stype == F_DRAFT) {
1126                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
1127         }
1128
1129         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
1130         if (!msginfo) return NULL;
1131
1132         msginfo->msgnum = atoi(file);
1133         msginfo->folder = item;
1134
1135         if (stat(file, &s) < 0) {
1136                 FILE_OP_ERROR(file, "stat");
1137                 msginfo->size = 0;
1138                 msginfo->mtime = 0;
1139         } else {
1140                 msginfo->size = s.st_size;
1141                 msginfo->mtime = s.st_mtime;
1142         }
1143
1144         return msginfo;
1145 }
1146
1147 #if 0
1148 static gboolean mh_is_maildir_one(const gchar *path, const gchar *dir)
1149 {
1150         gchar *entry;
1151         gboolean result;
1152
1153         entry = g_strconcat(path, G_DIR_SEPARATOR_S, dir, NULL);
1154         result = is_dir_exist(entry);
1155         g_free(entry);
1156
1157         return result;
1158 }
1159
1160 /*
1161  * check whether PATH is a Maildir style mailbox.
1162  * This is the case if the 3 subdir: new, cur, tmp are existing.
1163  * This functon assumes that entry is an directory
1164  */
1165 static gboolean mh_is_maildir(const gchar *path)
1166 {
1167         return mh_is_maildir_one(path, "new") &&
1168                mh_is_maildir_one(path, "cur") &&
1169                mh_is_maildir_one(path, "tmp");
1170 }
1171 #endif
1172
1173 static void mh_scan_tree_recursive(FolderItem *item)
1174 {
1175         DIR *dp;
1176         struct dirent *d;
1177         struct stat s;
1178         gchar *entry;
1179         gint n_msg = 0;
1180
1181         g_return_if_fail(item != NULL);
1182         g_return_if_fail(item->folder != NULL);
1183
1184         dp = opendir(item->path ? item->path : ".");
1185         if (!dp) {
1186                 FILE_OP_ERROR(item->path ? item->path : ".", "opendir");
1187                 return;
1188         }
1189
1190         debug_print("scanning %s ...\n",
1191                     item->path ? item->path
1192                     : LOCAL_FOLDER(item->folder)->rootpath);
1193         if (item->folder->ui_func)
1194                 item->folder->ui_func(item->folder, item,
1195                                       item->folder->ui_func_data);
1196
1197         while ((d = readdir(dp)) != NULL) {
1198                 if (d->d_name[0] == '.') continue;
1199
1200                 if (item->path)
1201                         entry = g_strconcat(item->path, G_DIR_SEPARATOR_S,
1202                                             d->d_name, NULL);
1203                 else
1204                         entry = g_strdup(d->d_name);
1205
1206                 if (stat(entry, &s) < 0) {
1207                         FILE_OP_ERROR(entry, "stat");
1208                         g_free(entry);
1209                         continue;
1210                 }
1211
1212                 if (S_ISDIR(s.st_mode)) {
1213                         FolderItem *new_item;
1214
1215 #if 0
1216                         if (mh_is_maildir(entry)) {
1217                                 g_free(entry);
1218                                 continue;
1219                         }
1220 #endif
1221
1222                         new_item = folder_item_new(item->folder, d->d_name, entry);
1223                         folder_item_append(item, new_item);
1224                         if (!item->path) {
1225                                 if (!strcmp(d->d_name, INBOX_DIR)) {
1226                                         new_item->stype = F_INBOX;
1227                                         item->folder->inbox = new_item;
1228                                 } else if (!strcmp(d->d_name, OUTBOX_DIR)) {
1229                                         new_item->stype = F_OUTBOX;
1230                                         item->folder->outbox = new_item;
1231                                 } else if (!strcmp(d->d_name, DRAFT_DIR)) {
1232                                         new_item->stype = F_DRAFT;
1233                                         item->folder->draft = new_item;
1234                                 } else if (!strcmp(d->d_name, QUEUE_DIR)) {
1235                                         new_item->stype = F_QUEUE;
1236                                         item->folder->queue = new_item;
1237                                 } else if (!strcmp(d->d_name, TRASH_DIR)) {
1238                                         new_item->stype = F_TRASH;
1239                                         item->folder->trash = new_item;
1240                                 }
1241                         }
1242                         mh_scan_tree_recursive(new_item);
1243                 } else if (to_number(d->d_name) != -1) n_msg++;
1244
1245                 g_free(entry);
1246         }
1247
1248         closedir(dp);
1249
1250 /*
1251         if (item->path) {
1252                 gint new, unread, total, min, max;
1253
1254                 procmsg_get_mark_sum(item->path, &new, &unread, &total,
1255                                      &min, &max, 0);
1256                 if (n_msg > total) {
1257                         new += n_msg - total;
1258                         unread += n_msg - total;
1259                 }
1260                 item->new = new;
1261                 item->unread = unread;
1262                 item->total = n_msg;
1263         }
1264 */
1265 }
1266
1267 static gboolean mh_rename_folder_func(GNode *node, gpointer data)
1268 {
1269         FolderItem *item = node->data;
1270         gchar **paths = data;
1271         const gchar *oldpath = paths[0];
1272         const gchar *newpath = paths[1];
1273         gchar *base;
1274         gchar *new_itempath;
1275         gint oldpathlen;
1276
1277         oldpathlen = strlen(oldpath);
1278         if (strncmp(oldpath, item->path, oldpathlen) != 0) {
1279                 g_warning("path doesn't match: %s, %s\n", oldpath, item->path);
1280                 return TRUE;
1281         }
1282
1283         base = item->path + oldpathlen;
1284         while (*base == G_DIR_SEPARATOR) base++;
1285         if (*base == '\0')
1286                 new_itempath = g_strdup(newpath);
1287         else
1288                 new_itempath = g_strconcat(newpath, G_DIR_SEPARATOR_S, base,
1289                                            NULL);
1290         g_free(item->path);
1291         item->path = new_itempath;
1292
1293         return FALSE;
1294 }