/* code for handling local mail boxes */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "Evolution.h" #include "evolution-storage.h" #include "evolution-shell-component.h" #include "folder-browser.h" #include "camel/camel.h" #include "filter/vfolder-context.h" #include "filter/vfolder-rule.h" #include "filter/vfolder-editor.h" #include "mail.h" #include "mail-local.h" #define d(x) struct _local_meta { char *path; /* path of metainfo file */ char *format; /* format of mailbox */ char *name; /* name of mbox itself */ }; static struct _local_meta * load_metainfo(const char *path) { xmlDocPtr doc; xmlNodePtr node; struct _local_meta *meta; meta = g_malloc0(sizeof(*meta)); meta->path = g_strdup(path); printf("Loading folder metainfo from : %s\n", meta->path); doc = xmlParseFile(meta->path); if (doc == NULL) { goto dodefault; } node = doc->root; if (strcmp(node->name, "folderinfo")) { goto dodefault; } node = node->childs; while (node) { if (!strcmp(node->name, "folder")) { meta->format = xmlGetProp(node, "type"); meta->name = xmlGetProp(node, "name"); } node = node->next; } xmlFreeDoc(doc); return meta; dodefault: meta->format = g_strdup("mbox"); /* defaults */ meta->name = g_strdup("mbox"); if (doc) xmlFreeDoc(doc); return meta; } static void free_metainfo(struct _local_meta *meta) { g_free(meta->path); g_free(meta->format); g_free(meta->name); g_free(meta); } static int save_metainfo(struct _local_meta *meta) { xmlDocPtr doc; xmlNodePtr root, node; int ret; printf("Saving folder metainfo to : %s\n", meta->path); doc = xmlNewDoc("1.0"); root = xmlNewDocNode(doc, NULL, "folderinfo", NULL); xmlDocSetRootElement(doc, root); node = xmlNewChild(root, NULL, "folder", NULL); xmlSetProp(node, "type", meta->format); xmlSetProp(node, "name", meta->name); ret = xmlSaveFile(meta->path, doc); xmlFreeDoc(doc); return ret; } CamelFolder * local_uri_to_folder(const char *uri, CamelException *ex) { CamelURL *url; char *metapath; char *storename; CamelStore *store; CamelFolder *folder = NULL; struct _local_meta *meta; if (strncmp(uri, "file:", 5)) { return NULL; } printf("opening local folder %s\n", uri); /* get the actual location of the mailbox */ url = camel_url_new(uri, ex); if (camel_exception_is_set(ex)) { return NULL; } metapath = g_strdup_printf("%s/local-metadata.xml", url->path); meta = load_metainfo(metapath); g_free(metapath); /* change file: to format: */ camel_url_set_protocol(url, meta->format); storename = camel_url_to_string(url, TRUE); printf("store name is %s\n", storename); store = camel_session_get_store(session, storename, ex); g_free(storename); if (store) { folder = camel_store_get_folder(store, meta->name, FALSE, ex); gtk_object_unref((GtkObject *)store); } camel_url_free(url); free_metainfo(meta); return folder; } /* open new copy old->new close old rename old oldsave rename new old open oldsave delete oldsave close old rename oldtmp open new open oldtmp copy oldtmp new close oldtmp close oldnew */ static void update_progress(GtkProgress *progress, char *fmt, float percent) { if (fmt) gtk_progress_set_format_string(progress, fmt); gtk_progress_set_percentage(progress, percent); while( gtk_events_pending() ) gtk_main_iteration(); } static void do_local_reconfigure_folder(FolderBrowser *fb, char *newtype, GtkProgress *progress, CamelException *ex) { CamelStore *fromstore, *tostore; char *fromurl, *tourl, *uri; CamelFolder *fromfolder, *tofolder; GPtrArray *uids; int i; char *metapath; char *tmpname; CamelURL *url; struct _local_meta *meta; printf("reconfiguring folder: %s to type %s\n", fb->uri, newtype); /* get the actual location of the mailbox */ url = camel_url_new(fb->uri, ex); if (url == NULL || camel_exception_is_set(ex)) { camel_exception_free(ex); g_warning("%s is not a workable url!", fb->uri); return; } metapath = g_strdup_printf("%s/local-metadata.xml", url->path); meta = load_metainfo(metapath); g_free(metapath); /* first, 'close' the old folder */ if (fb->folder != NULL) { update_progress(progress, "Closing current folder", 0.0); printf("Closing old folder ...\n"); camel_folder_sync(fb->folder, FALSE, ex); gtk_object_unref (GTK_OBJECT (fb->folder)); fb->folder = NULL; } camel_url_set_protocol(url, meta->format); fromurl = camel_url_to_string(url, TRUE); camel_url_set_protocol(url, newtype); tourl = camel_url_to_string(url, TRUE); printf("opening stores %s and %s\n", fromurl, tourl); fromstore = camel_session_get_store(session, fromurl, ex); if (camel_exception_is_set(ex)) { return; } tostore = camel_session_get_store(session, tourl, ex); if (camel_exception_is_set(ex)) { return; } /* rename the old mbox and open it again */ tmpname = g_strdup_printf("%s_reconfig", meta->name); printf("renaming mbox to mboxtmp, and opening it\n"); update_progress(progress, "Renaming old folder and opening", 0.0); camel_store_rename_folder(fromstore, meta->name, tmpname, ex); if (camel_exception_is_set(ex)) { return; } fromfolder = camel_store_get_folder(fromstore, tmpname, TRUE, ex); if (fromfolder == NULL || camel_exception_is_set(ex)) { /* try and recover ... */ camel_store_rename_folder(fromstore, tmpname, meta->name, ex); return; } /* create a new mbox */ printf("Creating the destination mbox\n"); update_progress(progress, "Creating new folder", 0.0); tofolder = camel_store_get_folder(tostore, meta->name, TRUE, ex); if (tofolder == NULL || camel_exception_is_set(ex)) { printf("cannot open destination folder\n"); /* try and recover ... */ camel_store_rename_folder(fromstore, tmpname, meta->name, ex); return; } /* copy the messages across */ uids = camel_folder_get_uids (fromfolder); printf("got %d messages in source\n", uids->len); update_progress(progress, "Copying messages", 0.0); for (i = 0; i < uids->len; i++) { CamelMimeMessage *msg; char *uid = uids->pdata[i]; const CamelMessageInfo *info; update_progress(progress, NULL, i/uids->len); printf("copying message %s\n", uid); msg = camel_folder_get_message(fromfolder, uid, ex); if (camel_exception_is_set(ex)) { /* we're fucked a bit ... */ /* need to: delete new folder rename old back again */ g_warning("cannot get message"); return; } info = camel_folder_get_message_info(fromfolder, uid); camel_folder_append_message(tofolder, msg, info, ex); if (camel_exception_is_set(ex)) { /* we're fucked a bit ... */ /* need to: delete new folder rename old back again */ g_warning("cannot append message"); return; } gtk_object_unref((GtkObject *)msg); } update_progress(progress, "Synchronising", 0.0); /* sync while we're doing i/o, just to make sure */ camel_folder_sync(tofolder, FALSE, ex); if (camel_exception_is_set(ex)) { /* same again */ } /* delete everything in the old mailbox */ printf("deleting old mbox contents\n"); for (i = 0; i < uids->len; i++) { char *uid = uids->pdata[i]; camel_folder_delete_message(fromfolder, uid); } camel_folder_sync(fromfolder, TRUE, ex); gtk_object_unref((GtkObject *)fromfolder); printf("and old mbox ...\n"); camel_store_delete_folder(fromstore, tmpname, ex); /* switch format */ g_free(meta->format); meta->format = g_strdup(newtype); if (save_metainfo(meta) == -1) { g_warning("Cannot save folder metainfo, you'll probably find you can't\n" "open this folder anymore: %s", tourl); } free_metainfo(meta); /* force a reload of the newly formatted folder */ printf("opening new source\n"); uri = g_strdup(fb->uri); folder_browser_set_uri(fb, uri); g_free(uri); /* and unref our copy of the new folder ... */ gtk_object_unref((GtkObject *)tofolder); g_free(fromurl); g_free(tourl); } struct _reconfig_data { FolderBrowser *fb; GtkProgress *progress; GtkWidget *frame; GtkWidget *apply; GtkWidget *cancel; GtkOptionMenu *optionlist; }; static void reconfigure_clicked(GnomeDialog *d, int button, struct _reconfig_data *data) { if (button == 0) { GtkMenu *menu; int type; char *types[] = { "mh", "mbox" }; CamelException *ex; ex = camel_exception_new(); menu = (GtkMenu *)gtk_option_menu_get_menu(data->optionlist); type = g_list_index(GTK_MENU_SHELL(menu)->children, gtk_menu_get_active(menu)); if (type < 0 || type > 1) type = 1; gtk_progress_set_percentage(data->progress, 0.0); gtk_widget_set_sensitive(data->frame, FALSE); gtk_widget_set_sensitive(data->apply, FALSE); gtk_widget_set_sensitive(data->cancel, FALSE); do_local_reconfigure_folder(data->fb, types[type], data->progress, ex); if (camel_exception_is_set(ex)) { GtkWidget *win = gtk_widget_get_ancestor((GtkWidget *)d, GTK_TYPE_WINDOW); char *error; error = g_strdup_printf("A failure occured:\n %s\n\n" "If you can no longer open this mailbox, then\n" "you may need to repair it manually.", camel_exception_get_description(ex)); gnome_error_dialog_parented(error, GTK_WINDOW (win)); g_free(error); } camel_exception_free(ex); } if (button != -1) { gnome_dialog_close(d); } } void local_reconfigure_folder(FolderBrowser *fb) { CamelStore *store; GladeXML *gui; GnomeDialog *gd; struct _reconfig_data *data; if (fb->folder == NULL) { g_warning("Trying to reconfigure nonexistant folder"); return; } data = g_malloc0(sizeof(*data)); store = camel_folder_get_parent_store(fb->folder); gui = glade_xml_new(EVOLUTION_GLADEDIR "/local-config.glade", "dialog_format"); gd = (GnomeDialog *)glade_xml_get_widget (gui, "dialog_format"); data->progress = (GtkProgress *)glade_xml_get_widget (gui, "progress_format"); gtk_progress_set_show_text(data->progress, TRUE); data->frame = glade_xml_get_widget (gui, "frame_format"); data->apply = glade_xml_get_widget (gui, "apply_format"); data->cancel = glade_xml_get_widget (gui, "cancel_format"); data->optionlist = (GtkOptionMenu *)glade_xml_get_widget (gui, "option_format"); data->fb = fb; gtk_label_set_text((GtkLabel *)glade_xml_get_widget (gui, "label_format"), ((CamelService *)store)->url->protocol); gtk_signal_connect((GtkObject *)gd, "clicked", reconfigure_clicked, data); gtk_object_set_data_full((GtkObject *)gd, "data", data, g_free); gtk_widget_show((GtkWidget *)gd); gtk_object_unref((GtkObject *)gui); }