No issues found
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Iain Holmes <iain@ximian.com>
18 * Michael Zucchi <notzed@ximian.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <errno.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35
36 #include <glib/gstdio.h>
37 #include <glib/gi18n.h>
38
39 #include "e-util/e-util-private.h"
40 #include "shell/e-shell-backend.h"
41
42 #include "libemail-utils/mail-mt.h"
43 #include "libemail-engine/mail-tools.h"
44 #include "libemail-engine/e-mail-session.h"
45
46 #include "mail-importer.h"
47
48 struct _import_mbox_msg {
49 MailMsg base;
50
51 EMailSession *session;
52 gchar *path;
53 gchar *uri;
54 GCancellable *cancellable;
55
56 void (*done)(gpointer data, GError **error);
57 gpointer done_data;
58 };
59
60 static gchar *
61 import_mbox_desc (struct _import_mbox_msg *m)
62 {
63 return g_strdup (_("Importing mailbox"));
64 }
65
66 static struct {
67 gchar tag;
68 guint32 mozflag;
69 guint32 flag;
70 } status_flags[] = {
71 { 'F', MSG_FLAG_MARKED, CAMEL_MESSAGE_FLAGGED },
72 { 'A', MSG_FLAG_REPLIED, CAMEL_MESSAGE_ANSWERED },
73 { 'D', MSG_FLAG_EXPUNGED, CAMEL_MESSAGE_DELETED },
74 { 'R', MSG_FLAG_READ, CAMEL_MESSAGE_SEEN },
75 };
76
77 static guint32
78 decode_status (const gchar *status)
79 {
80 const gchar *p;
81 guint32 flags = 0;
82 gint i;
83
84 p = status;
85 while ((*p++)) {
86 for (i = 0; i < G_N_ELEMENTS (status_flags); i++)
87 if (status_flags[i].tag == *p)
88 flags |= status_flags[i].flag;
89 }
90
91 return flags;
92 }
93
94 static guint32
95 decode_mozilla_status (const gchar *tmp)
96 {
97 gulong status = strtoul (tmp, NULL, 16);
98 guint32 flags = 0;
99 gint i;
100
101 for (i = 0; i < G_N_ELEMENTS (status_flags); i++)
102 if (status_flags[i].mozflag & status)
103 flags |= status_flags[i].flag;
104 return flags;
105 }
106
107 static void
108 import_mbox_exec (struct _import_mbox_msg *m,
109 GCancellable *cancellable,
110 GError **error)
111 {
112 CamelFolder *folder;
113 CamelMimeParser *mp = NULL;
114 struct stat st;
115 gint fd;
116 CamelMessageInfo *info;
117
118 if (g_stat (m->path, &st) == -1) {
119 g_warning (
120 "cannot find source file to import '%s': %s",
121 m->path, g_strerror (errno));
122 return;
123 }
124
125 if (m->uri == NULL || m->uri[0] == 0)
126 folder = e_mail_session_get_local_folder (
127 m->session, E_MAIL_LOCAL_FOLDER_INBOX);
128 else
129 folder = e_mail_session_uri_to_folder_sync (
130 m->session, m->uri, CAMEL_STORE_FOLDER_CREATE,
131 cancellable, error);
132
133 if (folder == NULL)
134 return;
135
136 if (S_ISREG (st.st_mode)) {
137 fd = g_open (m->path, O_RDONLY | O_BINARY, 0);
138 if (fd == -1) {
139 g_warning (
140 "cannot find source file to import '%s': %s",
141 m->path, g_strerror (errno));
142 goto fail1;
143 }
144
145 mp = camel_mime_parser_new ();
146 camel_mime_parser_scan_from (mp, TRUE);
147 if (camel_mime_parser_init_with_fd (mp, fd) == -1) {
148 /* will never happen - 0 is unconditionally returned */
149 goto fail2;
150 }
151
152 camel_operation_push_message (
153 m->cancellable, _("Importing '%s'"),
154 camel_folder_get_display_name (folder));
155 camel_folder_freeze (folder);
156 while (camel_mime_parser_step (mp, NULL, NULL) ==
157 CAMEL_MIME_PARSER_STATE_FROM) {
158
159 CamelMimeMessage *msg;
160 const gchar *tmp;
161 gint pc = 0;
162 guint32 flags = 0;
163
164 if (st.st_size > 0)
165 pc = (gint) (100.0 * ((gdouble)
166 camel_mime_parser_tell (mp) /
167 (gdouble) st.st_size));
168 camel_operation_progress (m->cancellable, pc);
169
170 msg = camel_mime_message_new ();
171 if (!camel_mime_part_construct_from_parser_sync (
172 (CamelMimePart *) msg, mp, NULL, NULL)) {
173 /* set exception? */
174 g_object_unref (msg);
175 break;
176 }
177
178 info = camel_message_info_new (NULL);
179
180 tmp = camel_medium_get_header ((CamelMedium *) msg, "X-Mozilla-Status");
181 if (tmp)
182 flags |= decode_mozilla_status (tmp);
183 tmp = camel_medium_get_header ((CamelMedium *) msg, "Status");
184 if (tmp)
185 flags |= decode_status (tmp);
186 tmp = camel_medium_get_header ((CamelMedium *) msg, "X-Status");
187 if (tmp)
188 flags |= decode_status (tmp);
189
190 camel_message_info_set_flags (info, flags, ~0);
191 camel_folder_append_message_sync (
192 folder, msg, info, NULL,
193 cancellable, error);
194 camel_message_info_free (info);
195 g_object_unref (msg);
196
197 if (error && *error != NULL)
198 break;
199
200 camel_mime_parser_step (mp, NULL, NULL);
201 }
202 /* FIXME Not passing a GCancellable or GError here. */
203 camel_folder_synchronize_sync (folder, FALSE, NULL, NULL);
204 camel_folder_thaw (folder);
205 camel_operation_pop_message (m->cancellable);
206 fail2:
207 g_object_unref (mp);
208 }
209 fail1:
210 /* FIXME Not passing a GCancellable or GError here. */
211 camel_folder_synchronize_sync (folder, FALSE, NULL, NULL);
212 g_object_unref (folder);
213 }
214
215 static void
216 import_mbox_done (struct _import_mbox_msg *m)
217 {
218 if (m->done)
219 m->done (m->done_data, &m->base.error);
220 }
221
222 static void
223 import_mbox_free (struct _import_mbox_msg *m)
224 {
225 g_object_unref (m->session);
226 if (m->cancellable)
227 g_object_unref (m->cancellable);
228 g_free (m->uri);
229 g_free (m->path);
230 }
231
232 static MailMsgInfo import_mbox_info = {
233 sizeof (struct _import_mbox_msg),
234 (MailMsgDescFunc) import_mbox_desc,
235 (MailMsgExecFunc) import_mbox_exec,
236 (MailMsgDoneFunc) import_mbox_done,
237 (MailMsgFreeFunc) import_mbox_free
238 };
239
240 gint
241 mail_importer_import_mbox (EMailSession *session,
242 const gchar *path,
243 const gchar *folderuri,
244 GCancellable *cancellable,
245 void (*done) (gpointer data,
246 GError **error),
247 gpointer data)
248 {
249 struct _import_mbox_msg *m;
250 gint id;
251
252 m = mail_msg_new (&import_mbox_info);
253 m->session = g_object_ref (session);
254 m->path = g_strdup (path);
255 m->uri = g_strdup (folderuri);
256 m->done = done;
257 m->done_data = data;
258 if (cancellable)
259 m->cancellable = g_object_ref (cancellable);
260
261 id = m->base.seq;
262 mail_msg_fast_ordered_push (m);
263
264 return id;
265 }
266
267 void
268 mail_importer_import_mbox_sync (EMailSession *session,
269 const gchar *path,
270 const gchar *folderuri,
271 GCancellable *cancellable)
272 {
273 struct _import_mbox_msg *m;
274
275 m = mail_msg_new (&import_mbox_info);
276 m->session = g_object_ref (session);
277 m->path = g_strdup (path);
278 m->uri = g_strdup (folderuri);
279 if (cancellable)
280 m->base.cancellable = cancellable;
281
282 cancellable = m->base.cancellable;
283
284 import_mbox_exec (m, cancellable, &m->base.error);
285 import_mbox_done (m);
286 mail_msg_unref (m);
287 }
288
289 struct _import_folders_data {
290 MailImporterSpecial *special_folders;
291 EMailSession *session;
292 GCancellable *cancellable;
293
294 guint elmfmt : 1;
295 };
296
297 static void
298 import_folders_rec (struct _import_folders_data *m,
299 const gchar *filepath,
300 const gchar *folderparent)
301 {
302 GDir *dir;
303 const gchar *d;
304 struct stat st;
305 const gchar *data_dir;
306 gchar *filefull, *foldersub, *uri, *utf8_filename;
307 const gchar *folder;
308
309 dir = g_dir_open (filepath, 0, NULL);
310 if (dir == NULL)
311 return;
312
313 data_dir = mail_session_get_data_dir ();
314
315 utf8_filename = g_filename_to_utf8 (filepath, -1, NULL, NULL, NULL);
316 camel_operation_push_message (m->cancellable, _("Scanning %s"), utf8_filename);
317 g_free (utf8_filename);
318
319 while ((d = g_dir_read_name (dir))) {
320 if (d[0] == '.')
321 continue;
322
323 filefull = g_build_filename (filepath, d, NULL);
324
325 /* skip non files and directories, and skip directories in mozilla mode */
326 if (g_stat (filefull, &st) == -1
327 || !(S_ISREG (st.st_mode)
328 || (m->elmfmt && S_ISDIR (st.st_mode)))) {
329 g_free (filefull);
330 continue;
331 }
332
333 folder = d;
334 if (folderparent == NULL) {
335 gint i;
336
337 for (i = 0; m->special_folders[i].orig; i++)
338 if (strcmp (m->special_folders[i].orig, folder) == 0) {
339 folder = m->special_folders[i].new;
340 break;
341 }
342 /* FIXME: need a better way to get default store location */
343 uri = g_strdup_printf (
344 "mbox:%s/local#%s", data_dir, folder);
345 } else {
346 uri = g_strdup_printf (
347 "mbox:%s/local#%s/%s",
348 data_dir, folderparent, folder);
349 }
350
351 printf ("importing to uri %s\n", uri);
352 mail_importer_import_mbox_sync (
353 m->session, filefull, uri, m->cancellable);
354 g_free (uri);
355
356 /* This little gem re-uses the stat buffer and filefull
357 * to automagically scan mozilla-format folders. */
358 if (!m->elmfmt) {
359 gchar *tmp = g_strdup_printf ("%s.sbd", filefull);
360
361 g_free (filefull);
362 filefull = tmp;
363 if (g_stat (filefull, &st) == -1) {
364 g_free (filefull);
365 continue;
366 }
367 }
368
369 if (S_ISDIR (st.st_mode)) {
370 foldersub = folderparent ?
371 g_strdup_printf (
372 "%s/%s", folderparent, folder) :
373 g_strdup (folder);
374 import_folders_rec (m, filefull, foldersub);
375 g_free (foldersub);
376 }
377
378 g_free (filefull);
379 }
380 g_dir_close (dir);
381
382 camel_operation_pop_message (m->cancellable);
383 }
384
385 /**
386 * mail_importer_import_folders_sync:
387 * @filepath:
388 * @:
389 * @flags:
390 * @cancel:
391 *
392 * import from a base path @filepath into the root local folder tree,
393 * scanning all sub-folders.
394 *
395 * if @flags is MAIL_IMPORTER_MOZFMT, then subfolders are assumed to
396 * be in mozilla/evolutoin 1.5 format, appending '.sbd' to the
397 * directory name. Otherwise they are in elm/mutt/pine format, using
398 * standard unix directories.
399 **/
400 void
401 mail_importer_import_folders_sync (EMailSession *session,
402 const gchar *filepath,
403 MailImporterSpecial special_folders[],
404 gint flags,
405 GCancellable *cancellable)
406 {
407 struct _import_folders_data m;
408
409 m.special_folders = special_folders;
410 m.elmfmt = (flags & MAIL_IMPORTER_MOZFMT) == 0;
411 m.session = g_object_ref (session);
412 m.cancellable = cancellable;
413
414 import_folders_rec (&m, filepath, NULL);
415 }