/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with the program; if not, see * * * Authors: * Iain Holmes * Michael Zucchi * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include "e-util/e-util-private.h" #include "shell/e-shell-backend.h" #include "mail-mt.h" #include "mail-tools.h" #include "e-mail-local.h" #include "mail-session.h" #include "mail-importer.h" struct _import_mbox_msg { MailMsg base; gchar *path; gchar *uri; CamelOperation *cancel; void (*done)(gpointer data, GError **error); gpointer done_data; }; static gchar * import_mbox_desc (struct _import_mbox_msg *m) { return g_strdup (_("Importing mailbox")); } static struct { gchar tag; guint32 mozflag; guint32 flag; } status_flags[] = { { 'F', MSG_FLAG_MARKED, CAMEL_MESSAGE_FLAGGED }, { 'A', MSG_FLAG_REPLIED, CAMEL_MESSAGE_ANSWERED }, { 'D', MSG_FLAG_EXPUNGED, CAMEL_MESSAGE_DELETED }, { 'R', MSG_FLAG_READ, CAMEL_MESSAGE_SEEN }, }; static guint32 decode_status (const gchar *status) { const gchar *p; guint32 flags = 0; gint i; p = status; while ((*p++)) { for (i = 0; i < G_N_ELEMENTS (status_flags); i++) if (status_flags[i].tag == *p) flags |= status_flags[i].flag; } return flags; } static guint32 decode_mozilla_status (const gchar *tmp) { unsigned long status = strtoul (tmp, NULL, 16); guint32 flags = 0; gint i; for (i = 0; i < G_N_ELEMENTS (status_flags); i++) if (status_flags[i].mozflag & status) flags |= status_flags[i].flag; return flags; } static void import_mbox_exec (struct _import_mbox_msg *m) { CamelFolder *folder; CamelMimeParser *mp = NULL; struct stat st; gint fd; CamelMessageInfo *info; if (g_stat (m->path, &st) == -1) { g_warning("cannot find source file to import '%s': %s", m->path, g_strerror(errno)); return; } if (m->uri == NULL || m->uri[0] == 0) folder = e_mail_local_get_folder (E_MAIL_FOLDER_INBOX); else folder = mail_tool_uri_to_folder ( m->uri, CAMEL_STORE_FOLDER_CREATE, m->base.cancellable, &m->base.error); if (folder == NULL) return; if (S_ISREG (st.st_mode)) { fd = g_open (m->path, O_RDONLY|O_BINARY, 0); if (fd == -1) { g_warning("cannot find source file to import '%s': %s", m->path, g_strerror(errno)); goto fail1; } mp = camel_mime_parser_new (); camel_mime_parser_scan_from (mp, TRUE); if (camel_mime_parser_init_with_fd (mp, fd) == -1) { /* will never happen - 0 is unconditionally returned */ goto fail2; } camel_operation_push_message ( m->base.cancellable, _("Importing '%s'"), camel_folder_get_full_name (folder)); camel_folder_freeze (folder); while (camel_mime_parser_step (mp, NULL, NULL) == CAMEL_MIME_PARSER_STATE_FROM) { CamelMimeMessage *msg; const gchar *tmp; gint pc = 0; guint32 flags = 0; if (st.st_size > 0) pc = (gint)(100.0 * ((double)camel_mime_parser_tell (mp) / (double)st.st_size)); camel_operation_progress (m->base.cancellable, pc); msg = camel_mime_message_new (); if (!camel_mime_part_construct_from_parser_sync ( (CamelMimePart *)msg, mp, NULL, NULL)) { /* set exception? */ g_object_unref (msg); break; } info = camel_message_info_new (NULL); tmp = camel_medium_get_header((CamelMedium *)msg, "X-Mozilla-Status"); if (tmp) flags |= decode_mozilla_status (tmp); tmp = camel_medium_get_header((CamelMedium *)msg, "Status"); if (tmp) flags |= decode_status (tmp); tmp = camel_medium_get_header((CamelMedium *)msg, "X-Status"); if (tmp) flags |= decode_status (tmp); camel_message_info_set_flags (info, flags, ~0); camel_folder_append_message_sync ( folder, msg, info, NULL, m->base.cancellable, &m->base.error); camel_message_info_free (info); g_object_unref (msg); if (m->base.error != NULL) break; camel_mime_parser_step (mp, NULL, NULL); } /* FIXME Not passing a GCancellable or GError here. */ camel_folder_synchronize_sync (folder, FALSE, NULL, NULL); camel_folder_thaw (folder); camel_operation_pop_message (m->base.cancellable); /* TODO: these api's are a bit weird, registering the old is the same as deregistering */ fail2: g_object_unref (mp); } fail1: /* FIXME Not passing a GCancellable or GError here. */ camel_folder_synchronize_sync (folder, FALSE, NULL, NULL); g_object_unref (folder); } static void import_mbox_done (struct _import_mbox_msg *m) { if (m->done) m->done (m->done_data, &m->base.error); } static void import_mbox_free (struct _import_mbox_msg *m) { if (m->cancel) g_object_unref (m->cancel); g_free (m->uri); g_free (m->path); } static MailMsgInfo import_mbox_info = { sizeof (struct _import_mbox_msg), (MailMsgDescFunc) import_mbox_desc, (MailMsgExecFunc) import_mbox_exec, (MailMsgDoneFunc) import_mbox_done, (MailMsgFreeFunc) import_mbox_free }; gint mail_importer_import_mbox (const gchar *path, const gchar *folderuri, CamelOperation *cancel, void (*done)(gpointer data, GError **), gpointer data) { struct _import_mbox_msg *m; gint id; m = mail_msg_new (&import_mbox_info); m->path = g_strdup (path); m->uri = g_strdup (folderuri); m->done = done; m->done_data = data; if (cancel) m->cancel = g_object_ref (cancel); id = m->base.seq; mail_msg_fast_ordered_push (m); return id; } void mail_importer_import_mbox_sync (const gchar *path, const gchar *folderuri, CamelOperation *cancel) { struct _import_mbox_msg *m; m = mail_msg_new (&import_mbox_info); m->path = g_strdup (path); m->uri = g_strdup (folderuri); if (cancel) m->cancel = g_object_ref (cancel); import_mbox_exec (m); import_mbox_done (m); mail_msg_unref (m); } struct _import_folders_data { MailImporterSpecial *special_folders; CamelOperation *cancel; guint elmfmt:1; }; static void import_folders_rec (struct _import_folders_data *m, const gchar *filepath, const gchar *folderparent) { GDir *dir; const gchar *d; struct stat st; const gchar *data_dir; gchar *filefull, *foldersub, *uri, *utf8_filename; const gchar *folder; dir = g_dir_open (filepath, 0, NULL); if (dir == NULL) return; data_dir = mail_session_get_data_dir (); utf8_filename = g_filename_to_utf8 (filepath, -1, NULL, NULL, NULL); camel_operation_push_message (NULL, _("Scanning %s"), utf8_filename); g_free (utf8_filename); while ( (d=g_dir_read_name (dir))) { if (d[0] == '.') continue; filefull = g_build_filename (filepath, d, NULL); /* skip non files and directories, and skip directories in mozilla mode */ if (g_stat (filefull, &st) == -1 || !(S_ISREG (st.st_mode) || (m->elmfmt && S_ISDIR (st.st_mode)))) { g_free (filefull); continue; } folder = d; if (folderparent == NULL) { gint i; for (i=0;m->special_folders[i].orig;i++) if (strcmp (m->special_folders[i].orig, folder) == 0) { folder = m->special_folders[i].new; break; } /* FIXME: need a better way to get default store location */ uri = g_strdup_printf("mbox:%s/local#%s", data_dir, folder); } else { uri = g_strdup_printf("mbox:%s/local#%s/%s", data_dir, folderparent, folder); } printf("importing to uri %s\n", uri); mail_importer_import_mbox_sync (filefull, uri, m->cancel); g_free (uri); /* This little gem re-uses the stat buffer and filefull to automagically scan mozilla-format folders */ if (!m->elmfmt) { gchar *tmp = g_strdup_printf("%s.sbd", filefull); g_free (filefull); filefull = tmp; if (g_stat (filefull, &st) == -1) { g_free (filefull); continue; } } if (S_ISDIR (st.st_mode)) { foldersub = folderparent?g_strdup_printf("%s/%s", folderparent, folder):g_strdup(folder); import_folders_rec (m, filefull, foldersub); g_free (foldersub); } g_free (filefull); } g_dir_close (dir); camel_operation_pop_message (NULL); } /** * mail_importer_import_folders_sync: * @filepath: * @: * @flags: * @cancel: * * import from a base path @filepath into the root local folder tree, * scanning all sub-folders. * * if @flags is MAIL_IMPORTER_MOZFMT, then subfolders are assumed to * be in mozilla/evolutoin 1.5 format, appending '.sbd' to the * directory name. Otherwise they are in elm/mutt/pine format, using * standard unix directories. **/ void mail_importer_import_folders_sync (const gchar *filepath, MailImporterSpecial special_folders[], gint flags, CamelOperation *cancel) { struct _import_folders_data m; m.special_folders = special_folders; m.elmfmt = (flags & MAIL_IMPORTER_MOZFMT) == 0; m.cancel = cancel; import_folders_rec (&m, filepath, NULL); }