implementation of mbox folder with unique messages numbers
[claws.git] / src / mailmbox_folder.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2003 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 #include <sys/types.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <sys/mman.h>
36
37 #undef MEASURE_TIME
38
39 #ifdef MEASURE_TIME
40 #  include <sys/time.h>
41 #endif
42
43 #include "intl.h"
44 #include "folder.h"
45 #include "procmsg.h"
46 #include "procheader.h"
47 #include "utils.h"
48 #include "mailmbox.h"
49 #include "mailmbox_folder.h"
50 #include "mailmbox_parse.h"
51
52 static Folder *s_mailmbox_folder_new(const gchar *name, const gchar *path);
53
54 static void mailmbox_folder_destroy(Folder *folder);
55
56 static FolderItem *mailmbox_folder_item_new(Folder *folder);
57
58 static void mailmbox_folder_item_destroy(Folder *folder, FolderItem *_item);
59
60 static gchar *mailmbox_item_get_path(Folder *folder, FolderItem *item);
61
62 static gint mailmbox_get_num_list(Folder *folder, FolderItem *item,
63     GSList **list, gboolean *old_uids_valid);
64
65 static MsgInfo *mailmbox_get_msginfo(Folder *folder,
66     FolderItem *item, gint num);
67
68 static GSList *mailmbox_get_msginfos(Folder *folder, FolderItem *item,
69     GSList *msgnum_list);
70
71 static gchar *s_mailmbox_fetch_msg(Folder *folder, FolderItem *item, gint num);
72
73 static gint mailmbox_add_msg(Folder *folder, FolderItem *dest,
74     const gchar *file, MsgFlags *flags);
75
76 static gint mailmbox_add_msgs(Folder *folder, FolderItem *dest,
77     GSList *file_list, 
78     GRelation *relation);
79
80 static gint s_mailmbox_copy_msg(Folder *folder,
81     FolderItem *dest, MsgInfo *msginfo);
82
83 static gint mailmbox_copy_msgs(Folder *folder, FolderItem *dest, 
84     MsgInfoList *msglist, GRelation *relation);
85
86 static gint mailmbox_remove_msg(Folder *folder, FolderItem *item, gint num);
87
88 static gint mailmbox_remove_all_msg(Folder *folder, FolderItem *item);
89
90 static FolderItem *mailmbox_create_folder(Folder *folder, FolderItem *parent,
91     const gchar *name);
92
93 static gboolean mailmbox_scan_required(Folder *folder, FolderItem *_item);
94
95 static gint mailmbox_rename_folder(Folder *folder,
96     FolderItem *item, const gchar *name);
97
98 static gint mailmbox_remove_folder(Folder *folder, FolderItem *item);
99
100 static gint mailmbox_create_tree(Folder *folder);
101
102 static FolderClass mailmbox_class =
103 {
104         F_MBOX,
105         "mbox",
106         "MBOX",
107
108         /* Folder functions */
109         s_mailmbox_folder_new,
110         mailmbox_folder_destroy,
111         NULL, /* mailmbox_scan_tree, */
112         mailmbox_create_tree,
113
114         /* FolderItem functions */
115         mailmbox_folder_item_new,
116         mailmbox_folder_item_destroy,
117         mailmbox_item_get_path,
118         mailmbox_create_folder,
119         mailmbox_rename_folder,
120         mailmbox_remove_folder,
121         NULL,
122         mailmbox_get_num_list,
123         NULL,
124         NULL,
125         NULL,
126         mailmbox_scan_required,
127         
128         /* Message functions */
129         mailmbox_get_msginfo,
130         mailmbox_get_msginfos,
131         s_mailmbox_fetch_msg,
132         mailmbox_add_msg,
133         mailmbox_add_msgs,
134         s_mailmbox_copy_msg,
135         mailmbox_copy_msgs,
136         mailmbox_remove_msg,
137         mailmbox_remove_all_msg,
138         NULL,
139         NULL,
140 };
141
142 FolderClass *mailmbox_get_class(void)
143 {
144         return &mailmbox_class;
145 }
146
147
148 static void mailmbox_folder_init(Folder *folder,
149     const gchar *name, const gchar *path)
150 {
151         folder_local_folder_init(folder, name, path);
152 }
153
154 static Folder *s_mailmbox_folder_new(const gchar *name, const gchar *path)
155 {
156         Folder *folder;
157
158         folder = (Folder *)g_new0(MBOXFolder, 1);
159         folder->klass = &mailmbox_class;
160         mailmbox_folder_init(folder, name, path);
161         
162         return folder;
163 }
164
165 static void mailmbox_folder_destroy(Folder *folder)
166 {
167         folder_local_folder_destroy(LOCAL_FOLDER(folder));
168 }
169
170 typedef struct _MBOXFolderItem  MBOXFolderItem;
171 struct _MBOXFolderItem
172 {
173         FolderItem item;
174         uint32_t old_max_uid;
175         struct mailmbox_folder * mbox;
176 };
177
178 static FolderItem *mailmbox_folder_item_new(Folder *folder)
179 {
180         MBOXFolderItem *item;
181         
182         item = g_new0(MBOXFolderItem, 1);
183         item->mbox = NULL;
184         item->old_max_uid = 0;
185
186         return (FolderItem *)item;
187 }
188
189 #define MAX_UID_FILE "max-uid"
190
191 void read_max_uid_value(FolderItem *item, uint32_t * pmax_uid)
192 {
193         gchar * path;
194         gchar * file;
195         FILE * f;
196         uint32_t max_uid;
197         size_t r;
198         
199         path = folder_item_get_path(item);
200         file = g_strconcat(path, G_DIR_SEPARATOR_S, MAX_UID_FILE, NULL);
201         g_free(path);
202         
203         f = fopen(file, "r");
204         g_free(file);
205         if (f == NULL)
206                 return;
207         r = fread(&max_uid, sizeof(max_uid), 1, f);
208         if (r == 0) {
209                 fclose(f);
210                 return;
211         }
212         
213         fclose(f);
214         
215         * pmax_uid = max_uid;
216 }
217
218 void write_max_uid_value(FolderItem *item, uint32_t max_uid)
219 {
220         gchar * path;
221         gchar * file;
222         FILE * f;
223         size_t r;
224         
225         path = folder_item_get_path(item);
226         file = g_strconcat(path, G_DIR_SEPARATOR_S, MAX_UID_FILE, NULL);
227         g_free(path);
228         
229         f = fopen(file, "w");
230         g_free(file);
231         if (f == NULL)
232                 return;
233         r = fwrite(&max_uid, sizeof(max_uid), 1, f);
234         if (r == 0) {
235                 fclose(f);
236                 return;
237         }
238         
239         fclose(f);
240 }
241
242 static void mailmbox_folder_item_destroy(Folder *folder, FolderItem *_item)
243 {
244         MBOXFolderItem *item = (MBOXFolderItem *)_item;
245
246         g_return_if_fail(item != NULL);
247         
248         if (item->mbox != NULL) {
249                 write_max_uid_value(_item, item->mbox->written_uid);
250                 mailmbox_done(item->mbox);
251         }
252         g_free(_item);
253 }
254
255 static void mailmbox_folder_create_parent(const gchar * path)
256 {
257         if (!is_file_exist(path)) {
258                 gchar * new_path;
259
260                 new_path = g_dirname(path);
261                 if (new_path[strlen(new_path) - 1] == G_DIR_SEPARATOR)
262                         new_path[strlen(new_path) - 1] = '\0';
263
264                 if (!is_dir_exist(new_path))
265                         make_dir_hier(new_path);
266                 g_free(new_path);
267                 
268         }
269 }
270
271 static gchar * mailmbox_folder_get_path(Folder *folder, FolderItem *item)
272 {
273         gchar *folder_path;
274         gchar *path;
275
276         g_return_val_if_fail(item != NULL, NULL);
277
278         if (item->path && item->path[0] == G_DIR_SEPARATOR) {
279                 mailmbox_folder_create_parent(item->path);
280                 return g_strdup(item->path);
281         }
282
283         folder_path = g_strdup(LOCAL_FOLDER(item->folder)->rootpath);
284         g_return_val_if_fail(folder_path != NULL, NULL);
285
286         if (folder_path[0] == G_DIR_SEPARATOR) {
287                 if (item->path) {
288                         path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
289                                            item->path, NULL);
290                 }
291                 else
292                         path = g_strdup(folder_path);
293         } else {
294                 if (item->path)
295                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
296                                            folder_path, G_DIR_SEPARATOR_S,
297                                            item->path, NULL);
298                 else
299                         path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
300                                            folder_path, NULL);
301         }
302
303         g_free(folder_path);
304         
305         mailmbox_folder_create_parent(path);
306
307         return path;
308 }
309
310 static int mailmbox_item_sync(FolderItem *_item, int validate_uid)
311 {
312         MBOXFolderItem *item = (MBOXFolderItem *)_item;
313         int r;
314
315         if (item->mbox == NULL) {
316                 uint32_t written_uid;
317                 gchar * path;
318                 
319                 written_uid = 0;
320                 read_max_uid_value(_item, &written_uid);
321                 path = mailmbox_folder_get_path(_item->folder, _item);
322                 r = mailmbox_init(path, 0, 0, written_uid, &item->mbox);
323                 g_free(path);
324                 if (r != MAILMBOX_NO_ERROR)
325                         return -1;
326         }
327
328         if (!validate_uid) {
329                 r = mailmbox_validate_read_lock(item->mbox);
330                 if (r != MAILMBOX_NO_ERROR) {
331                         goto err;
332                 }
333                 
334                 mailmbox_read_unlock(item->mbox);
335         }
336         else {
337                 r = mailmbox_validate_write_lock(item->mbox);
338                 if (r != MAILMBOX_NO_ERROR) {
339                         goto err;
340                 }
341                 
342                 if (item->mbox->written_uid < item->mbox->max_uid) {
343                         r = mailmbox_expunge_no_lock(item->mbox);
344                         if (r != MAILMBOX_NO_ERROR)
345                                 goto unlock;
346                 }
347                 mailmbox_write_unlock(item->mbox);
348         }
349         
350         return 0;
351         
352  unlock:
353         mailmbox_write_unlock(item->mbox);
354  err:
355         return -1;
356 }
357
358 static struct mailmbox_folder * get_mbox(FolderItem *_item, int validate_uid)
359 {
360         MBOXFolderItem *item = (MBOXFolderItem *)_item;
361         
362         mailmbox_item_sync(_item, validate_uid);
363         
364         return item->mbox;
365 }
366
367 static gint mailmbox_get_num_list(Folder *folder, FolderItem *item,
368     GSList **list, gboolean *old_uids_valid)
369 {
370         gint nummsgs = 0;
371         uint32_t i;
372         struct mailmbox_folder * mbox;
373
374         g_return_val_if_fail(item != NULL, -1);
375
376         debug_print("mbox_get_last_num(): Scanning %s ...\n", item->path);
377         
378         *old_uids_valid = TRUE;
379         
380         mbox = get_mbox(item, 1);
381         if (mbox == NULL)
382                 return -1;
383         
384         for(i = 0 ; i < carray_count(mbox->tab) ; i ++) {
385                 struct mailmbox_msg_info * msg;
386                 
387                 msg = carray_get(mbox->tab, i);
388                 if (msg != NULL) {
389                         *list = g_slist_prepend(*list,
390                             GINT_TO_POINTER(msg->uid));
391                         nummsgs ++;
392                 }
393         }
394         
395         return nummsgs;
396 }
397
398 static gchar *s_mailmbox_fetch_msg(Folder *folder, FolderItem *item, gint num)
399 {
400         gchar *path;
401         gchar *file;
402         int r;
403         struct mailmbox_folder * mbox;
404         char * data;
405         size_t len;
406         FILE * f;
407         mode_t old_mask;
408
409         g_return_val_if_fail(item != NULL, NULL);
410         g_return_val_if_fail(num > 0, NULL);
411         
412         mbox = get_mbox(item, 0);
413         if (mbox == NULL)
414                 return NULL;
415
416         path = folder_item_get_path(item);
417         file = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
418         g_free(path);
419         if (is_file_exist(file)) {
420                 return file;
421         }
422         
423         r = mailmbox_fetch_msg(mbox, num, &data, &len);
424         if (r != MAILMBOX_NO_ERROR)
425                 goto free;
426         
427         fprintf(stderr, file);
428         old_mask = umask(0077);
429         f = fopen(file, "w");
430         umask(old_mask);
431         if (f == NULL)
432                 goto free_data;
433         
434         r = fwrite(data, 1, len, f);
435         if (r == 0)
436                 goto close;
437         
438         fclose(f);
439         free(data);
440         
441         return file;
442         
443  close:
444         fclose(f);
445         unlink(file);
446  free_data:
447         free(data);
448  free:
449         free(file);
450  err:
451         return NULL;
452 }
453
454 static MsgInfo *mailmbox_parse_msg(uint32_t uid,
455     char * data, size_t len, FolderItem *item)
456 {
457         MsgInfo *msginfo;
458         MsgFlags flags;
459
460         flags.perm_flags = MSG_NEW|MSG_UNREAD;
461         flags.tmp_flags = 0;
462
463         g_return_val_if_fail(item != NULL, NULL);
464         g_return_val_if_fail(data != NULL, NULL);
465
466         if (item->stype == F_QUEUE) {
467                 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
468         } else if (item->stype == F_DRAFT) {
469                 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
470         }
471
472         msginfo = procheader_parse_str(data, flags, FALSE, FALSE);
473         if (!msginfo) return NULL;
474
475         msginfo->msgnum = uid;
476         msginfo->folder = item;
477
478         return msginfo;
479 }
480
481 static MsgInfo *mailmbox_get_msginfo(Folder *folder,
482     FolderItem *item, gint num)
483 {
484         MsgInfo *msginfo;
485         int r;
486         char * data;
487         size_t len;
488         struct mailmbox_folder * mbox;
489
490         g_return_val_if_fail(item != NULL, NULL);
491         g_return_val_if_fail(num > 0, NULL);
492
493         mbox = get_mbox(item, 0);
494         if (mbox == NULL)
495                 goto err;
496
497         r = mailmbox_validate_read_lock(mbox);
498         if (r != MAILMBOX_NO_ERROR)
499                 goto err;
500         
501         r = mailmbox_fetch_msg_headers_no_lock(mbox, num, &data, &len);
502         if (r != MAILMBOX_NO_ERROR)
503                 goto unlock;
504         
505         msginfo = mailmbox_parse_msg(num, data, len, item);
506         if (!msginfo)
507                 goto unlock;
508
509         msginfo->msgnum = num;
510
511         mailmbox_read_unlock(mbox);
512
513         return msginfo;
514
515  unlock:
516         mailmbox_read_unlock(mbox);
517  err:
518         return NULL;
519 }
520
521 static GSList *mailmbox_get_msginfos(Folder *folder, FolderItem *item,
522     GSList *msgnum_list)
523 {
524         int r;
525         GSList * cur;
526         GSList * ret;
527         struct mailmbox_folder * mbox;
528         
529         g_return_val_if_fail(item != NULL, NULL);
530
531         mbox = get_mbox(item, 0);
532         if (mbox == NULL)
533                 goto err;
534
535         r = mailmbox_validate_read_lock(mbox);
536         if (r != MAILMBOX_NO_ERROR)
537                 goto err;
538
539         ret = NULL;
540         
541         for (cur = msgnum_list ; cur != NULL ; cur = g_slist_next(cur)) {
542                 char * data;
543                 size_t len;
544                 gint num;
545                 MsgInfo *msginfo;
546                 
547                 num = GPOINTER_TO_INT(cur->data);
548                 
549                 r = mailmbox_fetch_msg_headers_no_lock(mbox, num, &data, &len);
550                 if (r != MAILMBOX_NO_ERROR)
551                         continue;
552                 
553                 msginfo = mailmbox_parse_msg(num, data, len, item);
554                 if (!msginfo)
555                         continue;
556                 
557                 msginfo->msgnum = num;
558                 
559                 ret = g_slist_append(ret, msginfo);
560         }
561         
562         mailmbox_read_unlock(mbox);
563
564         return ret;
565
566  unlock:
567         mailmbox_read_unlock(mbox);
568  err:
569         return NULL;
570 }
571
572 /* ok */
573
574 static gint mailmbox_add_msg(Folder *folder, FolderItem *dest,
575     const gchar *file, MsgFlags *flags)
576 {
577         gint ret;
578         GSList file_list;
579         MsgFileInfo fileinfo;
580
581         g_return_val_if_fail(file != NULL, -1);
582
583         fileinfo.msginfo = NULL;
584         fileinfo.file = (gchar *)file;
585         fileinfo.flags = flags;
586         file_list.data = &fileinfo;
587         file_list.next = NULL;
588
589         ret = mailmbox_add_msgs(folder, dest, &file_list, NULL);
590         return ret;
591
592
593 /* ok */
594  
595 static gint mailmbox_add_msgs(Folder *folder, FolderItem *dest,
596     GSList *file_list, 
597     GRelation *relation)
598
599         gchar *destfile;
600         GSList *cur;
601         gint last_num;
602         struct mailmbox_folder * mbox;
603         carray * append_list;
604         struct mailmbox_append_info append_info;
605         int r;
606
607         g_return_val_if_fail(dest != NULL, -1);
608         g_return_val_if_fail(file_list != NULL, -1);
609         
610         mbox = get_mbox(dest, 0);
611         if (mbox == NULL)
612                 return -1;
613
614         r = mailmbox_validate_write_lock(mbox);
615         if (r != MAILMBOX_NO_ERROR)
616                 return -1;
617         
618         r = mailmbox_expunge_no_lock(mbox);
619         if (r != MAILMBOX_NO_ERROR)
620                 goto unlock;
621         
622         last_num = -1;
623
624         append_list = carray_new(1);
625         if (append_list == NULL)
626                 goto unlock;
627
628         r = carray_set_size(append_list, 1);
629         if (r < 0)
630                 goto free;
631         
632         carray_set(append_list, 0, &append_info);
633         
634         for (cur = file_list; cur != NULL; cur = cur->next) {
635                 int fd;
636                 struct stat stat_info;
637                 char * data;
638                 size_t len;
639                 struct mailmbox_msg_info * msg;
640                 size_t cur_token;
641                 MsgFileInfo *fileinfo;
642                 
643                 fileinfo = (MsgFileInfo *)cur->data;
644                 
645                 fd = open(fileinfo->file, O_RDONLY);
646                 if (fd == -1)
647                         goto err;
648                 
649                 r = fstat(fd, &stat_info);
650                 if (r < 0)
651                         goto close;
652                 
653                 len = stat_info.st_size;
654                 data = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
655                 if (data == MAP_FAILED)
656                         goto close;
657                 
658                 append_info.message = data;
659                 append_info.size = len;
660                 
661                 cur_token = mbox->mapping_size;
662                 
663                 r = mailmbox_append_message_list_no_lock(mbox, append_list);
664                 if (r != MAILMBOX_NO_ERROR)
665                         goto unmap;
666
667                 munmap(data, len);
668                 close(fd);
669                 
670                 mailmbox_sync(mbox);
671                 
672                 r = mailmbox_parse_additionnal(mbox, &cur_token);
673                 if (r != MAILMBOX_NO_ERROR)
674                         goto unlock;
675                 
676                 msg = carray_get(mbox->tab, carray_count(mbox->tab) - 1);
677
678                 if (relation != NULL)
679                         g_relation_insert(relation, fileinfo,
680                             GINT_TO_POINTER(msg->uid));
681                 
682                 last_num = msg->uid;
683                 
684                 continue;
685                 
686         unmap:
687                 munmap(data, len);
688         close:
689                 close(fd);
690         err:
691                 continue;
692         }
693         
694         carray_free(append_list);
695         mailmbox_write_unlock(mbox);
696         
697         return last_num;
698
699  free:
700         carray_free(append_list);
701  unlock:
702         mailmbox_write_unlock(mbox);
703 return -1;
704 }
705
706 static gint s_mailmbox_copy_msg(Folder *folder,
707     FolderItem *dest, MsgInfo *msginfo)
708 {
709         GSList msglist;
710
711         g_return_val_if_fail(msginfo != NULL, -1);
712
713         msglist.data = msginfo;
714         msglist.next = NULL;
715
716         return mailmbox_copy_msgs(folder, dest, &msglist, NULL);
717 }
718
719 static gint mailmbox_copy_msgs(Folder *folder, FolderItem *dest, 
720     MsgInfoList *msglist, GRelation *relation)
721 {
722         MsgInfo *msginfo;
723         GSList *file_list;
724         gint ret;
725         
726         g_return_val_if_fail(folder != NULL, -1);
727         g_return_val_if_fail(dest != NULL, -1);
728         g_return_val_if_fail(msglist != NULL, -1);
729
730         msginfo = (MsgInfo *)msglist->data;
731         g_return_val_if_fail(msginfo->folder != NULL, -1);
732
733         file_list = procmsg_get_message_file_list(msglist);
734         g_return_val_if_fail(file_list != NULL, -1);
735
736         ret = mailmbox_add_msgs(folder, dest, file_list, relation);
737
738         procmsg_message_file_list_free(file_list);
739
740         return ret;
741 }
742
743
744 static gint mailmbox_remove_msg(Folder *folder, FolderItem *item, gint num)
745 {
746         struct mailmbox_folder * mbox;
747         int r;
748         
749         g_return_val_if_fail(item != NULL, -1);
750         
751         mbox = get_mbox(item, 0);
752         if (mbox == NULL)
753                 return -1;
754         
755         r = mailmbox_delete_msg(mbox, num);
756         if (r != MAILMBOX_NO_ERROR)
757                 return -1;
758
759         return 0;
760 }
761
762 static gint mailmbox_remove_all_msg(Folder *folder, FolderItem *item)
763 {
764         struct mailmbox_folder * mbox;
765         int r;
766         uint32_t i;
767         
768         g_return_val_if_fail(item != NULL, -1);
769         
770         mbox = get_mbox(item, 0);
771         if (mbox == NULL)
772                 return -1;
773        
774         for(i = 0 ; i < carray_count(mbox->tab) ; i ++) {
775                 struct mailmbox_msg_info * msg;
776                 
777                 msg = carray_get(mbox->tab, i);
778                 if (msg == NULL)
779                         continue;
780                 
781                 r = mailmbox_delete_msg(mbox, msg->uid);
782                 if (r != MAILMBOX_NO_ERROR)
783                         continue;
784         }
785         
786         return 0;
787 }
788
789
790 static gchar * mailmbox_get_new_path(FolderItem * parent, gchar * name)
791 {
792         gchar * path;
793
794         if (strchr(name, G_DIR_SEPARATOR) == NULL) {
795                 if (parent->path != NULL)
796                         path = g_strconcat(parent->path, ".sbd", G_DIR_SEPARATOR_S, name, NULL);
797                 else
798                         path = g_strdup(name);
799         }
800         else
801                 path = g_strdup(name);
802
803         return path;
804 }
805
806 static gchar * mailmbox_get_folderitem_name(gchar * name)
807 {
808         gchar * foldername;
809
810         foldername = g_strdup(g_basename(name));
811         
812         return foldername;
813 }
814
815 static FolderItem *mailmbox_create_folder(Folder *folder, FolderItem *parent,
816     const gchar *name)
817 {
818         gchar * path;
819         FolderItem *new_item;
820         gchar * foldername;
821
822         g_return_val_if_fail(folder != NULL, NULL);
823         g_return_val_if_fail(parent != NULL, NULL);
824         g_return_val_if_fail(name != NULL, NULL);
825
826         path = mailmbox_get_new_path(parent, (gchar *) name);
827
828         foldername = mailmbox_get_folderitem_name((gchar *) name);
829
830         new_item = folder_item_new(folder, foldername, path);
831         folder_item_append(parent, new_item);
832
833         if (!strcmp(name, "inbox")) {
834                 new_item->stype = F_INBOX;
835                 new_item->folder->inbox = new_item;
836         } else if (!strcmp(name, "outbox")) {
837                 new_item->stype = F_OUTBOX;
838                 new_item->folder->outbox = new_item;
839         } else if (!strcmp(name, "draft")) {
840                 new_item->stype = F_DRAFT;
841                 new_item->folder->draft = new_item;
842         } else if (!strcmp(name, "queue")) {
843                 new_item->stype = F_QUEUE;
844                 new_item->folder->queue = new_item;
845         } else if (!strcmp(name, "trash")) {
846                 new_item->stype = F_TRASH;
847                 new_item->folder->trash = new_item;
848         }
849         
850         g_free(foldername);
851         g_free(path);
852         
853         return new_item;
854 }
855
856
857
858 static gboolean mailmbox_scan_required(Folder *folder, FolderItem *_item)
859 {
860         struct mailmbox_folder * mbox;
861         MBOXFolderItem *item = (MBOXFolderItem *)_item;
862         int scan_required;
863         
864         g_return_val_if_fail(folder != NULL, FALSE);
865         g_return_val_if_fail(item != NULL, FALSE);
866
867         if (item->item.path == NULL)
868                 return FALSE;
869         
870         mbox = get_mbox(_item, 0);
871         if (mbox == NULL)
872                 return FALSE;
873         
874         scan_required = (item->old_max_uid != item->mbox->max_uid);
875         
876         item->old_max_uid = item->mbox->max_uid;
877
878         return scan_required;
879 }
880
881
882 static gint mailmbox_rename_folder(Folder *folder,
883     FolderItem *item, const gchar *name)
884 {
885         gchar * path;
886         gchar * foldername;
887
888         g_return_val_if_fail(folder != NULL, -1);
889         g_return_val_if_fail(item != NULL, -1);
890         g_return_val_if_fail(item->path != NULL, -1);
891         g_return_val_if_fail(name != NULL, -1);
892
893         path = mailmbox_get_new_path(item->parent, (gchar *) name);
894         foldername = mailmbox_get_folderitem_name((gchar *) name);
895
896         if (rename(item->path, path) == -1) {
897                 g_free(foldername);
898                 g_free(path);
899                 g_warning("Cannot rename folder item");
900
901                 return -1;
902         }
903         else {
904                 g_free(item->name);
905                 g_free(item->path);
906                 item->path = path;
907                 item->name = foldername;
908                 
909                 return 0;
910         }
911 }
912
913 static gint mailmbox_remove_folder(Folder *folder, FolderItem *item)
914 {
915         g_return_val_if_fail(folder != NULL, -1);
916         g_return_val_if_fail(item != NULL, -1);
917         g_return_val_if_fail(item->path != NULL, -1);
918
919         folder_item_remove(item);
920         return 0;
921 }
922
923 #define MAKE_DIR_IF_NOT_EXIST(dir) \
924 { \
925         if (!is_dir_exist(dir)) { \
926                 if (is_file_exist(dir)) { \
927                         g_warning("File `%s' already exists.\n" \
928                                     "Can't create folder.", dir); \
929                         return -1; \
930                 } \
931                 if (mkdir(dir, S_IRWXU) < 0) { \
932                         FILE_OP_ERROR(dir, "mkdir"); \
933                         return -1; \
934                 } \
935                 if (chmod(dir, S_IRWXU) < 0) \
936                         FILE_OP_ERROR(dir, "chmod"); \
937         } \
938 }
939
940 static gint mailmbox_create_tree(Folder *folder)
941 {
942         gchar *rootpath;
943
944         g_return_val_if_fail(folder != NULL, -1);
945
946         CHDIR_RETURN_VAL_IF_FAIL(get_home_dir(), -1);
947         rootpath = LOCAL_FOLDER(folder)->rootpath;
948         MAKE_DIR_IF_NOT_EXIST(rootpath);
949         CHDIR_RETURN_VAL_IF_FAIL(rootpath, -1);
950
951         return 0;
952 }
953
954 #undef MAKE_DIR_IF_NOT_EXIST
955
956
957 static char * quote_mailbox(char * mb)
958 {
959         char path[PATH_MAX];
960         char * str;
961         size_t remaining;
962         char * p;
963         
964         remaining = sizeof(path) - 1;
965         p = path;
966
967         while (* mb != 0) {
968                 char hex[3];
969                 
970                 if (((* mb >= 'a') && (* mb <= 'z')) ||
971                     ((* mb >= 'A') && (* mb <= 'Z')) ||
972                     ((* mb >= '0') && (* mb <= '9'))) {
973                         if (remaining < 1)
974                                 return NULL;
975                         * p = * mb;
976                         p ++;
977                         remaining --;
978                 }
979                 else {
980                         if (remaining < 3)
981                                 return NULL;
982                         * p = '%';
983                         p ++;
984                         snprintf(p, 3, "%02x", (unsigned char) (* mb));
985                         p += 2;
986                 }
987                 mb ++;
988         }
989         
990         * p = 0;
991
992         str = strdup(path);
993         if (str == NULL)
994                 return NULL;
995         
996         return str;
997 }
998
999 static gchar *mailmbox_item_get_path(Folder *folder, FolderItem *item)
1000 {
1001         gchar *itempath, *path;
1002         gchar * folderpath;
1003
1004         if (item->path == NULL)
1005                 return NULL;
1006
1007         if (folder->name == NULL)
1008                 return NULL;
1009
1010         folderpath = quote_mailbox(folder->name);
1011         if (folderpath == NULL)  
1012                 return NULL;
1013         itempath = quote_mailbox(item->path);
1014         if (itempath == NULL) {
1015                 free(folderpath);
1016                 return NULL;
1017         }
1018         path = g_strconcat(get_mbox_cache_dir(),
1019             G_DIR_SEPARATOR_S, folderpath,
1020             G_DIR_SEPARATOR_S, itempath, NULL);  
1021         free(itempath);
1022         free(folderpath);
1023         
1024         return path;
1025 }