2 * Maildir Plugin -- Maildir++ support for Sylpheed
3 * Copyright (C) 2003-2004 Christoph Hohmann
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.
21 # include "pluginconfig.h"
26 #include <sys/types.h>
35 #include "procheader.h"
38 #include "localfolder.h"
40 #include "mainwindow.h"
41 #include "summaryview.h"
42 #include "messageview.h"
44 #define MAILDIR_FOLDERITEM(item) ((MaildirFolderItem *) item)
46 typedef struct _MaildirFolder MaildirFolder;
47 typedef struct _MaildirFolderItem MaildirFolderItem;
49 static Folder *maildir_folder_new(const gchar * name,
50 const gchar * folder);
51 static void maildir_folder_destroy(Folder * folder);
52 static gint maildir_scan_tree(Folder * folder);
53 static FolderItem *maildir_item_new(Folder * folder);
54 static void maildir_item_destroy(Folder * folder, FolderItem * item);
55 static gchar *maildir_item_get_path(Folder * folder, FolderItem * item);
56 static gint maildir_get_num_list(Folder * folder, FolderItem * item,
57 MsgNumberList ** list,
58 gboolean * old_uids_valid);
59 static MsgInfo *maildir_get_msginfo(Folder * folder, FolderItem * item,
61 static gchar *maildir_fetch_msg(Folder * folder, FolderItem * item,
63 static gint maildir_add_msg(Folder * folder, FolderItem * _dest,
64 const gchar * file, MsgFlags * flags);
65 static gint maildir_copy_msg(Folder * folder, FolderItem * dest,
67 static gint maildir_remove_msg(Folder * folder, FolderItem * _item,
69 static void maildir_change_flags(Folder * folder, FolderItem * item,
70 MsgInfo * msginfo, MsgPermFlags newflags);
71 static FolderItem *maildir_create_folder(Folder * folder,
74 static gint maildir_create_tree(Folder *folder);
75 static void remove_missing_folder_items(Folder *folder);
76 static gint maildir_remove_folder(Folder *folder, FolderItem *item);
77 static gint maildir_rename_folder(Folder *folder, FolderItem *item,
79 static gint maildir_get_flags (Folder *folder, FolderItem *item,
80 MsgInfoList *msglist, GHashTable *msgflags);
82 static gchar *filename_from_utf8(const gchar *path);
83 static gchar *filename_to_utf8(const gchar *path);
85 FolderClass maildir_class;
92 struct _MaildirFolderItem
100 FolderClass *maildir_get_class()
102 if (maildir_class.idstr == NULL) {
103 maildir_class.type = F_MAILDIR;
104 maildir_class.idstr = "maildir";
105 maildir_class.uistr = "Maildir++";
107 /* Folder functions */
108 maildir_class.new_folder = maildir_folder_new;
109 maildir_class.destroy_folder = maildir_folder_destroy;
110 maildir_class.set_xml = folder_local_set_xml;
111 maildir_class.get_xml = folder_local_get_xml;
112 maildir_class.scan_tree = maildir_scan_tree;
113 maildir_class.create_tree = maildir_create_tree;
115 /* FolderItem functions */
116 maildir_class.item_new = maildir_item_new;
117 maildir_class.item_destroy = maildir_item_destroy;
118 maildir_class.item_get_path = maildir_item_get_path;
119 maildir_class.create_folder = maildir_create_folder;
120 maildir_class.remove_folder = maildir_remove_folder;
121 maildir_class.rename_folder = maildir_rename_folder;
122 maildir_class.get_num_list = maildir_get_num_list;
124 /* Message functions */
125 maildir_class.get_msginfo = maildir_get_msginfo;
126 maildir_class.fetch_msg = maildir_fetch_msg;
127 maildir_class.add_msg = maildir_add_msg;
128 maildir_class.copy_msg = maildir_copy_msg;
129 maildir_class.remove_msg = maildir_remove_msg;
130 maildir_class.change_flags = maildir_change_flags;
131 maildir_class.get_flags = maildir_get_flags;
134 return &maildir_class;
137 static Folder *maildir_folder_new(const gchar * name,
140 MaildirFolder *folder;
142 folder = g_new0(MaildirFolder, 1);
143 FOLDER(folder)->klass = &maildir_class;
144 folder_local_folder_init(FOLDER(folder), name, path);
146 return FOLDER(folder);
149 static void maildir_folder_destroy(Folder *_folder)
151 MaildirFolder *folder = (MaildirFolder *) _folder;
153 folder_local_folder_destroy(LOCAL_FOLDER(folder));
156 static gint open_database(MaildirFolderItem *item)
158 gchar *path, *database;
160 g_return_val_if_fail(item->db == NULL, -1);
162 path = maildir_item_get_path(FOLDER_ITEM(item)->folder, FOLDER_ITEM(item));
163 Xstrcat_a(database, path, G_DIR_SEPARATOR_S "sylpheed_uid.db", return -1);
166 item->db = uiddb_open(database);
167 g_return_val_if_fail(item->db != NULL, -1);
172 static void close_database(MaildirFolderItem *item)
174 g_return_if_fail(item->db != NULL);
176 uiddb_close(item->db);
180 static FolderItem *maildir_item_new(Folder *folder)
182 MaildirFolderItem *item;
184 item = g_new0(MaildirFolderItem, 1);
188 return (FolderItem *) item;
192 static void maildir_item_destroy(Folder *folder, FolderItem *_item)
194 MaildirFolderItem *item = (MaildirFolderItem *)_item;
196 g_return_if_fail(item != NULL);
201 static gchar *maildir_item_get_path(Folder *folder, FolderItem *item)
203 gchar *folder_path, *path, *real_path;
205 g_return_val_if_fail(folder != NULL, NULL);
206 g_return_val_if_fail(item != NULL, NULL);
208 folder_path = g_strdup(LOCAL_FOLDER(folder)->rootpath);
209 g_return_val_if_fail(folder_path != NULL, NULL);
211 if (g_path_is_absolute(folder_path)) {
212 if (item->path && strcmp(item->path, ".inbox"))
213 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
216 path = g_strdup(folder_path);
218 if (item->path && strcmp(item->path, ".inbox"))
219 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
220 folder_path, G_DIR_SEPARATOR_S,
223 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
228 real_path = filename_from_utf8(path);
234 static void build_tree(GNode *node, glob_t *globbuf)
237 FolderItem *parent = FOLDER_ITEM(node->data);
238 gchar *prefix = parent->path ? filename_from_utf8(parent->path) : g_strdup("");
239 Folder *folder = parent->folder;
241 for (i = 0; i < globbuf->gl_pathc; i++) {
246 gchar *tmpstr, *dirname_utf8, *foldername_utf8;
249 dirname = g_path_get_basename(globbuf->gl_pathv[i]);
250 foldername = &(dirname[strlen(prefix) + 1]);
252 if (dirname[0] == '.' && dirname[1] == '\0') {
256 if (strncmp(dirname, prefix, strlen(prefix))) {
260 if (dirname[strlen(prefix)] != '.') {
264 if (strchr(foldername, '.') != NULL) {
269 if (!is_dir_exist(globbuf->gl_pathv[i])) {
273 tmpstr = g_strconcat(globbuf->gl_pathv[i], "/cur", NULL);
274 res = is_dir_exist(tmpstr);
280 dirname_utf8 = filename_to_utf8(dirname);
281 foldername_utf8 = filename_to_utf8(foldername);
283 /* don't add items that already exist in the tree */
284 newitem = folder_find_child_item_by_name(parent, dirname_utf8);
285 if (newitem == NULL) {
286 newitem = folder_item_new(parent->folder, foldername_utf8, dirname_utf8);
287 newitem->folder = folder;
289 newnode = g_node_new(newitem);
290 newitem->node = newnode;
291 g_node_append(node, newnode);
293 debug_print("added item %s\n", newitem->path);
296 g_free(dirname_utf8);
297 g_free(foldername_utf8);
300 if (!folder->outbox && !strcmp(dirname, "." OUTBOX_DIR)) {
301 newitem->stype = F_OUTBOX;
302 folder->outbox = newitem;
303 } else if (!folder->draft && !strcmp(dirname, "." DRAFT_DIR)) {
304 newitem->stype = F_DRAFT;
305 folder->draft = newitem;
306 } else if (!folder->queue && !strcmp(dirname, "." QUEUE_DIR)) {
307 newitem->stype = F_QUEUE;
308 folder->queue = newitem;
309 } else if (!folder->trash && !strcmp(dirname, "." TRASH_DIR)) {
310 newitem->stype = F_TRASH;
311 folder->trash = newitem;
315 build_tree(newitem->node, globbuf);
321 static gint maildir_scan_tree(Folder *folder)
323 FolderItem *rootitem, *inboxitem;
324 GNode *rootnode, *inboxnode;
326 gchar *rootpath, *globpat;
328 g_return_val_if_fail(folder != NULL, -1);
331 rootitem = folder_item_new(folder, folder->name, NULL);
332 rootitem->folder = folder;
333 rootnode = g_node_new(rootitem);
334 folder->node = rootnode;
335 rootitem->node = rootnode;
337 rootitem = FOLDER_ITEM(folder->node->data);
338 rootnode = folder->node;
341 /* Add inbox folder */
342 if (!folder->inbox) {
343 inboxitem = folder_item_new(folder, "inbox", ".inbox");
344 inboxitem->folder = folder;
345 inboxitem->stype = F_INBOX;
346 inboxnode = g_node_new(inboxitem);
347 inboxitem->node = inboxnode;
348 folder->inbox = inboxitem;
349 g_node_append(rootnode, inboxnode);
352 rootpath = folder_item_get_path(rootitem);
354 /* clear special folders to make sure we don't have invalid references
355 after remove_missing_folder_items */
356 folder->outbox = NULL;
357 folder->draft = NULL;
358 folder->queue = NULL;
359 folder->trash = NULL;
361 debug_print("scanning tree %s\n", rootpath);
362 maildir_create_tree(folder);
363 remove_missing_folder_items(folder);
365 globpat = g_strconcat(rootpath, G_DIR_SEPARATOR_S ".*", NULL);
367 glob(globpat, 0, NULL, &globbuf);
369 build_tree(rootnode, &globbuf);
375 static gchar *get_filename_for_msgdata(MessageData *msgdata)
379 if (msgdata->info[0])
380 filename = g_strconcat(msgdata->dir, G_DIR_SEPARATOR_S,
381 msgdata->uniq, ":", msgdata->info, NULL);
383 filename = g_strconcat(msgdata->dir, G_DIR_SEPARATOR_S,
384 msgdata->uniq, NULL);
389 static MessageData *get_msgdata_for_filename(const gchar *filename)
391 MessageData *msgdata;
392 const gchar *tmpfilename;
393 gchar **pathsplit, **namesplit;
395 tmpfilename = strrchr(filename, G_DIR_SEPARATOR);
396 if (tmpfilename == NULL || tmpfilename == filename)
400 while (tmpfilename > filename && tmpfilename[0] != G_DIR_SEPARATOR)
402 if (tmpfilename[0] == G_DIR_SEPARATOR)
405 pathsplit = g_strsplit(tmpfilename, G_DIR_SEPARATOR_S, 2);
406 if (pathsplit[1] == NULL) {
407 g_strfreev(pathsplit);
411 namesplit = g_strsplit(pathsplit[1], ":", 2);
413 msgdata = g_new0(MessageData, 1);
415 msgdata->dir = g_strdup(pathsplit[0]);
416 msgdata->uniq = g_strdup(namesplit[0]);
417 if (namesplit[1] != NULL)
418 msgdata->info = g_strdup(namesplit[1]);
420 msgdata->info = g_strdup("");
422 g_strfreev(namesplit);
423 g_strfreev(pathsplit);
428 static guint32 get_uid_for_filename(MaildirFolderItem *item, const gchar *filename)
431 MessageData *msgdata;
434 g_return_val_if_fail(item->db != NULL, 0);
436 uniq = strrchr(filename, G_DIR_SEPARATOR);
442 Xstrdup_a(uniq, uniq, return 0);
443 info = strchr(uniq, ':');
449 msgdata = uiddb_get_entry_for_uniq(item->db, uniq);
450 if (msgdata == NULL) {
451 msgdata = get_msgdata_for_filename(filename);
452 if (msgdata == NULL) {
455 msgdata->uid = uiddb_get_new_uid(item->db);
457 uiddb_insert_entry(item->db, msgdata);
458 } else if (strcmp(msgdata->info, info)) {
459 uiddb_delete_entry(item->db, msgdata->uid);
461 g_free(msgdata->info);
462 msgdata->info = g_strdup(info);
464 uiddb_insert_entry(item->db, msgdata);
468 uiddb_free_msgdata(msgdata);
473 static MessageData *get_msgdata_for_uid(MaildirFolderItem *item, guint32 uid)
475 MessageData *msgdata;
476 gchar *path, *msgname, *filename;
479 g_return_val_if_fail(item->db != NULL, NULL);
481 msgdata = uiddb_get_entry_for_uid(item->db, uid);
482 if (msgdata == NULL) {
485 path = maildir_item_get_path(FOLDER_ITEM(item)->folder, FOLDER_ITEM(item));
487 msgname = get_filename_for_msgdata(msgdata);
488 filename = g_strconcat(path, G_DIR_SEPARATOR_S, msgname, NULL);
491 if (is_file_exist(filename)) {
496 debug_print("researching for %s\n", msgdata->uniq);
497 /* delete old entry */
499 uiddb_delete_entry(item->db, uid);
501 /* try to find file with same uniq and different info */
502 filename = g_strconcat(path, G_DIR_SEPARATOR_S, DIR_NEW, G_DIR_SEPARATOR_S, msgdata->uniq, NULL);
503 if (!is_file_exist(filename)) {
506 filename = g_strconcat(path, G_DIR_SEPARATOR_S, DIR_CUR, G_DIR_SEPARATOR_S, msgdata->uniq, ":*", NULL);
507 glob(filename, 0, NULL, &globbuf);
513 if (globbuf.gl_pathc > 0)
514 filename = g_strdup(globbuf.gl_pathv[0]);
517 uiddb_free_msgdata(msgdata);
520 /* if found: update database and return new entry */
521 if (filename != NULL) {
522 debug_print("found %s\n", filename);
524 msgdata = get_msgdata_for_filename(filename);
527 uiddb_insert_entry(item->db, msgdata);
533 static gchar *get_filepath_for_msgdata(MaildirFolderItem *item, MessageData *msgdata)
535 gchar *path, *msgname, *filename;
537 path = maildir_item_get_path(FOLDER_ITEM(item)->folder, FOLDER_ITEM(item));
538 msgname = get_filename_for_msgdata(msgdata);
539 filename = g_strconcat(path, G_DIR_SEPARATOR_S, msgname, NULL);
546 static gchar *get_filepath_for_uid(MaildirFolderItem *item, guint32 uid)
548 MessageData *msgdata;
551 g_return_val_if_fail(item->db != NULL, NULL);
553 msgdata = get_msgdata_for_uid(item, uid);
554 if (msgdata == NULL) {
557 filename = get_filepath_for_msgdata(item, msgdata);
558 uiddb_free_msgdata(msgdata);
563 static gint maildir_uid_compare(gconstpointer a, gconstpointer b)
565 guint gint_a = GPOINTER_TO_INT(a);
566 guint gint_b = GPOINTER_TO_INT(b);
568 return (gint_a - gint_b);
571 static gint maildir_get_num_list(Folder *folder, FolderItem *item,
572 MsgNumberList ** list, gboolean *old_uids_valid)
574 gchar *path, *globpattern;
577 MsgNumberList * tail;
579 g_return_val_if_fail(open_database(MAILDIR_FOLDERITEM(item)) == 0, -1);
581 *old_uids_valid = TRUE;
584 path = maildir_item_get_path(folder, item);
586 globpattern = g_strconcat(path, G_DIR_SEPARATOR_S, DIR_CUR, G_DIR_SEPARATOR_S, "*", NULL);
587 glob(globpattern, GLOB_NOSORT, NULL, &globbuf);
590 globpattern = g_strconcat(path, G_DIR_SEPARATOR_S, DIR_NEW, G_DIR_SEPARATOR_S, "*", NULL);
591 glob(globpattern, GLOB_NOSORT | GLOB_APPEND, NULL, &globbuf);
596 tail = g_slist_last(*list);
598 for (i = 0; i < globbuf.gl_pathc; i++) {
601 uid = get_uid_for_filename((MaildirFolderItem *) item, globbuf.gl_pathv[i]);
603 tail = g_slist_append(tail, GINT_TO_POINTER(uid));
604 tail = g_slist_last(tail);
605 if (!*list) *list = tail;
610 *list = g_slist_sort(*list, maildir_uid_compare);
612 uiddb_delete_entries_not_in_list(((MaildirFolderItem *) item)->db, *list);
614 close_database(MAILDIR_FOLDERITEM(item));
615 return g_slist_length(*list);
618 static MsgInfo *maildir_parse_msg(const gchar *file, FolderItem *item)
623 flags.perm_flags = MSG_NEW|MSG_UNREAD;
626 g_return_val_if_fail(item != NULL, NULL);
627 g_return_val_if_fail(file != NULL, NULL);
629 if (item->stype == F_QUEUE) {
630 MSG_SET_TMP_FLAGS(flags, MSG_QUEUED);
631 } else if (item->stype == F_DRAFT) {
632 MSG_SET_TMP_FLAGS(flags, MSG_DRAFT);
635 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
636 if (!msginfo) return NULL;
638 msginfo->msgnum = atoi(file);
639 msginfo->folder = item;
642 /* this is already done by procheader_parse_file */
643 if (stat(file, &s) < 0) {
644 FILE_OP_ERROR(file, "stat");
648 msginfo->size = (goffset)s.st_size;
649 msginfo->mtime = s.st_mtime;
656 static MsgInfo *maildir_get_msginfo(Folder * folder,
657 FolderItem * item, gint num)
662 g_return_val_if_fail(item != NULL, NULL);
663 g_return_val_if_fail(num > 0, NULL);
665 file = maildir_fetch_msg(folder, item, num);
666 if (!file) return NULL;
668 msginfo = maildir_parse_msg(file, item);
670 msginfo->msgnum = num;
677 static gchar *maildir_fetch_msg(Folder * folder, FolderItem * item,
682 g_return_val_if_fail(open_database(MAILDIR_FOLDERITEM(item)) == 0, NULL);
683 filename = get_filepath_for_uid((MaildirFolderItem *) item, num);
684 close_database(MAILDIR_FOLDERITEM(item));
689 static gchar *generate_uniq()
691 gchar hostname[32], *strptr;
695 gethostname(hostname, 32);
698 strptr = &hostname[0];
699 while (*strptr != '\0') {
707 gettimeofday(&tv, NULL);
709 return g_strdup_printf("%d.P%dQ%dM%d.%s", (int) tv.tv_sec, getpid(), q++, (int) tv.tv_usec, hostname);
712 static gchar *get_infostr(MsgPermFlags permflags)
714 if (permflags & MSG_NEW)
717 return g_strconcat("2,",
718 permflags & MSG_MARKED ? "F" : "",
719 permflags & MSG_FORWARDED ? "P" : "",
720 permflags & MSG_REPLIED ? "R" : "",
721 !(permflags & MSG_UNREAD) ? "S" : "",
725 static gint add_file_to_maildir(MaildirFolderItem *item, const gchar *file, MsgFlags *flags)
727 MessageData *msgdata;
728 gchar *tmpname, *destname;
731 g_return_val_if_fail(item != NULL, -1);
732 g_return_val_if_fail(open_database(MAILDIR_FOLDERITEM(item)) == 0, -1);
734 msgdata = g_new0(MessageData, 1);
735 msgdata->uniq = generate_uniq();
737 msgdata->info = get_infostr(flags->perm_flags);
739 msgdata->info = g_strdup("");
740 msgdata->uid = uiddb_get_new_uid(item->db);
742 msgdata->dir = DIR_TMP;
743 tmpname = get_filepath_for_msgdata(item, msgdata);
746 msgdata->dir = g_strdup(flags->perm_flags & MSG_NEW ? DIR_NEW : DIR_CUR);
748 msgdata->dir = g_strdup(DIR_NEW);
750 if (copy_file(file, tmpname, FALSE) < 0) {
754 destname = get_filepath_for_msgdata(item, msgdata);
755 if (rename(tmpname, destname) < 0) {
760 uiddb_insert_entry(item->db, msgdata);
765 uiddb_free_msgdata(msgdata);
767 close_database(MAILDIR_FOLDERITEM(item));
771 static gint maildir_add_msg(Folder *folder, FolderItem *_dest, const gchar *file, MsgFlags *flags)
773 MaildirFolderItem *dest = MAILDIR_FOLDERITEM(_dest);
775 g_return_val_if_fail(folder != NULL, -1);
776 g_return_val_if_fail(dest != NULL, -1);
777 g_return_val_if_fail(file != NULL, -1);
779 return add_file_to_maildir(dest, file, flags);
782 static gint maildir_copy_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
786 gboolean delsrc = FALSE;
788 g_return_val_if_fail(folder != NULL, -1);
789 g_return_val_if_fail(dest != NULL, -1);
790 g_return_val_if_fail(msginfo != NULL, -1);
792 srcfile = procmsg_get_message_file(msginfo);
796 if ((MSG_IS_QUEUED(msginfo->flags) || MSG_IS_DRAFT(msginfo->flags))
797 && dest->stype != F_QUEUE && dest->stype != F_DRAFT) {
800 tmpfile = get_tmp_file();
801 if (procmsg_remove_special_headers(srcfile, tmpfile) != 0) {
811 ret = add_file_to_maildir(MAILDIR_FOLDERITEM(dest), srcfile, &msginfo->flags);
820 static gint maildir_remove_msg(Folder *folder, FolderItem *_item, gint num)
822 MaildirFolderItem *item = MAILDIR_FOLDERITEM(_item);
826 g_return_val_if_fail(folder != NULL, -1);
827 g_return_val_if_fail(item != NULL, -1);
828 g_return_val_if_fail(num > 0, -1);
830 g_return_val_if_fail(open_database(MAILDIR_FOLDERITEM(item)) == 0, -1);
832 filename = get_filepath_for_uid(item, num);
833 if (filename == NULL) {
838 ret = unlink(filename);
840 uiddb_delete_entry(item->db, num);
845 close_database(MAILDIR_FOLDERITEM(item));
849 static void maildir_change_flags(Folder *folder, FolderItem *_item, MsgInfo *msginfo, MsgPermFlags newflags)
851 MaildirFolderItem *item = MAILDIR_FOLDERITEM(_item);
852 MessageData *msgdata;
853 gchar *oldname, *newinfo, *newdir;
854 gboolean renamefile = FALSE;
856 g_return_if_fail(open_database(MAILDIR_FOLDERITEM(item)) == 0);
858 msgdata = get_msgdata_for_uid(item, msginfo->msgnum);
862 oldname = get_filepath_for_msgdata(item, msgdata);
864 newinfo = get_infostr(newflags);
865 if (strcmp(msgdata->info, newinfo)) {
866 g_free(msgdata->info);
867 msgdata->info = newinfo;
872 newdir = g_strdup(newflags & MSG_NEW ? DIR_NEW : DIR_CUR);
873 if (strcmp(msgdata->dir, newdir)) {
874 g_free(msgdata->dir);
875 msgdata->dir = newdir;
883 newname = get_filepath_for_msgdata(item, msgdata);
884 if (rename(oldname, newname) == 0) {
885 uiddb_delete_entry(item->db, msgdata->uid);
886 uiddb_insert_entry(item->db, msgdata);
887 msginfo->flags.perm_flags = newflags;
891 msginfo->flags.perm_flags = newflags;
895 uiddb_free_msgdata(msgdata);
897 close_database(MAILDIR_FOLDERITEM(item));
900 MainWindow *mainwin = mainwindow_get_mainwindow();
901 SummaryView *summaryview = mainwin->summaryview;
902 gint displayed_msgnum = -1;
903 if (summaryview->displayed)
904 displayed_msgnum = summary_get_msgnum(summaryview,
905 summaryview->displayed);
906 if (displayed_msgnum == msginfo->msgnum
907 && summaryview->folder_item == msginfo->folder)
909 summaryview->messageview,
911 summaryview->messageview->all_headers);
917 close_database(MAILDIR_FOLDERITEM(item));
920 static gboolean setup_new_folder(const gchar * path, gboolean subfolder)
922 gchar *curpath, *newpath, *tmppath, *maildirfolder;
923 gboolean failed = FALSE;
925 g_return_val_if_fail(path != NULL, TRUE);
927 curpath = g_strconcat(path, G_DIR_SEPARATOR_S, DIR_CUR, NULL);
928 newpath = g_strconcat(path, G_DIR_SEPARATOR_S, DIR_NEW, NULL);
929 tmppath = g_strconcat(path, G_DIR_SEPARATOR_S, DIR_TMP, NULL);
931 if (!is_dir_exist(path))
932 if (mkdir(path, DIR_PERMISSION) != 0)
934 if (!is_dir_exist(curpath))
935 if (mkdir(curpath, DIR_PERMISSION) != 0)
937 if (!is_dir_exist(newpath))
938 if (mkdir(newpath, DIR_PERMISSION) != 0)
940 if (!is_dir_exist(tmppath))
941 if (mkdir(tmppath, DIR_PERMISSION) != 0)
946 maildirfolder = g_strconcat(path, G_DIR_SEPARATOR_S, "maildirfolder", NULL);
947 res = open(maildirfolder, O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY,
948 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
969 static FolderItem *maildir_create_folder(Folder * folder,
973 gchar *folder_path, *path, *real_path;
974 FolderItem *newitem = NULL;
975 gboolean failed = FALSE;
977 g_return_val_if_fail(folder != NULL, NULL);
978 g_return_val_if_fail(parent != NULL, NULL);
979 g_return_val_if_fail(name != NULL, NULL);
981 folder_path = g_strdup(LOCAL_FOLDER(folder)->rootpath);
982 g_return_val_if_fail(folder_path != NULL, NULL);
984 if (g_path_is_absolute(folder_path)) {
985 path = g_strconcat(folder_path, G_DIR_SEPARATOR_S,
986 parent->path != NULL ? parent->path : "",
989 path = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
990 folder_path, G_DIR_SEPARATOR_S,
991 parent->path != NULL ? parent->path : "",
996 debug_print("creating new maildir folder: %s\n", path);
998 real_path = filename_from_utf8(path);
1001 failed = setup_new_folder(real_path, TRUE);
1007 path = g_strconcat((parent->path != NULL) ? parent->path : "", ".", name, NULL);
1008 newitem = folder_item_new(folder, name, path);
1009 folder_item_append(parent, newitem);
1015 static gint maildir_create_tree(Folder *folder)
1017 gchar *rootpath, *real_rootpath, *folder_path, *path;
1019 g_return_val_if_fail(folder != NULL, -1);
1021 folder_path = g_strdup(LOCAL_FOLDER(folder)->rootpath);
1022 g_return_val_if_fail(folder_path != NULL, -1);
1024 if (g_path_is_absolute(folder_path)) {
1025 rootpath = g_strdup(folder_path);
1027 rootpath = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1030 g_free(folder_path);
1032 real_rootpath = filename_from_utf8(rootpath);
1035 debug_print("creating new maildir tree: %s\n", real_rootpath);
1036 if (!is_dir_exist(real_rootpath)) {
1037 if (is_file_exist(real_rootpath)) {
1038 g_warning("File `%s' already exists.\n"
1039 "Can't create folder.", real_rootpath);
1042 if (make_dir_hier(real_rootpath) < 0)
1046 if (setup_new_folder(real_rootpath, FALSE)) { /* create INBOX */
1047 g_free(real_rootpath);
1050 path = g_strconcat(real_rootpath, G_DIR_SEPARATOR_S, "." OUTBOX_DIR, NULL);
1051 if (setup_new_folder(path, TRUE)) {
1053 g_free(real_rootpath);
1057 path = g_strconcat(real_rootpath, G_DIR_SEPARATOR_S, "." QUEUE_DIR, NULL);
1058 if (setup_new_folder(path, TRUE)) {
1060 g_free(real_rootpath);
1064 path = g_strconcat(real_rootpath, G_DIR_SEPARATOR_S, "." DRAFT_DIR, NULL);
1065 if (setup_new_folder(path, TRUE)) {
1067 g_free(real_rootpath);
1071 path = g_strconcat(real_rootpath, G_DIR_SEPARATOR_S, "." TRASH_DIR, NULL);
1072 if (setup_new_folder(path, TRUE)) {
1074 g_free(real_rootpath);
1078 g_free(real_rootpath);
1083 static gboolean remove_missing_folder_items_func(GNode *node, gpointer data)
1088 g_return_val_if_fail(node->data != NULL, FALSE);
1090 if (G_NODE_IS_ROOT(node))
1093 item = FOLDER_ITEM(node->data);
1095 if (item->stype == F_INBOX)
1098 path = folder_item_get_path(item);
1099 if (!is_dir_exist(path)) {
1100 debug_print("folder '%s' not found. removing...\n", path);
1101 folder_item_remove(item);
1108 static void remove_missing_folder_items(Folder *folder)
1110 g_return_if_fail(folder != NULL);
1112 debug_print("searching missing folders...\n");
1114 g_node_traverse(folder->node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
1115 remove_missing_folder_items_func, folder);
1118 static gboolean remove_folder_func(GNode *node, gpointer data)
1123 g_return_val_if_fail(node->data != NULL, FALSE);
1125 if (G_NODE_IS_ROOT(node))
1128 item = FOLDER_ITEM(node->data);
1130 if (item->stype != F_NORMAL)
1133 path = folder_item_get_path(item);
1134 debug_print("removing directory %s\n", path);
1135 if (remove_dir_recursive(path) < 0) {
1136 g_warning("can't remove directory `%s'\n", path);
1143 folder_item_remove(item);
1148 static gint maildir_remove_folder(Folder *folder, FolderItem *item)
1152 g_return_val_if_fail(folder != NULL, -1);
1153 g_return_val_if_fail(item != NULL, -1);
1154 g_return_val_if_fail(item->path != NULL, -1);
1155 g_return_val_if_fail(item->stype == F_NORMAL, -1);
1157 debug_print("removing folder %s\n", item->path);
1159 g_node_traverse(item->node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
1160 remove_folder_func, &res);
1171 static gboolean rename_folder_func(GNode *node, gpointer data)
1174 gchar *oldpath, *newpath, *newitempath;
1175 gchar *suffix, *real_path, *real_rootpath;
1176 struct RenameData *renamedata = data;
1178 g_return_val_if_fail(node->data != NULL, FALSE);
1180 if (G_NODE_IS_ROOT(node))
1183 item = FOLDER_ITEM(node->data);
1185 if (item->stype != F_NORMAL)
1188 real_rootpath = filename_from_utf8(LOCAL_FOLDER(item->folder)->rootpath);
1189 real_path = filename_from_utf8(item->path);
1190 suffix = real_path + renamedata->oldprefixlen;
1192 oldpath = folder_item_get_path(item);
1193 newitempath = g_strconcat(renamedata->newprefix, suffix, NULL);
1194 newpath = g_strconcat(real_rootpath, G_DIR_SEPARATOR_S, newitempath, NULL);
1196 g_free(real_rootpath);
1198 debug_print("renaming directory %s to %s\n", oldpath, newpath);
1200 if (rename(oldpath, newpath) < 0) {
1201 FILE_OP_ERROR(oldpath, "rename");
1204 item->path = filename_to_utf8(newitempath);
1207 g_free(newitempath);
1214 static gint maildir_rename_folder(Folder *folder, FolderItem *item,
1217 struct RenameData renamedata;
1218 gchar *p, *real_path, *real_name;
1220 g_return_val_if_fail(folder != NULL, -1);
1221 g_return_val_if_fail(item != NULL, -1);
1222 g_return_val_if_fail(item->path != NULL, -1);
1223 g_return_val_if_fail(name != NULL, -1);
1225 debug_print("renaming folder %s to %s\n", item->path, name);
1228 item->name = g_strdup(name);
1230 real_path = filename_from_utf8(item->path);
1231 real_name = filename_from_utf8(name);
1233 renamedata.oldprefixlen = strlen(real_path);
1234 p = strrchr(real_path, '.');
1236 p = g_strndup(real_path, p - real_path + 1);
1239 renamedata.newprefix = g_strconcat(p, real_name, NULL);
1244 g_node_traverse(item->node, G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1245 rename_folder_func, &renamedata);
1247 g_free(renamedata.newprefix);
1252 static gint get_flags_for_msgdata(MessageData *msgdata, MsgPermFlags *flags)
1256 g_return_val_if_fail(msgdata != NULL, -1);
1257 g_return_val_if_fail(msgdata->info != NULL, -1);
1259 if ((msgdata->info[0] != '2') && (msgdata->info[1] != ','))
1262 *flags = MSG_UNREAD;
1263 for (i = 2; i < strlen(msgdata->info); i++) {
1264 switch (msgdata->info[i]) {
1266 *flags |= MSG_MARKED;
1269 *flags |= MSG_FORWARDED;
1272 *flags |= MSG_REPLIED;
1275 *flags &= ~MSG_UNREAD;
1283 static gint maildir_get_flags (Folder *folder, FolderItem *item,
1284 MsgInfoList *msglist, GHashTable *msgflags)
1288 MessageData *msgdata;
1291 g_return_val_if_fail(folder != NULL, -1);
1292 g_return_val_if_fail(item != NULL, -1);
1293 g_return_val_if_fail(msglist != NULL, -1);
1294 g_return_val_if_fail(msgflags != NULL, -1);
1295 g_return_val_if_fail(open_database(MAILDIR_FOLDERITEM(item)) == 0, -1);
1297 for (elem = msglist; elem != NULL; elem = g_slist_next(elem)) {
1298 msginfo = (MsgInfo*) elem->data;
1299 msgdata = uiddb_get_entry_for_uid(MAILDIR_FOLDERITEM(item)->db, msginfo->msgnum);
1300 if (msgdata == NULL)
1303 if (get_flags_for_msgdata(msgdata, &flags) < 0)
1306 flags = flags | (msginfo->flags.perm_flags &
1307 ~(MSG_MARKED | MSG_FORWARDED | MSG_REPLIED | MSG_UNREAD | ((flags & MSG_UNREAD) == 0 ? MSG_NEW : 0)));
1308 g_hash_table_insert(msgflags, msginfo, GINT_TO_POINTER(flags));
1310 uiddb_free_msgdata(msgdata);
1313 close_database(MAILDIR_FOLDERITEM(item));
1317 static gchar *filename_from_utf8(const gchar *path)
1319 gchar *real_path = g_filename_from_utf8(path, -1, NULL, NULL, NULL);
1322 g_warning("filename_from_utf8: failed to convert character set\n");
1323 real_path = g_strdup(path);
1329 static gchar *filename_to_utf8(const gchar *path)
1331 gchar *utf8path = g_filename_to_utf8(path, -1, NULL, NULL, NULL);
1333 g_warning("filename_to_utf8: failed to convert character set\n");
1334 utf8path = g_strdup(path);