2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2001 Hiroyuki Yamamoto & The Sylpheed Claws Team
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.
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.
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.
25 #include <sys/types.h>
29 #include "mbox_folder.h"
32 #include "procheader.h"
36 #define MSGBUFSIZE 8192
38 static void mbox_folder_init (Folder *folder,
42 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
43 gchar * new_filename, gint size);
44 static gboolean mbox_rewrite(gchar * mbox);
45 static gboolean mbox_purge_deleted(gchar * mbox);
46 static gchar * mbox_get_new_path(FolderItem * parent, gchar * name);
47 static gchar * mbox_get_folderitem_name(gchar * name);
49 MsgInfo *mbox_get_msginfo(Folder *folder, FolderItem *item, gint num);
50 gint mbox_get_num_list(Folder *folder, FolderItem *item, GSList **list);
51 gboolean mbox_check_msgnum_validity(Folder *folder, FolderItem *item);
53 Folder *mbox_folder_new(const gchar *name, const gchar *path)
57 folder = (Folder *)g_new0(MBOXFolder, 1);
58 mbox_folder_init(folder, name, path);
63 void mbox_folder_destroy(Folder *folder)
65 folder_local_folder_destroy(LOCAL_FOLDER(folder));
68 static void mbox_folder_init(Folder *folder, const gchar *name, const gchar *path)
70 folder->type = F_MBOX;
72 folder_local_folder_init(folder, name, path);
75 folder->get_msg_list = mbox_get_msg_list;
77 folder->fetch_msg = mbox_fetch_msg;
78 folder->get_msginfo = mbox_get_msginfo;
79 folder->add_msg = mbox_add_msg;
80 folder->copy_msg = mbox_copy_msg;
81 folder->remove_msg = mbox_remove_msg;
82 folder->remove_all_msg = mbox_remove_all_msg;
84 folder->scan = mbox_scan_folder;
86 folder->get_num_list = mbox_get_num_list;
87 folder->create_tree = mbox_create_tree;
88 folder->create_folder = mbox_create_folder;
89 folder->rename_folder = mbox_rename_folder;
90 folder->remove_folder = mbox_remove_folder;
91 folder->destroy = mbox_folder_destroy;
92 folder->check_msgnum_validity
93 = mbox_check_msgnum_validity;
96 static void mbox_folder_create_parent(const gchar * path)
98 if (!is_file_exist(path)) {
101 new_path = g_dirname(path);
102 if (new_path[strlen(new_path) - 1] == G_DIR_SEPARATOR)
103 new_path[strlen(new_path) - 1] = '\0';
105 if (!is_dir_exist(new_path))
106 make_dir_hier(new_path);
113 static gchar *mbox_folder_get_path(FolderItem *item)
118 g_return_val_if_fail(item != NULL, NULL);
120 if (item->path && item->path[0] == G_DIR_SEPARATOR) {
121 mbox_folder_create_parent(item->path);
122 return g_strdup(item->path);
125 folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
126 g_return_val_if_fail(folder_path != NULL, NULL);
128 if (folder_path[0] == G_DIR_SEPARATOR) {
130 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
134 path = g_strdup(folder_path);
137 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
138 folder_path, G_DIR_SEPARATOR_S,
141 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
147 mbox_folder_create_parent(path);
153 /**********************************************************/
157 /**********************************************************/
160 static GSList * file_lock = NULL;
162 static gboolean mbox_file_lock_file(gchar * base)
164 gchar *lockfile, *locklink;
168 lockfile = g_strdup_printf("%s.%d", base, getpid());
169 if ((lockfp = fopen(lockfile, "wb")) == NULL) {
170 FILE_OP_ERROR(lockfile, "fopen");
171 g_warning("can't create lock file %s\n", lockfile);
172 g_warning("use 'flock' instead of 'file' if possible.\n");
177 fprintf(lockfp, "%d\n", getpid());
180 locklink = g_strconcat(base, ".lock", NULL);
181 while (link(lockfile, locklink) < 0) {
182 FILE_OP_ERROR(lockfile, "link");
184 g_warning("can't create %s\n", lockfile);
191 g_warning("mailbox is owned by another"
192 " process, waiting...\n");
203 static gboolean mbox_fcntl_lockwrite_file(FILE * fp)
207 lck.l_type = F_WRLCK;
212 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
218 static gboolean mbox_fcntl_lockread_file(FILE * fp)
222 lck.l_type = F_RDLCK;
227 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
233 static gboolean mbox_fcntl_unlock_file(FILE * fp)
237 lck.l_type = F_UNLCK;
242 if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
248 static gboolean mbox_file_unlock_file(gchar * base)
252 lockfile = g_strdup_printf("%s.lock", base);
259 static gboolean mbox_lockread_file(FILE * fp, gchar * base)
263 result = mbox_fcntl_lockread_file(fp);
265 if ((result = mbox_file_lock_file(base)) == TRUE) {
266 file_lock = g_slist_append(file_lock, g_strdup(base));
267 debug_print("lockfile lock %s.\n", base);
270 g_warning("could not lock read file %s\n", base);
273 debug_print("fcntl lock %s.\n", base);
278 static gboolean mbox_lockwrite_file(FILE * fp, gchar * base)
282 result = mbox_fcntl_lockwrite_file(fp);
284 if ((result = mbox_file_lock_file(base)) == TRUE) {
285 file_lock = g_slist_append(file_lock, g_strdup(base));
286 debug_print("lockfile lock %s.\n", base);
289 g_warning("could not lock write file %s\n", base);
292 debug_print("fcntl lock %s.\n", base);
297 static gboolean mbox_unlock_file(FILE * fp, gchar * base)
299 gboolean result = FALSE;
301 gboolean unlocked = FALSE;
303 for(l = file_lock ; l != NULL ; l = g_slist_next(l)) {
304 gchar * data = l->data;
306 if (strcmp(data, base) == 0) {
307 file_lock = g_slist_remove(file_lock, data);
309 result = mbox_file_unlock_file(base);
311 debug_print("lockfile unlock - %s.\n", base);
317 result = mbox_fcntl_unlock_file(fp);
318 debug_print("fcntl unlock - %s.\n", base);
324 /**********************************************************/
328 /**********************************************************/
330 #define MAILFILE_ERROR_NO_ERROR 0x000
331 #define MAILFILE_ERROR_FILE_NOT_FOUND 0x001
332 #define MAILFILE_ERROR_MEMORY 0x002
333 #define MAILFILE_ERROR_MESSAGE_NOT_FOUND 0x003
335 static int mailfile_error = MAILFILE_ERROR_NO_ERROR;
337 #define STATE_BEGIN 0x000
338 #define STATE_TEXT_READ 0x001
339 #define STATE_FROM_READ 0x002
340 #define STATE_FIELD_READ 0x003
341 #define STATE_END 0x004
342 #define STATE_END_OF_FILE 0x005
343 #define STATE_MEM_ERROR 0x006
344 #define STATE_TEXT_BEGIN 0x007
346 #define STATE_MASK 0x0FF /* filter state from functions */
348 #define STATE_RESTORE_POS 0x100 /* go back while reading */
350 typedef struct _mailfile mailfile;
374 #define MSG_IS_INVALID(msg) \
375 ((msg).perm_flags == (msg).tmp_flags && (msg).tmp_flags == -1)
377 #define MSG_SET_INVALID(msg) \
378 ((msg).perm_flags = (msg).tmp_flags = -1)
380 static int startFrom(char * s)
382 return (strncmp(s, "From ", 5) == 0);
385 static int startSpace(char * s)
387 return ((*s == ' ') || (*s == '\t'));
390 static int startEmpty(char * s)
395 static void free_msg_list(GList * l)
397 GList * elt = g_list_first(l);
402 elt = g_list_next(elt);
409 static mailfile * mailfile_init_from_file(FILE * f, gchar * filename)
412 GList * msg_list = NULL;
419 struct _message * data = NULL;
425 while (state != STATE_END_OF_FILE) {
426 if ((state & STATE_RESTORE_POS) == 0) {
427 former_pos = lastpos;
430 r = fgets(s, 256, f);
433 ignore_next = (s[strlen(s) - 1] != '\n');
438 switch(state & 0x0F) {
441 state = STATE_END_OF_FILE;
442 else if (startFrom(s)) {
443 state = STATE_FROM_READ;
445 data = g_new0(struct _message, 1);
447 free_msg_list(msg_list);
452 data->msgnum = msgnum;
453 data->offset = lastpos;
454 data->header = lastpos;
457 data->messageid = NULL;
458 data->fromspace = NULL;
459 MSG_SET_INVALID(data->flags);
460 MSG_SET_INVALID(data->old_flags);
461 data->fetched = FALSE;
462 msg_list = g_list_append(msg_list,
470 case STATE_TEXT_READ:
473 else if (startFrom(s))
474 state = STATE_END | STATE_RESTORE_POS;
476 state = STATE_TEXT_READ;
479 case STATE_TEXT_BEGIN:
480 data->content = lastpos;
483 else if (startFrom(s)) {
484 state = STATE_END | STATE_RESTORE_POS;
487 state = STATE_TEXT_READ;
491 case STATE_FROM_READ:
492 data->content = lastpos;
495 else if (startSpace(s))
496 state = STATE_FROM_READ;
497 else if (startEmpty(s))
498 state = STATE_TEXT_READ;
500 state = STATE_FIELD_READ;
503 case STATE_FIELD_READ:
504 data->content = lastpos;
507 else if (startSpace(s))
508 state = STATE_FIELD_READ;
509 else if (startEmpty(s)) {
510 state = STATE_TEXT_BEGIN;
513 state = STATE_FIELD_READ;
517 if ((state & STATE_MASK) == STATE_END) {
518 state = STATE_BEGIN | (state & STATE_RESTORE_POS);
524 r = fgets(s, 256, f);
525 if (r == NULL || *r == '\0')
528 while (s[strlen(s) - 1] != '\n');
532 mf = (mailfile *) g_new0(struct _mailfile, 1);
534 free_msg_list(msg_list);
535 mailfile_error = MAILFILE_ERROR_MEMORY;
539 mf->msg_list = g_list_first(msg_list);
541 mf->filename = g_strdup(filename);
544 mailfile_error = MAILFILE_ERROR_NO_ERROR;
549 static mailfile * mailfile_init(char * filename)
555 f = fopen(filename, "rb");
558 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
562 mbox_lockread_file(f, filename);
564 mf = mailfile_init_from_file(f, filename);
566 mbox_unlock_file(f, filename);
573 static void mailfile_done(mailfile * f)
575 free_msg_list(f->msg_list);
582 #define MAX_READ 4096
584 static char * readfile(char * filename, int offset, int max_offset)
593 handle = fopen(filename, "rb");
595 if (handle == NULL) {
596 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
600 size = max_offset - offset;
602 message = (char *) malloc(size + 1);
603 if (message == NULL) {
605 mailfile_error = MAILFILE_ERROR_MEMORY;
609 fseek(handle, offset, SEEK_SET);
613 if ((size - pos) > MAX_READ)
618 bread = fread(message + pos, 1, max, handle);
634 static char * mailfile_readmsg(mailfile f, int index)
640 struct _message * msginfo;
642 nth = g_list_nth(f->msg_list, index);
645 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
649 msginfo = (struct _message *)nth->data;
651 offset = msginfo->offset;
652 max_offset = msginfo->end;
653 message = readfile(f->filename, offset, max_offset);
655 mailfile_error = MAILFILE_ERROR_NO_ERROR;
660 static char * mailfile_readheader(mailfile f, int index)
666 struct _message * msginfo;
668 nth = g_list_nth(f->msg_list, index);
671 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
675 msginfo = (struct _message *)nth->data;
677 offset = msginfo->offset;
678 max_offset = msginfo->content;
679 message = readfile(f->filename, offset, max_offset);
681 mailfile_error = MAILFILE_ERROR_NO_ERROR;
686 static int mailfile_count(mailfile * f)
688 return g_list_length(f->msg_list);
691 static int mailfile_find_deleted(mailfile f, char * filename)
695 handle = fopen(filename, "rb");
698 struct _message m = elt->data;
699 n = fread(&m.deleted, sizeof(int), 1, handle);
702 elt = g_list_next(elt);
711 /**********************************************************/
713 /* mbox cache operations */
715 /**********************************************************/
722 gboolean modification;
725 typedef struct _mboxcache mboxcache;
727 static GHashTable * mbox_cache_table = NULL;
729 static MsgInfo *mbox_parse_msg(FILE * fp, struct _message * msg,
733 MsgFlags flags = { 0, 0 };
735 MSG_SET_PERM_FLAGS(flags, MSG_NEW | MSG_UNREAD);
737 g_return_val_if_fail(fp != NULL, NULL);
740 if (item->stype == F_QUEUE) {
741 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
742 } else if (item->stype == F_DRAFT) {
743 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
747 msginfo = procheader_parse_stream(fp, flags, FALSE, FALSE);
749 if (!msginfo) return NULL;
752 msginfo->msgnum = msg->msgnum;
753 msginfo->folder = item;
759 static void mbox_cache_init()
761 mbox_cache_table = g_hash_table_new(g_str_hash, g_str_equal);
764 static void mbox_cache_free_mbox(mboxcache * cache)
766 g_hash_table_remove(mbox_cache_table, cache->filename);
769 mailfile_done(cache->mf);
771 g_ptr_array_free(cache->tab_mf, FALSE);
773 g_free(cache->filename);
777 static void mbox_cache_get_msginfo_from_file(FILE * fp, GList * msg_list)
782 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
783 struct _message * msg;
785 msg = (struct _message *) l->data;
787 fseek(fp, msg->header, SEEK_SET);
788 msginfo = mbox_parse_msg(fp, msg, NULL);
792 g_strdup(msginfo->msgid);
793 if (msginfo->fromspace)
795 g_strdup(msginfo->fromspace);
796 msg->flags = msginfo->flags;
797 msg->old_flags = msginfo->flags;
799 procmsg_msginfo_free(msginfo);
804 static void mbox_cache_get_msginfo(gchar * filename, GList * msg_list)
808 fp = fopen(filename, "rb");
812 mbox_cache_get_msginfo_from_file(fp, msg_list);
816 static mboxcache * mbox_cache_read_mbox(gchar * filename)
823 if (stat(filename, &s) < 0)
826 mf = mailfile_init(filename);
830 cache = g_new0(mboxcache, 1);
832 cache->mtime = s.st_mtime;
834 cache->filename = g_strdup(filename);
835 cache->modification = FALSE;
837 cache->tab_mf = g_ptr_array_new();
838 for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
839 g_ptr_array_add(cache->tab_mf, l->data);
841 mbox_cache_get_msginfo(filename, mf->msg_list);
843 debug_print("read mbox - %s\n", filename);
848 static mboxcache * mbox_cache_read_mbox_from_file(FILE * fp, gchar * filename)
855 if (stat(filename, &s) < 0)
858 mf = mailfile_init_from_file(fp, filename);
862 cache = g_new0(mboxcache, 1);
864 cache->mtime = s.st_mtime;
866 cache->filename = g_strdup(filename);
868 cache->tab_mf = g_ptr_array_new();
869 for(l = mf->msg_list ; l != NULL ; l = g_list_next(l))
870 g_ptr_array_add(cache->tab_mf, l->data);
872 mbox_cache_get_msginfo_from_file(fp, mf->msg_list);
874 debug_print("read mbox from file - %s\n", filename);
879 static void mbox_cache_insert_mbox(mboxcache * data)
881 if (mbox_cache_table == NULL)
884 g_hash_table_insert(mbox_cache_table, data->filename, data);
887 static mboxcache * mbox_cache_get_mbox(gchar * filename)
889 if (mbox_cache_table == NULL)
892 return g_hash_table_lookup(mbox_cache_table, filename);
896 static gint mbox_cache_get_count(gchar * filename)
900 cache = mbox_cache_get_mbox(filename);
903 if (cache->mf == NULL)
905 return cache->mf->count;
908 static GList * mbox_cache_get_msg_list(gchar * filename)
912 cache = mbox_cache_get_mbox(filename);
917 if (cache->mf == NULL)
920 return cache->mf->msg_list;
923 static void mbox_cache_synchronize_lists(GList * old_msg_list,
924 GList * new_msg_list)
929 for(l2 = old_msg_list ; l2 != NULL ; l2 = g_list_next(l2)) {
930 struct _message * msg2 = l2->data;
932 if ((msg2->messageid == NULL) ||
933 (msg2->fromspace == NULL))
936 for(l = new_msg_list ; l != NULL ; l = g_list_next(l)) {
937 struct _message * msg = l->data;
939 if ((msg->messageid == NULL) ||
940 (msg->fromspace == NULL))
943 if ((strcmp(msg->messageid, msg2->messageid) == 0) &&
944 (strcmp(msg->fromspace, msg2->fromspace) == 0)) {
945 if (msg2->flags.perm_flags != msg2->old_flags.perm_flags) {
946 msg->flags = msg2->flags;
954 static void mbox_cache_synchronize(gchar * filename, gboolean sync)
956 mboxcache * new_cache;
957 mboxcache * old_cache;
958 gboolean scan_new = TRUE;
961 old_cache = mbox_cache_get_mbox(filename);
963 if (old_cache != NULL) {
964 if (stat(filename, &s) < 0) {
965 FILE_OP_ERROR(filename, "stat");
966 } else if (old_cache->mtime == s.st_mtime) {
967 debug_print("Folder is not modified.\n");
974 if (strstr(filename, "trash") == 0)
975 printf("old_cache: %p %s\n", old_cache, filename);
977 printf("begin old\n");
978 for(l = old_cache->mf->msg_list ; l != NULL ;
979 l = g_list_next(l)) {
980 struct _message * msg = l->data;
987 new_cache = mbox_cache_read_mbox(filename);
990 if (strstr(filename, "trash") == 0)
991 printf("new_cache: %p %s\n", new_cache, filename);
993 printf("begin new\n");
994 for(l = new_cache->mf->msg_list ; l != NULL ;
995 l = g_list_next(l)) {
996 struct _message * msg = l->data;
1006 if (sync && new_cache && old_cache)
1007 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1008 new_cache->mf->msg_list);
1010 if (old_cache != NULL)
1011 mbox_cache_free_mbox(old_cache);
1014 mbox_cache_insert_mbox(new_cache);
1016 printf("insert %p %s\n", new_cache, new_cache->filename);
1017 printf("inserted %s %p\n", filename,
1018 mbox_cache_get_mbox(filename));
1024 static void mbox_cache_synchronize_from_file(FILE * fp, gchar * filename,
1027 mboxcache * new_cache;
1028 mboxcache * old_cache;
1029 gboolean scan_new = TRUE;
1032 old_cache = mbox_cache_get_mbox(filename);
1034 if (old_cache != NULL) {
1035 if (stat(filename, &s) < 0) {
1036 FILE_OP_ERROR(filename, "stat");
1037 } else if (old_cache->mtime == s.st_mtime) {
1038 debug_print("Folder is not modified.\n");
1049 if (strstr(filename, "trash") == 0)
1050 printf("old_cache: %p %s\n", old_cache, filename);
1053 printf("begin old\n");
1054 for(l = old_cache->mf->msg_list ; l != NULL ;
1055 l = g_list_next(l)) {
1056 struct _message * msg = l->data;
1057 printf("%p\n", msg);
1059 printf("end old\n");
1063 new_cache = mbox_cache_read_mbox_from_file(fp, filename);
1066 if (strstr(filename, "trash") == 0)
1067 printf("new_cache: %p %s\n", new_cache, filename);
1070 printf("begin new\n");
1071 for(l = new_cache->mf->msg_list ; l != NULL ;
1072 l = g_list_next(l)) {
1073 struct _message * msg = l->data;
1074 printf("%p\n", msg);
1076 printf("end new\n");
1083 if (sync && new_cache && old_cache)
1084 mbox_cache_synchronize_lists(old_cache->mf->msg_list,
1085 new_cache->mf->msg_list);
1087 if (old_cache != NULL)
1088 mbox_cache_free_mbox(old_cache);
1091 mbox_cache_insert_mbox(new_cache);
1093 printf("insert %p %s\n", new_cache, new_cache->filename);
1094 printf("inserted %s %p\n", filename,
1095 mbox_cache_get_mbox(filename));
1101 gboolean mbox_cache_msg_fetched(gchar * filename, gint num)
1103 struct _message * msg;
1106 cache = mbox_cache_get_mbox(filename);
1111 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1116 return msg->fetched;
1119 void mbox_cache_msg_set_fetched(gchar * filename, gint num)
1121 struct _message * msg;
1124 cache = mbox_cache_get_mbox(filename);
1129 msg = (struct _message *) g_ptr_array_index(cache->tab_mf,
1134 msg->fetched = TRUE;
1137 struct _message * mbox_cache_get_msg(gchar * filename, gint num)
1141 cache = mbox_cache_get_mbox(filename);
1143 if (cache == NULL) {
1147 return (struct _message *) g_ptr_array_index(cache->tab_mf,
1152 /**********************************************************/
1154 /* mbox operations */
1156 /**********************************************************/
1159 GSList *mbox_get_msg_list(Folder *folder, FolderItem *item, gboolean use_cache)
1168 struct timeval tv_before, tv_after, tv_result;
1170 gettimeofday(&tv_before, NULL);
1175 mbox_path = mbox_folder_get_path(item);
1177 if (mbox_path == NULL)
1180 mbox_purge_deleted(mbox_path);
1182 fp = fopen(mbox_path, "rb");
1189 mbox_lockread_file(fp, mbox_path);
1191 mbox_cache_synchronize_from_file(fp, mbox_path, TRUE);
1193 item->last_num = mbox_cache_get_count(mbox_path);
1195 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
1196 l = g_list_next(l)) {
1197 struct _message * msg;
1199 msg = (struct _message *) l->data;
1201 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
1202 fseek(fp, msg->header, SEEK_SET);
1204 msginfo = mbox_parse_msg(fp, msg, item);
1206 if (!MSG_IS_INVALID(msg->flags))
1207 msginfo->flags = msg->flags;
1209 msg->old_flags = msginfo->flags;
1210 msg->flags = msginfo->flags;
1213 mlist = g_slist_append(mlist, msginfo);
1216 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
1220 mbox_unlock_file(fp, mbox_path);
1227 gettimeofday(&tv_after, NULL);
1229 timersub(&tv_after, &tv_before, &tv_result);
1230 g_print("mbox_get_msg_list: %s: elapsed time: %ld.%06ld sec\n",
1231 mbox_path, tv_result.tv_sec, tv_result.tv_usec);
1237 static gboolean mbox_extract_msg(FolderItem * item, gint msgnum,
1238 gchar * dest_filename)
1240 struct _message * msg;
1247 /* GList * msg_list;*/
1248 gboolean already_fetched;
1251 mbox_path = mbox_folder_get_path(item);
1253 if (mbox_path == NULL)
1256 src = fopen(mbox_path, "rb");
1262 mbox_lockread_file(src, mbox_path);
1264 mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
1266 already_fetched = mbox_cache_msg_fetched(mbox_path, msgnum);
1268 if (already_fetched) {
1269 mbox_unlock_file(src, mbox_path);
1275 msg = mbox_cache_get_msg(mbox_path, msgnum);
1278 mbox_unlock_file(src, mbox_path);
1284 offset = msg->offset;
1285 max_offset = msg->end;
1287 size = max_offset - offset;
1289 fseek(src, offset, SEEK_SET);
1291 dest = fopen(dest_filename, "wb");
1293 mbox_unlock_file(src, mbox_path);
1299 if (change_file_mode_rw(dest, dest_filename) < 0) {
1300 FILE_OP_ERROR(dest_filename, "chmod");
1301 g_warning("can't change file mode\n");
1304 if (!mbox_write_data(src, dest, dest_filename, size)) {
1305 mbox_unlock_file(src, mbox_path);
1308 unlink(dest_filename);
1316 FILE_OP_ERROR(mbox_path, "fread");
1320 mbox_cache_msg_set_fetched(mbox_path, msgnum);
1322 if (fclose(dest) == -1) {
1323 FILE_OP_ERROR(dest_filename, "fclose");
1327 mbox_unlock_file(src, mbox_path);
1329 if (fclose(src) == -1) {
1330 FILE_OP_ERROR(mbox_path, "fclose");
1337 unlink(dest_filename);
1344 gchar *mbox_fetch_msg(Folder *folder, FolderItem *item, gint num)
1349 g_return_val_if_fail(item != NULL, NULL);
1351 path = folder_item_get_path(item);
1352 if (!is_dir_exist(path))
1353 make_dir_hier(path);
1355 filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
1359 if (!mbox_extract_msg(item, num, filename)) {
1367 gint mbox_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
1368 gboolean remove_source)
1377 gchar from_line[MSGBUFSIZE];
1379 if (dest->last_num < 0) {
1380 mbox_scan_folder(folder, dest);
1381 if (dest->last_num < 0) return -1;
1384 src_fp = fopen(file, "rb");
1385 if (src_fp == NULL) {
1389 mbox_path = mbox_folder_get_path(dest);
1390 if (mbox_path == NULL)
1393 dest_fp = fopen(mbox_path, "ab");
1394 if (dest_fp == NULL) {
1400 if (change_file_mode_rw(dest_fp, mbox_path) < 0) {
1401 FILE_OP_ERROR(mbox_path, "chmod");
1402 g_warning("can't change file mode\n");
1405 old_size = ftell(dest_fp);
1407 mbox_lockwrite_file(dest_fp, mbox_path);
1409 if (fgets(from_line, sizeof(from_line), src_fp) == NULL) {
1410 mbox_unlock_file(dest_fp, mbox_path);
1411 g_warning("unvalid file - %s.\n", file);
1418 if (strncmp(from_line, "From ", 5) != 0) {
1421 if (stat(file, &s) < 0) {
1422 mbox_unlock_file(dest_fp, mbox_path);
1423 g_warning("invalid file - %s.\n", file);
1430 fprintf(dest_fp, "From - %s", ctime(&s.st_mtime));
1433 fputs(from_line, dest_fp);
1436 n_read = fread(buf, 1, sizeof(buf), src_fp);
1437 if ((n_read < (gint) sizeof(buf)) && ferror(src_fp))
1439 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
1440 mbox_unlock_file(dest_fp, mbox_path);
1441 g_warning("writing to %s failed.\n", mbox_path);
1442 ftruncate(fileno(dest_fp), old_size);
1449 if (n_read < (gint) sizeof(buf))
1455 if (ferror(src_fp)) {
1456 FILE_OP_ERROR(mbox_path, "fread");
1459 mbox_unlock_file(dest_fp, mbox_path);
1461 if (fclose(src_fp) == -1) {
1462 FILE_OP_ERROR(file, "fclose");
1466 if (fclose(dest_fp) == -1) {
1467 FILE_OP_ERROR(mbox_path, "fclose");
1473 ftruncate(fileno(dest_fp), old_size);
1478 if (remove_source) {
1479 if (unlink(file) < 0)
1480 FILE_OP_ERROR(file, "unlink");
1486 return dest->last_num;
1490 gint mbox_remove_msg(Folder *folder, FolderItem *item, gint num)
1492 struct _message * msg;
1495 mbox_path = mbox_folder_get_path(item);
1496 if (mbox_path == NULL)
1499 mbox_cache_synchronize(mbox_path, TRUE);
1501 msg = mbox_cache_get_msg(mbox_path, num);
1506 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
1511 gint mbox_remove_all_msg(Folder *folder, FolderItem *item)
1516 mbox_path = mbox_folder_get_path(item);
1517 if (mbox_path == NULL)
1520 fp = fopen(mbox_path, "wb");
1534 gint mbox_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1539 filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1540 if (filename == NULL)
1543 msgnum = mbox_add_msg(folder, dest, filename, TRUE);
1546 MSG_SET_FLAGS(msginfo->flags, MSG_REALLY_DELETED);
1547 mbox_change_flags(folder, msginfo->folder, msginfo);
1553 gint mbox_move_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1556 gchar * mbox_path = NULL;
1558 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1559 MsgInfo * msginfo = (MsgInfo *) l->data;
1561 if (msginfo->folder && mbox_path == NULL)
1562 mbox_path = mbox_folder_get_path(msginfo->folder);
1564 mbox_move_msg(folder, dest, msginfo);
1568 mbox_cache_synchronize(mbox_path);
1572 mbox_path = mbox_folder_get_path(dest);
1573 mbox_cache_synchronize(mbox_path);
1576 return dest->last_num;
1581 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1586 filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1587 if (filename == NULL)
1590 msgnum = mbox_add_msg(folder, dest, filename, FALSE);
1595 gint mbox_copy_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1598 gchar * mbox_path = NULL;
1600 for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1601 MsgInfo * msginfo = (MsgInfo *) l->data;
1603 if (msginfo->folder && mbox_path == NULL)
1604 mbox_path = mbox_folder_get_path(msginfo->folder);
1606 mbox_copy_msg(folder, dest, msginfo);
1610 mbox_cache_synchronize(mbox_path);
1614 mbox_path = mbox_folder_get_path(dest);
1615 mbox_cache_synchronize(mbox_path);
1618 return dest->last_num;
1622 struct _copy_flags_info
1628 typedef struct _copy_flags_info CopyFlagsInfo;
1630 GSList * copy_flags_data = NULL;
1632 gint mbox_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1634 Folder * src_folder;
1637 CopyFlagsInfo * flags_info;
1639 src_folder = msginfo->folder->folder;
1641 g_return_val_if_fail(src_folder->fetch_msg != NULL, -1);
1644 mbox_path = mbox_folder_get_path(msginfo->folder);
1645 mbox_rewrite(mbox_path);
1649 filename = src_folder->fetch_msg(src_folder,
1652 if (filename == NULL)
1655 num = folder->add_msg(folder, dest, filename, FALSE);
1658 mbox_path = mbox_folder_get_path(dest);
1659 msg = mbox_cache_get_msg(mbox_path, num);
1661 msg->flags = msginfo->flags;
1668 flags_info = g_new0(CopyFlagsInfo, 1);
1669 flags_info->num = num;
1670 flags_info->flags = msginfo->flags;
1671 copy_flags_data = g_slist_append(copy_flags_data, flags_info);
1676 void mbox_finished_copy(Folder *folder, FolderItem *dest)
1682 mbox_path = mbox_folder_get_path(dest);
1683 if (mbox_path == NULL)
1686 mbox_cache_synchronize(mbox_path, TRUE);
1688 for(l = copy_flags_data ; l != NULL ; l = g_slist_next(l)) {
1689 CopyFlagsInfo * flags_info = l->data;
1690 struct _message * msg;
1692 msg = mbox_cache_get_msg(mbox_path, flags_info->num);
1694 msg->flags = flags_info->flags;
1698 if (copy_flags_data != NULL) {
1699 cache = mbox_cache_get_mbox(mbox_path);
1700 cache->modification = TRUE;
1703 g_slist_free(copy_flags_data);
1704 copy_flags_data = NULL;
1706 mbox_rewrite(mbox_path);
1711 void mbox_scan_folder(Folder *folder, FolderItem *item)
1718 mbox_path = mbox_folder_get_path(item);
1719 if (mbox_path == NULL)
1722 mbox_cache_synchronize(mbox_path, TRUE);
1724 cached = mbox_cache_get_mbox(mbox_path);
1726 if (cached == NULL) {
1735 n_msg = mbox_cache_get_count(mbox_path);
1738 item->new = item->unread = item->total = 0;
1745 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
1746 l = g_list_next(l)) {
1747 struct _message * msg = (struct _message *) l->data;
1748 if (!MSG_IS_REALLY_DELETED(msg->flags))
1750 if (MSG_IS_NEW(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1752 if (MSG_IS_UNREAD(msg->flags) && !MSG_IS_IGNORE_THREAD(msg->flags))
1757 item->unread = unread;
1758 item->total = total;
1761 debug_print("Last number in dir %s = %d\n", mbox_path,
1763 item->last_num = n_msg;
1767 gchar * mbox_get_virtual_path(FolderItem * item)
1772 if (item->parent == NULL) {
1776 gchar * parent_path;
1777 gchar * result_path;
1779 parent_path = mbox_get_virtual_path(item->parent);
1780 if (parent_path == NULL)
1781 result_path = g_strdup(item->name);
1783 result_path = g_strconcat(parent_path,
1786 g_free(parent_path);
1792 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
1793 gchar * new_filename, gint size)
1801 while (pos < size) {
1802 if ((size - pos) > (gint) sizeof(buf))
1807 n_read = fread(buf, 1, max, mbox_fp);
1809 if (n_read < max && ferror(mbox_fp)) {
1812 if (fwrite(buf, n_read, 1, new_fp) < 1) {
1813 g_warning("writing to %s failed.\n", new_filename);
1826 static gboolean mbox_write_message(FILE * mbox_fp, FILE * new_fp,
1827 gchar * new_filename,
1828 struct _message * msg)
1831 GPtrArray * headers;
1834 fseek(mbox_fp, msg->header, SEEK_SET);
1836 headers = procheader_get_header_array_asis(mbox_fp);
1838 for (i = 0; i < (gint) headers->len; i++) {
1839 Header * h = g_ptr_array_index(headers, i);
1841 if (!procheader_headername_equal(h->name,
1843 !procheader_headername_equal(h->name,
1845 fwrite(h->name, strlen(h->name),
1847 if (h->name[strlen(h->name) - 1] != ' ')
1848 fwrite(" ", 1, 1, new_fp);
1849 fwrite(h->body, strlen(h->body),
1851 fwrite("\n", 1, 1, new_fp);
1853 procheader_header_free(h);
1854 g_ptr_array_remove_index(headers, i);
1858 g_ptr_array_free(headers, FALSE);
1860 if (!MSG_IS_INVALID(msg->flags)) {
1862 fwrite("Status: ", strlen("Status: "), 1, new_fp);
1863 if (!MSG_IS_UNREAD(msg->flags))
1864 fwrite("R", 1, 1, new_fp);
1865 fwrite("O", 1, 1, new_fp);
1866 fwrite("\n", 1, 1, new_fp);
1868 /* X-Status header */
1869 if (MSG_IS_REALLY_DELETED(msg->flags)
1870 || MSG_IS_MARKED(msg->flags)
1871 || MSG_IS_DELETED(msg->flags)
1872 || MSG_IS_REPLIED(msg->flags)
1873 || MSG_IS_FORWARDED(msg->flags)) {
1874 fwrite("X-Status: ", strlen("X-Status: "), 1, new_fp);
1875 if (MSG_IS_REALLY_DELETED(msg->flags))
1876 fwrite("D", 1, 1, new_fp); /* really deleted */
1878 if (MSG_IS_MARKED(msg->flags))
1879 fwrite("F", 1, 1, new_fp);
1880 if (MSG_IS_DELETED(msg->flags))
1881 fwrite("d", 1, 1, new_fp);
1882 if (MSG_IS_REPLIED(msg->flags))
1883 fwrite("r", 1, 1, new_fp);
1884 if (MSG_IS_FORWARDED(msg->flags))
1885 fwrite("f", 1, 1, new_fp);
1887 fwrite("\n", 1, 1, new_fp);
1891 fwrite("\n", 1, 1, new_fp);
1893 size = msg->end - msg->content;
1894 fseek(mbox_fp, msg->content, SEEK_SET);
1896 return mbox_write_data(mbox_fp, new_fp, new_filename, size);
1899 void mbox_update_mark(Folder * folder, FolderItem * item)
1903 mbox_path = mbox_folder_get_path(item);
1904 if (mbox_path == NULL)
1907 mbox_rewrite(mbox_path);
1911 void mbox_change_flags(Folder * folder, FolderItem * item, MsgInfo * info)
1913 struct _message * msg;
1917 mbox_path = mbox_folder_get_path(item);
1918 if (mbox_path == NULL)
1921 msg = mbox_cache_get_msg(mbox_path, info->msgnum);
1923 cache = mbox_cache_get_mbox(mbox_path);
1927 if ((msg == NULL) || (cache == NULL))
1930 msg->flags = info->flags;
1932 cache->modification = TRUE;
1937 static gboolean mbox_rewrite(gchar * mbox)
1948 msg_list = mbox_cache_get_msg_list(mbox);
1950 cache = mbox_cache_get_mbox(mbox);
1954 if (!cache->modification) {
1955 debug_print("no modification - %s\n", mbox);
1959 debug_print("save modification - %s\n", mbox);
1961 mbox_fp = fopen(mbox, "rb+");
1962 mbox_lockwrite_file(mbox_fp, mbox);
1964 mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
1966 new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
1967 new_fp = fopen(new, "wb");
1969 if (change_file_mode_rw(new_fp, new) < 0) {
1970 FILE_OP_ERROR(new, "chmod");
1971 g_warning("can't change file mode\n");
1974 mbox_lockwrite_file(new_fp, new);
1979 msg_list = mbox_cache_get_msg_list(mbox);
1980 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
1981 struct _message * msg = (struct _message *) l->data;
1982 if (!mbox_write_message(mbox_fp, new_fp, new, msg)) {
1991 if (rename(new, mbox) == -1) {
1992 g_warning("can't rename %s to %s\n", new, mbox);
1993 mbox_unlock_file(new_fp, new);
1995 mbox_unlock_file(mbox_fp, mbox);
2001 if (change_file_mode_rw(new_fp, mbox) < 0) {
2002 FILE_OP_ERROR(new, "chmod");
2003 g_warning("can't change file mode\n");
2006 mbox_unlock_file(new_fp, new);
2010 mbox_unlock_file(mbox_fp, mbox);
2014 debug_print("%i messages written - %s\n", count, mbox);
2016 cache = mbox_cache_get_mbox(mbox);
2021 mbox_cache_synchronize(mbox, FALSE);
2028 static gboolean mbox_purge_deleted(gchar * mbox)
2035 gboolean modification = FALSE;
2039 mbox_cache_synchronize(mbox, TRUE);
2041 msg_list = mbox_cache_get_msg_list(mbox);
2043 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
2044 struct _message * msg = (struct _message *) l->data;
2045 if (!MSG_IS_INVALID(msg->flags) && MSG_IS_REALLY_DELETED(msg->flags)) {
2046 modification = TRUE;
2051 if (!modification) {
2052 debug_print("no deleted messages - %s\n", mbox);
2056 debug_print("purge deleted messages - %s\n", mbox);
2058 mbox_fp = fopen(mbox, "rb+");
2059 mbox_lockwrite_file(mbox_fp, mbox);
2061 mbox_cache_synchronize_from_file(mbox_fp, mbox, TRUE);
2063 new = g_strconcat(mbox, ".", itos((int) mbox), NULL);
2064 new_fp = fopen(new, "wb");
2066 if (change_file_mode_rw(new_fp, new) < 0) {
2067 FILE_OP_ERROR(new, "chmod");
2068 g_warning("can't change file mode\n");
2071 mbox_lockwrite_file(new_fp, new);
2076 msg_list = mbox_cache_get_msg_list(mbox);
2077 for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
2078 struct _message * msg = (struct _message *) l->data;
2079 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
2080 if (!mbox_write_message(mbox_fp, new_fp, new, msg)) {
2090 if (rename(new, mbox) == -1) {
2091 g_warning("can't rename %s to %s\n", new, mbox);
2092 mbox_unlock_file(new_fp, new);
2094 mbox_unlock_file(mbox_fp, mbox);
2100 if (change_file_mode_rw(new_fp, mbox) < 0) {
2101 FILE_OP_ERROR(new, "chmod");
2102 g_warning("can't change file mode\n");
2105 mbox_unlock_file(new_fp, new);
2109 mbox_unlock_file(mbox_fp, mbox);
2113 debug_print("%i messages written - %s\n", count, mbox);
2115 mbox_cache_synchronize(mbox, FALSE);
2120 #define MAKE_DIR_IF_NOT_EXIST(dir) \
2122 if (!is_dir_exist(dir)) { \
2123 if (is_file_exist(dir)) { \
2124 g_warning("File `%s' already exists.\n" \
2125 "Can't create folder.", dir); \
2128 if (mkdir(dir, S_IRWXU) < 0) { \
2129 FILE_OP_ERROR(dir, "mkdir"); \
2132 if (chmod(dir, S_IRWXU) < 0) \
2133 FILE_OP_ERROR(dir, "chmod"); \
2137 gint mbox_create_tree(Folder *folder)
2141 g_return_val_if_fail(folder != NULL, -1);
2143 CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
2144 rootpath = LOCAL_FOLDER(folder)->rootpath;
2145 MAKE_DIR_IF_NOT_EXIST(rootpath);
2146 CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
2151 #undef MAKE_DIR_IF_NOT_EXIST
2153 static gchar * mbox_get_new_path(FolderItem * parent, gchar * name)
2157 if (strchr(name, '/') == NULL) {
2158 if (parent->path != NULL)
2159 path = g_strconcat(parent->path, ".sbd", G_DIR_SEPARATOR_S, name, NULL);
2161 path = g_strdup(name);
2164 path = g_strdup(name);
2169 static gchar * mbox_get_folderitem_name(gchar * name)
2173 foldername = g_strdup(g_basename(name));
2178 FolderItem *mbox_create_folder(Folder *folder, FolderItem *parent,
2182 FolderItem *new_item;
2185 g_return_val_if_fail(folder != NULL, NULL);
2186 g_return_val_if_fail(parent != NULL, NULL);
2187 g_return_val_if_fail(name != NULL, NULL);
2189 path = mbox_get_new_path(parent, (gchar *) name);
2191 foldername = mbox_get_folderitem_name((gchar *) name);
2193 new_item = folder_item_new(folder, foldername, path);
2194 folder_item_append(parent, new_item);
2196 if (!strcmp(name, "inbox")) {
2197 new_item->stype = F_INBOX;
2198 new_item->folder->inbox = new_item;
2199 } else if (!strcmp(name, "outbox")) {
2200 new_item->stype = F_OUTBOX;
2201 new_item->folder->outbox = new_item;
2202 } else if (!strcmp(name, "draft")) {
2203 new_item->stype = F_DRAFT;
2204 new_item->folder->draft = new_item;
2205 } else if (!strcmp(name, "queue")) {
2206 new_item->stype = F_QUEUE;
2207 new_item->folder->queue = new_item;
2208 } else if (!strcmp(name, "trash")) {
2209 new_item->stype = F_TRASH;
2210 new_item->folder->trash = new_item;
2219 gint mbox_rename_folder(Folder *folder, FolderItem *item, const gchar *name)
2224 g_return_val_if_fail(folder != NULL, -1);
2225 g_return_val_if_fail(item != NULL, -1);
2226 g_return_val_if_fail(item->path != NULL, -1);
2227 g_return_val_if_fail(name != NULL, -1);
2229 path = mbox_get_new_path(item->parent, (gchar *) name);
2230 foldername = mbox_get_folderitem_name((gchar *) name);
2232 if (rename(item->path, path) == -1) {
2235 g_warning("Cannot rename folder item");
2243 item->name = foldername;
2249 gint mbox_remove_folder(Folder *folder, FolderItem *item)
2251 g_return_val_if_fail(folder != NULL, -1);
2252 g_return_val_if_fail(item != NULL, -1);
2253 g_return_val_if_fail(item->path != NULL, -1);
2255 folder_item_remove(item);
2259 gint mbox_get_num_list(Folder *folder, FolderItem *item, GSList **mlist)
2266 mbox_path = mbox_folder_get_path(item);
2268 if (mbox_path == NULL)
2271 mbox_purge_deleted(mbox_path);
2273 fp = fopen(mbox_path, "rb");
2280 mbox_lockread_file(fp, mbox_path);
2282 mbox_cache_synchronize_from_file(fp, mbox_path, TRUE);
2284 item->last_num = mbox_cache_get_count(mbox_path);
2286 for(l = mbox_cache_get_msg_list(mbox_path) ; l != NULL ;
2287 l = g_list_next(l)) {
2288 struct _message * msg;
2290 msg = (struct _message *) l->data;
2292 if (MSG_IS_INVALID(msg->flags) || !MSG_IS_REALLY_DELETED(msg->flags)) {
2293 *mlist = g_slist_append(*mlist, GINT_TO_POINTER(msg->msgnum));
2296 MSG_SET_PERM_FLAGS(msg->flags, MSG_REALLY_DELETED);
2300 mbox_unlock_file(fp, mbox_path);
2309 MsgInfo *mbox_get_msginfo(Folder *folder, FolderItem *item, gint num)
2312 struct _message *msg;
2316 g_return_val_if_fail(folder != NULL, NULL);
2317 g_return_val_if_fail(item != NULL, NULL);
2319 mbox_path = mbox_folder_get_path(item);
2321 g_return_val_if_fail(mbox_path != NULL, NULL);
2323 src = fopen(mbox_path, "rb");
2328 mbox_lockread_file(src, mbox_path);
2329 mbox_cache_synchronize_from_file(src, mbox_path, TRUE);
2331 msg = mbox_cache_get_msg(mbox_path, num);
2333 mbox_unlock_file(src, mbox_path);
2339 fseek(src, msg->header, SEEK_SET);
2340 msginfo = mbox_parse_msg(src, msg, item);
2342 mbox_unlock_file(src, mbox_path);
2349 gboolean mbox_check_msgnum_validity(Folder *folder, FolderItem *item)
2351 mboxcache * old_cache;
2352 gboolean scan_new = TRUE;
2356 filename = mbox_folder_get_path(item);
2358 old_cache = mbox_cache_get_mbox(filename);
2360 if (old_cache != NULL) {
2361 if (stat(filename, &s) < 0) {
2362 FILE_OP_ERROR(filename, "stat");
2363 } else if (old_cache->mtime == s.st_mtime) {
2364 debug_print("Folder is not modified.\n");