Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
mail-ops.c:266:17 | clang-analyzer | Assigned value is garbage or undefined | ||
mail-ops.c:266:17 | clang-analyzer | Assigned value is garbage or undefined |
1 /*
2 * mail-ops.c: callbacks for the mail toolbar/menus
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 *
18 * Authors:
19 * Dan Winship <danw@ximian.com>
20 * Jeffrey Stedfast <fejj@ximian.com>
21 * Peter Williams <peterw@ximian.com>
22 * Michael Zucchi <notzed@ximian.com>
23 *
24 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
25 *
26 */
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31
32 #include <errno.h>
33
34 #include <glib/gstdio.h>
35 #include <glib/gi18n.h>
36
37 #include <libedataserver/libedataserver.h>
38
39 #include <libemail-utils/mail-mt.h>
40
41 #include "e-mail-utils.h"
42 #include "mail-ops.h"
43 #include "mail-tools.h"
44
45 #include "e-mail-folder-utils.h"
46 #include "e-mail-session.h"
47 #include "e-mail-session-utils.h"
48
49 #define w(x)
50 #define d(x)
51
52 /* XXX Make this a preprocessor definition. */
53 const gchar *x_mailer = "Evolution " VERSION SUB_VERSION " " VERSION_COMMENT;
54
55 /* used for both just filtering a folder + uid's, and for filtering a whole folder */
56 /* used both for fetching mail, and for filtering mail */
57 struct _filter_mail_msg {
58 MailMsg base;
59
60 EMailSession *session;
61 CamelFolder *source_folder; /* where they come from */
62 GPtrArray *source_uids; /* uids to copy, or NULL == copy all */
63 CamelUIDCache *cache; /* UID cache if we are to cache
64 * the uids, NULL otherwise */
65 CamelFilterDriver *driver;
66 gint delete; /* delete messages after filtering? */
67 CamelFolder *destination; /* default destination for any
68 * messages, NULL for none */
69 };
70
71 /* since fetching also filters, we subclass the data here */
72 struct _fetch_mail_msg {
73 struct _filter_mail_msg fmsg;
74
75 CamelStore *store;
76 GCancellable *cancellable; /* we have our own cancellation
77 * struct, the other should be empty */
78 gint keep; /* keep on server? */
79 gint fetch_count;
80 CamelFetchType fetch_type;
81 gint still_more;
82
83 MailProviderFetchLockFunc provider_lock;
84 MailProviderFetchUnlockFunc provider_unlock;
85 MailProviderFetchInboxFunc provider_fetch_inbox;
86
87 void (*done)(gint still_more, gpointer data);
88 gpointer data;
89 };
90
91 static gchar *
92 em_filter_folder_element_desc (struct _filter_mail_msg *m)
93 {
94 return g_strdup (_("Filtering Selected Messages"));
95 }
96
97 /* filter a folder, or a subset thereof, uses source_folder/source_uids */
98 /* this is shared with fetch_mail */
99 static gboolean
100 em_filter_folder_element_exec (struct _filter_mail_msg *m,
101 GCancellable *cancellable,
102 GError **error)
103 {
104 CamelFolder *folder;
105 GPtrArray *uids, *folder_uids = NULL;
106 gboolean success = TRUE;
107
108 folder = m->source_folder;
109
110 if (folder == NULL || camel_folder_get_message_count (folder) == 0)
111 return success;
112
113 if (m->destination) {
114 camel_folder_freeze (m->destination);
115 camel_filter_driver_set_default_folder (m->driver, m->destination);
116 }
117
118 camel_folder_freeze (folder);
119
120 if (m->source_uids)
121 uids = m->source_uids;
122 else
123 folder_uids = uids = camel_folder_get_uids (folder);
124
125 success = camel_filter_driver_filter_folder (
126 m->driver, folder, m->cache, uids, m->delete,
127 cancellable, error) == 0;
128 camel_filter_driver_flush (m->driver, error);
129
130 if (folder_uids)
131 camel_folder_free_uids (folder, folder_uids);
132
133 /* sync our source folder */
134 if (!m->cache)
135 camel_folder_synchronize_sync (
136 folder, FALSE, cancellable, error);
137 camel_folder_thaw (folder);
138
139 if (m->destination)
140 camel_folder_thaw (m->destination);
141
142 /* this may thaw/unref source folders, do it here so we dont do
143 * it in the main thread see also fetch_mail_fetch () below */
144 g_object_unref (m->driver);
145 m->driver = NULL;
146
147 return success;
148 }
149
150 static void
151 em_filter_folder_element_done (struct _filter_mail_msg *m)
152 {
153 }
154
155 static void
156 em_filter_folder_element_free (struct _filter_mail_msg *m)
157 {
158 mail_session_flush_filter_log (m->session);
159
160 if (m->session)
161 g_object_unref (m->session);
162
163 if (m->source_folder)
164 g_object_unref (m->source_folder);
165
166 if (m->source_uids)
167 em_utils_uids_free (m->source_uids);
168
169 if (m->destination)
170 g_object_unref (m->destination);
171
172 if (m->driver)
173 g_object_unref (m->driver);
174 }
175
176 static MailMsgInfo em_filter_folder_element_info = {
177 sizeof (struct _filter_mail_msg),
178 (MailMsgDescFunc) em_filter_folder_element_desc,
179 (MailMsgExecFunc) em_filter_folder_element_exec,
180 (MailMsgDoneFunc) em_filter_folder_element_done,
181 (MailMsgFreeFunc) em_filter_folder_element_free
182 };
183
184 void
185 mail_filter_folder (EMailSession *session,
186 CamelFolder *source_folder,
187 GPtrArray *uids,
188 const gchar *type,
189 gboolean notify)
190 {
191 struct _filter_mail_msg *m;
192
193 m = mail_msg_new (&em_filter_folder_element_info);
194 m->session = g_object_ref (session);
195 m->source_folder = g_object_ref (source_folder);
196 m->source_uids = uids;
197 m->cache = NULL;
198 m->delete = FALSE;
199
200 m->driver = camel_session_get_filter_driver (
201 CAMEL_SESSION (session), type, NULL);
202
203 if (!notify) {
204 /* FIXME: have a #define NOTIFY_FILTER_NAME macro? */
205 /* the filter name has to stay in sync
206 * with mail-session::get_filter_driver */
207 camel_filter_driver_remove_rule_by_name (
208 m->driver, "new-mail-notification");
209 }
210
211 mail_msg_unordered_push (m);
212 }
213
214 /* ********************************************************************** */
215
216 static gchar *
217 fetch_mail_desc (struct _fetch_mail_msg *m)
218 {
219 return g_strdup (_("Fetching Mail"));
220 }
221
222 static void
223 fetch_mail_exec (struct _fetch_mail_msg *m,
224 GCancellable *cancellable,
225 GError **error)
226 {
227 struct _filter_mail_msg *fm = (struct _filter_mail_msg *) m;
228 GObjectClass *class;
229 CamelFolder *folder = NULL;
230 CamelService *service;
231 CamelSession *session;
232 CamelSettings *settings;
233 CamelStore *parent_store;
234 CamelUIDCache *cache = NULL;
235 CamelURL *url;
236 gboolean keep;
237 gboolean delete_fetched;
238 gboolean is_local_delivery = FALSE;
239 const gchar *uid = NULL;
240 const gchar *data_dir;
241 gchar *cachename;
242 gint i;
243
244 service = CAMEL_SERVICE (m->store);
245 session = camel_service_get_session (service);
246
247 fm->destination = e_mail_session_get_local_folder (
248 E_MAIL_SESSION (session), E_MAIL_LOCAL_FOLDER_LOCAL_INBOX);
249 if (fm->destination == NULL)
250 goto exit;
251 g_object_ref (fm->destination);
252
253 service = CAMEL_SERVICE (m->store);
254 uid = camel_service_get_uid (service);
255
256 settings = camel_service_ref_settings (service);
257
258 /* XXX This is a POP3-specific setting. */
259 class = G_OBJECT_GET_CLASS (settings);
260 if (g_object_class_find_property (class, "keep-on-server") != NULL)
261 g_object_get (settings, "keep-on-server", &keep, NULL);
262
263 g_object_unref (settings);
264
265 /* Just for readability. */
266 delete_fetched = !keep;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
267
268 url = camel_service_new_camel_url (service);
269 is_local_delivery = em_utils_is_local_delivery_mbox_file (url);
270
271 if (is_local_delivery) {
272 gchar *path;
273 gchar *url_string;
274
275 path = mail_tool_do_movemail (m->store, error);
276 url_string = camel_url_to_string (url, CAMEL_URL_HIDE_ALL);
277
278 if (path && (!error || !*error)) {
279 camel_folder_freeze (fm->destination);
280 camel_filter_driver_set_default_folder (
281 fm->driver, fm->destination);
282 camel_filter_driver_filter_mbox (
283 fm->driver, path, url_string,
284 cancellable, error);
285 camel_folder_thaw (fm->destination);
286
287 if (!error || !*error)
288 g_unlink (path);
289 }
290
291 g_free (path);
292 g_free (url_string);
293 } else {
294 uid = camel_service_get_uid (service);
295 if (m->provider_lock)
296 m->provider_lock (uid);
297
298 folder = fm->source_folder =
299 e_mail_session_get_inbox_sync (
300 fm->session, uid, cancellable, error);
301 }
302
303 camel_url_free (url);
304
305 if (folder == NULL)
306 goto exit;
307
308 parent_store = camel_folder_get_parent_store (folder);
309
310 if (m->fetch_count > 0) {
311 /* We probably should fetch some old messages first. */
312 m->still_more = camel_folder_fetch_messages_sync (
313 folder, m->fetch_type,
314 m->fetch_count, cancellable, error) ? 1 : 0;
315 }
316
317 service = CAMEL_SERVICE (parent_store);
318 data_dir = camel_service_get_user_data_dir (service);
319
320 cachename = g_build_filename (data_dir, "uid-cache", NULL);
321 cache = camel_uid_cache_new (cachename);
322 g_free (cachename);
323
324 if (cache) {
325 GPtrArray *folder_uids, *cache_uids, *uids;
326 GError *local_error = NULL;
327
328 if (m->provider_fetch_inbox) {
329 g_object_unref (fm->destination);
330 fm->destination = m->provider_fetch_inbox (uid, cancellable, &local_error);
331 if (fm->destination == NULL)
332 goto exit;
333 g_object_ref (fm->destination);
334 }
335
336 if (!local_error) {
337 folder_uids = camel_folder_get_uids (folder);
338 cache_uids = camel_uid_cache_get_new_uids (cache, folder_uids);
339
340 if (cache_uids) {
341 gboolean success;
342
343 /* need to copy this, sigh */
344 fm->source_uids = uids = g_ptr_array_new ();
345 g_ptr_array_set_size (uids, cache_uids->len);
346
347 /* Reverse it so that we fetch the latest as first, while fetching POP */
348 for (i = 0; i < cache_uids->len; i++) {
349 uids->pdata[cache_uids->len - i - 1] = g_strdup (cache_uids->pdata[i]);
350 }
351
352 fm->cache = cache;
353
354 success = em_filter_folder_element_exec (fm, cancellable, &local_error);
355
356 /* need to uncancel so writes/etc. don't fail */
357 if (g_cancellable_is_cancelled (m->cancellable))
358 g_cancellable_reset (m->cancellable);
359
360 if (!success) {
361 /* re-enter known UIDs, thus they are not
362 * re-fetched next time */
363 for (i = 0; i < cache_uids->len; i++) {
364 camel_uid_cache_save_uid (cache, cache_uids->pdata[i]);
365 }
366 }
367
368 /* save the cache of uids that we've just downloaded */
369 camel_uid_cache_save (cache);
370
371 camel_uid_cache_free_uids (cache_uids);
372 }
373
374 if (delete_fetched && !local_error) {
375 /* not keep on server - just delete all
376 * the actual messages on the server */
377 for (i = 0; i < folder_uids->len; i++) {
378 camel_folder_delete_message (
379 folder, folder_uids->pdata[i]);
380 }
381 }
382
383 if ((delete_fetched || cache_uids) && !local_error) {
384 /* expunge messages (downloaded so far) */
385 /* FIXME Not passing a GCancellable or GError here. */
386 camel_folder_synchronize_sync (
387 folder, delete_fetched, NULL, NULL);
388 }
389
390 camel_folder_free_uids (folder, folder_uids);
391 }
392
393 camel_uid_cache_destroy (cache);
394
395 if (local_error)
396 g_propagate_error (error, local_error);
397 } else {
398 em_filter_folder_element_exec (fm, cancellable, error);
399 }
400
401 /* we unref the source folder here since we
402 * may now block in finalize (we try to
403 * disconnect cleanly) */
404 g_object_unref (fm->source_folder);
405 fm->source_folder = NULL;
406
407 exit:
408 if (!is_local_delivery && m->provider_unlock)
409 m->provider_unlock (uid);
410
411 /* we unref this here as it may have more work to do (syncing
412 * folders and whatnot) before we are really done */
413 /* should this be cancellable too? (i.e. above unregister above) */
414 if (fm->driver) {
415 g_object_unref (fm->driver);
416 fm->driver = NULL;
417 }
418
419 /* also disconnect if not a local delivery mbox;
420 * there is no need to keep the connection alive forever */
421 if (!is_local_delivery)
422 camel_service_disconnect_sync (
423 service, TRUE, cancellable, NULL);
424 }
425
426 static void
427 fetch_mail_done (struct _fetch_mail_msg *m)
428 {
429 if (m->done)
430 m->done (m->still_more, m->data);
431 }
432
433 static void
434 fetch_mail_free (struct _fetch_mail_msg *m)
435 {
436 if (m->store != NULL)
437 g_object_unref (m->store);
438
439 if (m->cancellable != NULL)
440 g_object_unref (m->cancellable);
441
442 em_filter_folder_element_free ((struct _filter_mail_msg *) m);
443 }
444
445 static MailMsgInfo fetch_mail_info = {
446 sizeof (struct _fetch_mail_msg),
447 (MailMsgDescFunc) fetch_mail_desc,
448 (MailMsgExecFunc) fetch_mail_exec,
449 (MailMsgDoneFunc) fetch_mail_done,
450 (MailMsgFreeFunc) fetch_mail_free
451 };
452
453 /* ouch, a 'do everything' interface ... */
454 void
455 mail_fetch_mail (CamelStore *store,
456 CamelFetchType fetch_type,
457 gint fetch_count,
458 const gchar *type,
459 MailProviderFetchLockFunc lock_func,
460 MailProviderFetchUnlockFunc unlock_func,
461 MailProviderFetchInboxFunc fetch_inbox_func,
462 GCancellable *cancellable,
463 CamelFilterGetFolderFunc get_folder,
464 gpointer get_data,
465 CamelFilterStatusFunc *status,
466 gpointer status_data,
467 void (*done)(gint still_more,
468 gpointer data),
469 gpointer data)
470 {
471 struct _fetch_mail_msg *m;
472 struct _filter_mail_msg *fm;
473 CamelSession *session;
474
475 g_return_if_fail (CAMEL_IS_STORE (store));
476
477 session = camel_service_get_session (CAMEL_SERVICE (store));
478
479 m = mail_msg_new (&fetch_mail_info);
480 fm = (struct _filter_mail_msg *) m;
481 fm->session = g_object_ref (session);
482 m->store = g_object_ref (store);
483 fm->cache = NULL;
484 if (cancellable)
485 m->cancellable = g_object_ref (cancellable);
486 m->done = done;
487 m->data = data;
488
489 m->fetch_count = fetch_count;
490 m->fetch_type = fetch_type;
491 m->still_more = -1;
492
493 m->provider_lock = lock_func;
494 m->provider_unlock = unlock_func;
495 m->provider_fetch_inbox = fetch_inbox_func;
496
497 fm->driver = camel_session_get_filter_driver (session, type, NULL);
498 camel_filter_driver_set_folder_func (fm->driver, get_folder, get_data);
499 if (status)
500 camel_filter_driver_set_status_func (fm->driver, status, status_data);
501
502 mail_msg_unordered_push (m);
503 }
504
505 /* ********************************************************************** */
506 /* sending stuff */
507 /* ** SEND MAIL *********************************************************** */
508
509 static const gchar *normal_recipients[] = {
510 CAMEL_RECIPIENT_TYPE_TO,
511 CAMEL_RECIPIENT_TYPE_CC,
512 CAMEL_RECIPIENT_TYPE_BCC
513 };
514
515 static const gchar *resent_recipients[] = {
516 CAMEL_RECIPIENT_TYPE_RESENT_TO,
517 CAMEL_RECIPIENT_TYPE_RESENT_CC,
518 CAMEL_RECIPIENT_TYPE_RESENT_BCC
519 };
520
521 struct _send_queue_msg {
522 MailMsg base;
523
524 EMailSession *session;
525 CamelFolder *queue;
526 CamelTransport *transport;
527
528 CamelFilterDriver *driver;
529
530 /* we use camelfilterstatusfunc, even though its not the filter doing it */
531 CamelFilterStatusFunc *status;
532 gpointer status_data;
533
534 void (*done)(gpointer data);
535 gpointer data;
536 };
537
538 static void report_status (struct _send_queue_msg *m,
539 enum camel_filter_status_t status,
540 gint pc,
541 const gchar *desc,
542 ...);
543
544 static gboolean
545 get_submission_details_from_identity (EMailSession *session,
546 const gchar *identity_uid,
547 gchar **out_transport_uid,
548 gchar **out_sent_folder_uri)
549 {
550 ESource *source;
551 ESourceRegistry *registry;
552 ESourceExtension *extension;
553 const gchar *extension_name;
554
555 registry = e_mail_session_get_registry (session);
556 extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
557 source = e_source_registry_ref_source (registry, identity_uid);
558
559 if (source == NULL)
560 return FALSE;
561
562 if (!e_source_has_extension (source, extension_name)) {
563 g_object_unref (source);
564 return FALSE;
565 }
566
567 extension = e_source_get_extension (source, extension_name);
568
569 *out_sent_folder_uri =
570 e_source_mail_submission_dup_sent_folder (
571 E_SOURCE_MAIL_SUBMISSION (extension));
572
573 *out_transport_uid =
574 e_source_mail_submission_dup_transport_uid (
575 E_SOURCE_MAIL_SUBMISSION (extension));
576
577 g_object_unref (source);
578
579 return TRUE;
580 }
581
582 /* send 1 message to a specific transport */
583 static void
584 mail_send_message (struct _send_queue_msg *m,
585 CamelFolder *queue,
586 const gchar *uid,
587 CamelTransport *transport,
588 CamelFilterDriver *driver,
589 GCancellable *cancellable,
590 GError **error)
591 {
592 CamelService *service;
593 const CamelInternetAddress *iaddr;
594 CamelAddress *from, *recipients;
595 CamelMessageInfo *info = NULL;
596 CamelProvider *provider = NULL;
597 gchar *transport_uid = NULL;
598 gchar *sent_folder_uri = NULL;
599 const gchar *resent_from, *tmp;
600 CamelFolder *folder = NULL;
601 GString *err = NULL;
602 struct _camel_header_raw *xev, *header;
603 CamelMimeMessage *message;
604 gint i;
605 GError *local_error = NULL;
606
607 message = camel_folder_get_message_sync (
608 queue, uid, cancellable, error);
609 if (!message)
610 return;
611
612 camel_medium_set_header (CAMEL_MEDIUM (message), "X-Mailer", x_mailer);
613
614 err = g_string_new ("");
615 xev = mail_tool_remove_xevolution_headers (message);
616
617 tmp = camel_header_raw_find (&xev, "X-Evolution-Identity", NULL);
618 if (tmp != NULL) {
619 gchar *identity_uid;
620
621 identity_uid = g_strstrip (g_strdup (tmp));
622 get_submission_details_from_identity (
623 m->session, identity_uid,
624 &transport_uid, &sent_folder_uri);
625 g_free (identity_uid);
626 }
627
628 tmp = camel_header_raw_find (&xev, "X-Evolution-Transport", NULL);
629 if (transport_uid == NULL && tmp != NULL)
630 transport_uid = g_strstrip (g_strdup (tmp));
631
632 tmp = camel_header_raw_find (&xev, "X-Evolution-Fcc", NULL);
633 if (sent_folder_uri == NULL && tmp != NULL)
634 sent_folder_uri = g_strstrip (g_strdup (tmp));
635
636 service = camel_session_ref_service (
637 CAMEL_SESSION (m->session), transport_uid);
638 if (service != NULL)
639 provider = camel_service_get_provider (service);
640
641 if (CAMEL_IS_TRANSPORT (service)) {
642 const gchar *tuid;
643
644 /* Let the dialog know the right account it is using. */
645 tuid = camel_service_get_uid (CAMEL_SERVICE (transport));
646 report_status (m, CAMEL_FILTER_STATUS_ACTION, 0, tuid);
647 }
648
649 /* Check for email sending */
650 from = (CamelAddress *) camel_internet_address_new ();
651 resent_from = camel_medium_get_header (CAMEL_MEDIUM (message), "Resent-From");
652 if (resent_from) {
653 camel_address_decode (from, resent_from);
654 } else {
655 iaddr = camel_mime_message_get_from (message);
656 camel_address_copy (from, CAMEL_ADDRESS (iaddr));
657 }
658
659 recipients = (CamelAddress *) camel_internet_address_new ();
660 for (i = 0; i < 3; i++) {
661 const gchar *type;
662
663 type = resent_from ? resent_recipients[i] : normal_recipients[i];
664 iaddr = camel_mime_message_get_recipients (message, type);
665 camel_address_cat (recipients, CAMEL_ADDRESS (iaddr));
666 }
667
668 if (camel_address_length (recipients) > 0) {
669 if (!camel_service_connect_sync (
670 service, cancellable, error))
671 goto exit;
672
673 if (!camel_transport_send_to_sync (
674 CAMEL_TRANSPORT (service), message,
675 from, recipients, cancellable, error))
676 goto exit;
677 }
678
679 /* Now check for posting, failures are ignored */
680 info = camel_message_info_new (NULL);
681 camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN, ~0);
682
683 for (header = xev; header; header = header->next) {
684 gchar *uri;
685
686 if (strcmp (header->name, "X-Evolution-PostTo") != 0)
687 continue;
688
689 /* TODO: don't lose errors */
690
691 uri = g_strstrip (g_strdup (header->value));
692 /* FIXME Not passing a GCancellable or GError here. */
693 folder = e_mail_session_uri_to_folder_sync (
694 m->session, uri, 0, NULL, NULL);
695 if (folder) {
696 /* FIXME Not passing a GCancellable or GError here. */
697 camel_folder_append_message_sync (
698 folder, message, info, NULL, NULL, NULL);
699 g_object_unref (folder);
700 folder = NULL;
701 }
702 g_free (uri);
703 }
704
705 /* post process */
706 mail_tool_restore_xevolution_headers (message, xev);
707
708 if (driver) {
709 camel_filter_driver_filter_message (
710 driver, message, info, NULL, NULL,
711 NULL, "", cancellable, &local_error);
712
713 if (local_error != NULL) {
714 if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
715 goto exit;
716
717 /* sending mail, filtering failed */
718 g_string_append_printf (
719 err, _("Failed to apply outgoing filters: %s"),
720 local_error->message);
721
722 g_clear_error (&local_error);
723 }
724 }
725
726 if (provider == NULL
727 || !(provider->flags & CAMEL_PROVIDER_DISABLE_SENT_FOLDER)) {
728 GError *local_error = NULL;
729
730 if (sent_folder_uri) {
731 folder = e_mail_session_uri_to_folder_sync (
732 m->session, sent_folder_uri, 0,
733 cancellable, &local_error);
734
735 if (local_error != NULL) {
736 g_string_append_printf (
737 err, _("Failed to append to %s: %s\n"
738 "Appending to local 'Sent' folder instead."),
739 sent_folder_uri,
740 local_error->message);
741 g_clear_error (&local_error);
742 }
743 }
744
745 if (!folder) {
746 folder = e_mail_session_get_local_folder (
747 m->session, E_MAIL_LOCAL_FOLDER_SENT);
748 g_object_ref (folder);
749 }
750
751 if (!camel_folder_append_message_sync (
752 folder, message, info,
753 NULL, cancellable, &local_error)) {
754
755 CamelFolder *sent_folder;
756
757 if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
758 goto exit;
759
760 sent_folder = e_mail_session_get_local_folder (
761 m->session, E_MAIL_LOCAL_FOLDER_SENT);
762
763 if (folder != sent_folder) {
764 const gchar *description;
765
766 description = camel_folder_get_description (folder);
767 if (err->len)
768 g_string_append (err, "\n\n");
769 g_string_append_printf (
770 err, _("Failed to append to %s: %s\n"
771 "Appending to local 'Sent' folder instead."),
772 description, local_error->message);
773 g_object_ref (sent_folder);
774 g_object_unref (folder);
775 folder = sent_folder;
776
777 g_clear_error (&local_error);
778 camel_folder_append_message_sync (
779 folder, message, info,
780 NULL, cancellable, &local_error);
781 }
782
783 if (local_error != NULL) {
784 if (g_error_matches (
785 local_error, G_IO_ERROR,
786 G_IO_ERROR_CANCELLED))
787 goto exit;
788
789 if (err->len)
790 g_string_append (err, "\n\n");
791 g_string_append_printf (
792 err, _("Failed to append to "
793 "local 'Sent' folder: %s"),
794 local_error->message);
795 }
796 }
797 }
798
799 if (local_error == NULL) {
800 /* Mark the draft message for deletion, if present. */
801 e_mail_session_handle_draft_headers_sync (
802 m->session, message, cancellable, &local_error);
803 if (local_error != NULL) {
804 g_warning (
805 "%s: Failed to handle draft headers: %s",
806 G_STRFUNC, local_error->message);
807 g_clear_error (&local_error);
808 }
809
810 /* Set flags on the original source message, if present.
811 * Source message refers to the message being forwarded
812 * or replied to. */
813 e_mail_session_handle_source_headers_sync (
814 m->session, message, cancellable, &local_error);
815 if (local_error != NULL) {
816 g_warning (
817 "%s: Failed to handle source headers: %s",
818 G_STRFUNC, local_error->message);
819 g_clear_error (&local_error);
820 }
821 }
822
823 if (local_error == NULL) {
824 camel_folder_set_message_flags (
825 queue, uid, CAMEL_MESSAGE_DELETED |
826 CAMEL_MESSAGE_SEEN, ~0);
827 /* Sync it to disk, since if it crashes in between,
828 * we keep sending it again on next start. */
829 /* FIXME Not passing a GCancellable or GError here. */
830 camel_folder_synchronize_sync (queue, FALSE, NULL, NULL);
831 }
832
833 if (err->len) {
834 /* set the culmulative exception report */
835 g_set_error (
836 &local_error, CAMEL_ERROR,
837 CAMEL_ERROR_GENERIC, "%s", err->str);
838 }
839
840 exit:
841 if (local_error != NULL)
842 g_propagate_error (error, local_error);
843
844 /* FIXME Not passing a GCancellable or GError here. */
845 if (folder) {
846 camel_folder_synchronize_sync (folder, FALSE, NULL, NULL);
847 g_object_unref (folder);
848 }
849 if (info)
850 camel_message_info_free (info);
851
852 if (service != NULL)
853 g_object_unref (service);
854
855 g_object_unref (recipients);
856 g_object_unref (from);
857 g_free (sent_folder_uri);
858 g_free (transport_uid);
859 camel_header_raw_clear (&xev);
860 g_string_free (err, TRUE);
861 g_object_unref (message);
862 }
863
864 /* ** SEND MAIL QUEUE ***************************************************** */
865
866 static void
867 report_status (struct _send_queue_msg *m,
868 enum camel_filter_status_t status,
869 gint pc,
870 const gchar *desc,
871 ...)
872 {
873 va_list ap;
874 gchar *str;
875
876 if (m->status) {
877 va_start (ap, desc);
878 str = g_strdup_vprintf (desc, ap);
879 va_end (ap);
880 m->status (m->driver, status, pc, str, m->status_data);
881 g_free (str);
882 }
883 }
884
885 static void
886 send_queue_exec (struct _send_queue_msg *m,
887 GCancellable *cancellable,
888 GError **error)
889 {
890 CamelFolder *sent_folder;
891 GPtrArray *uids, *send_uids = NULL;
892 gint i, j;
893 GError *local_error = NULL;
894
895 d (printf ("sending queue\n"));
896
897 sent_folder =
898 e_mail_session_get_local_folder (
899 m->session, E_MAIL_LOCAL_FOLDER_SENT);
900
901 if (!(uids = camel_folder_get_uids (m->queue)))
902 return;
903
904 send_uids = g_ptr_array_sized_new (uids->len);
905 for (i = 0, j = 0; i < uids->len; i++) {
906 CamelMessageInfo *info;
907
908 info = camel_folder_get_message_info (m->queue, uids->pdata[i]);
909 if (info) {
910 if ((camel_message_info_flags (info) & CAMEL_MESSAGE_DELETED) == 0)
911 send_uids->pdata[j++] = uids->pdata[i];
912 camel_folder_free_message_info (m->queue, info);
913 }
914 }
915
916 send_uids->len = j;
917 if (send_uids->len == 0) {
918 /* nothing to send */
919 camel_folder_free_uids (m->queue, uids);
920 g_ptr_array_free (send_uids, TRUE);
921 return;
922 }
923
924 camel_operation_push_message (cancellable, _("Sending message"));
925
926 /* NB: This code somewhat abuses the 'exception' stuff. Apart from
927 * fatal problems, it is also used as a mechanism to accumualte
928 * warning messages and present them back to the user. */
929
930 for (i = 0, j = 0; i < send_uids->len; i++) {
931 gint pc = (100 * i) / send_uids->len;
932
933 report_status (
934 m, CAMEL_FILTER_STATUS_START, pc,
935 _("Sending message %d of %d"), i + 1,
936 send_uids->len);
937
938 camel_operation_progress (
939 cancellable, (i + 1) * 100 / send_uids->len);
940
941 mail_send_message (
942 m, m->queue, send_uids->pdata[i], m->transport,
943 m->driver, cancellable, &local_error);
944 if (local_error != NULL) {
945 if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
946 /* merge exceptions into one */
947 if (m->base.error != NULL) {
948 gchar *old_message;
949
950 old_message = g_strdup (
951 m->base.error->message);
952 g_clear_error (&m->base.error);
953 g_set_error (
954 &m->base.error, CAMEL_ERROR,
955 CAMEL_ERROR_GENERIC,
956 "%s\n\n%s", old_message,
957 local_error->message);
958 g_free (old_message);
959
960 g_clear_error (&local_error);
961 } else {
962 g_propagate_error (&m->base.error, local_error);
963 local_error = NULL;
964 }
965
966 /* keep track of the number of failures */
967 j++;
968 } else {
969 /* transfer the USER_CANCEL error to the
970 * async op exception and then break */
971 g_propagate_error (&m->base.error, local_error);
972 local_error = NULL;
973 break;
974 }
975 }
976 }
977
978 j += (send_uids->len - i);
979
980 if (j > 0)
981 report_status (
982 m, CAMEL_FILTER_STATUS_END, 100,
983 /* Translators: The string is distinguished by total
984 * count of messages to be sent. Failed messages is
985 * always more than zero. */
986 ngettext (
987 "Failed to send a message",
988 "Failed to send %d of %d messages",
989 send_uids->len),
990 j, send_uids->len);
991 else if (g_error_matches (
992 m->base.error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
993 report_status (m, CAMEL_FILTER_STATUS_END, 100, _("Canceled."));
994 else
995 report_status (m, CAMEL_FILTER_STATUS_END, 100, _("Complete."));
996
997 if (m->driver) {
998 g_object_unref (m->driver);
999 m->driver = NULL;
1000 }
1001
1002 camel_folder_free_uids (m->queue, uids);
1003 g_ptr_array_free (send_uids, TRUE);
1004
1005 /* FIXME Not passing a GCancellable or GError here. */
1006 if (j <= 0 && m->base.error == NULL)
1007 camel_folder_synchronize_sync (m->queue, TRUE, NULL, NULL);
1008
1009 /* FIXME Not passing a GCancellable or GError here. */
1010 if (sent_folder)
1011 camel_folder_synchronize_sync (sent_folder, FALSE, NULL, NULL);
1012
1013 camel_operation_pop_message (cancellable);
1014 }
1015
1016 static void
1017 send_queue_done (struct _send_queue_msg *m)
1018 {
1019 if (m->done)
1020 m->done (m->data);
1021 }
1022
1023 static gchar *
1024 send_queue_desc (struct _send_queue_msg *m)
1025 {
1026 return g_strdup (_("Sending message"));
1027 }
1028
1029 static void
1030 send_queue_free (struct _send_queue_msg *m)
1031 {
1032 if (m->session != NULL)
1033 g_object_unref (m->session);
1034 if (m->driver != NULL)
1035 g_object_unref (m->driver);
1036 if (m->transport != NULL)
1037 g_object_unref (m->transport);
1038 g_object_unref (m->queue);
1039 }
1040
1041 static MailMsgInfo send_queue_info = {
1042 sizeof (struct _send_queue_msg),
1043 (MailMsgDescFunc) send_queue_desc,
1044 (MailMsgExecFunc) send_queue_exec,
1045 (MailMsgDoneFunc) send_queue_done,
1046 (MailMsgFreeFunc) send_queue_free
1047 };
1048
1049 /* same interface as fetch_mail, just 'cause i'm lazy today
1050 * (and we need to run it from the same spot?) */
1051 void
1052 mail_send_queue (EMailSession *session,
1053 CamelFolder *queue,
1054 CamelTransport *transport,
1055 const gchar *type,
1056 GCancellable *cancellable,
1057 CamelFilterGetFolderFunc get_folder,
1058 gpointer get_data,
1059 CamelFilterStatusFunc *status,
1060 gpointer status_data,
1061 void (*done)(gpointer data),
1062 gpointer data)
1063 {
1064 struct _send_queue_msg *m;
1065
1066 g_return_if_fail (E_IS_MAIL_SESSION (session));
1067
1068 m = mail_msg_new (&send_queue_info);
1069 m->session = g_object_ref (session);
1070 m->queue = g_object_ref (queue);
1071 m->transport = g_object_ref (transport);
1072 if (G_IS_CANCELLABLE (cancellable))
1073 m->base.cancellable = g_object_ref (cancellable);
1074 m->status = status;
1075 m->status_data = status_data;
1076 m->done = done;
1077 m->data = data;
1078
1079 m->driver = camel_session_get_filter_driver (
1080 CAMEL_SESSION (session), type, NULL);
1081 camel_filter_driver_set_folder_func (m->driver, get_folder, get_data);
1082
1083 mail_msg_unordered_push (m);
1084 }
1085
1086 /* ** TRANSFER MESSAGES **************************************************** */
1087
1088 struct _transfer_msg {
1089 MailMsg base;
1090
1091 EMailSession *session;
1092 CamelFolder *source;
1093 GPtrArray *uids;
1094 gboolean delete;
1095 gchar *dest_uri;
1096 guint32 dest_flags;
1097
1098 void (*done)(gboolean ok, gpointer data);
1099 gpointer data;
1100 };
1101
1102 static gchar *
1103 transfer_messages_desc (struct _transfer_msg *m)
1104 {
1105 return g_strdup_printf (
1106 m->delete ?
1107 _("Moving messages to '%s'") :
1108 _("Copying messages to '%s'"),
1109 m->dest_uri);
1110
1111 }
1112
1113 static void
1114 transfer_messages_exec (struct _transfer_msg *m,
1115 GCancellable *cancellable,
1116 GError **error)
1117 {
1118 CamelFolder *dest;
1119
1120 dest = e_mail_session_uri_to_folder_sync (
1121 m->session, m->dest_uri, m->dest_flags,
1122 cancellable, error);
1123 if (dest == NULL)
1124 return;
1125
1126 if (dest == m->source) {
1127 g_object_unref (dest);
1128 /* no-op */
1129 return;
1130 }
1131
1132 camel_folder_freeze (m->source);
1133 camel_folder_freeze (dest);
1134
1135 camel_folder_transfer_messages_to_sync (
1136 m->source, m->uids, dest, m->delete, NULL,
1137 cancellable, error);
1138
1139 /* make sure all deleted messages are marked as seen */
1140
1141 if (m->delete) {
1142 gint i;
1143
1144 for (i = 0; i < m->uids->len; i++)
1145 camel_folder_set_message_flags (
1146 m->source, m->uids->pdata[i],
1147 CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
1148 }
1149
1150 camel_folder_thaw (m->source);
1151 camel_folder_thaw (dest);
1152
1153 /* FIXME Not passing a GCancellable or GError here. */
1154 camel_folder_synchronize_sync (dest, FALSE, NULL, NULL);
1155 g_object_unref (dest);
1156 }
1157
1158 static void
1159 transfer_messages_done (struct _transfer_msg *m)
1160 {
1161 if (m->done)
1162 m->done (m->base.error == NULL, m->data);
1163 }
1164
1165 static void
1166 transfer_messages_free (struct _transfer_msg *m)
1167 {
1168 g_object_unref (m->session);
1169 g_object_unref (m->source);
1170 g_free (m->dest_uri);
1171 em_utils_uids_free (m->uids);
1172 }
1173
1174 static MailMsgInfo transfer_messages_info = {
1175 sizeof (struct _transfer_msg),
1176 (MailMsgDescFunc) transfer_messages_desc,
1177 (MailMsgExecFunc) transfer_messages_exec,
1178 (MailMsgDoneFunc) transfer_messages_done,
1179 (MailMsgFreeFunc) transfer_messages_free
1180 };
1181
1182 void
1183 mail_transfer_messages (EMailSession *session,
1184 CamelFolder *source,
1185 GPtrArray *uids,
1186 gboolean delete_from_source,
1187 const gchar *dest_uri,
1188 guint32 dest_flags,
1189 void (*done) (gboolean ok,
1190 gpointer data),
1191 gpointer data)
1192 {
1193 struct _transfer_msg *m;
1194
1195 g_return_if_fail (CAMEL_IS_FOLDER (source));
1196 g_return_if_fail (uids != NULL);
1197 g_return_if_fail (dest_uri != NULL);
1198
1199 m = mail_msg_new (&transfer_messages_info);
1200 m->session = g_object_ref (session);
1201 m->source = g_object_ref (source);
1202 m->uids = uids;
1203 m->delete = delete_from_source;
1204 m->dest_uri = g_strdup (dest_uri);
1205 m->dest_flags = dest_flags;
1206 m->done = done;
1207 m->data = data;
1208
1209 mail_msg_slow_ordered_push (m);
1210 }
1211
1212 /* ** SYNC FOLDER ********************************************************* */
1213
1214 struct _sync_folder_msg {
1215 MailMsg base;
1216
1217 CamelFolder *folder;
1218 void (*done) (CamelFolder *folder, gpointer data);
1219 gpointer data;
1220 };
1221
1222 static gchar *
1223 sync_folder_desc (struct _sync_folder_msg *m)
1224 {
1225 return g_strdup_printf (
1226 _("Storing folder '%s'"),
1227 camel_folder_get_full_name (m->folder));
1228 }
1229
1230 static void
1231 sync_folder_exec (struct _sync_folder_msg *m,
1232 GCancellable *cancellable,
1233 GError **error)
1234 {
1235 camel_folder_synchronize_sync (
1236 m->folder, FALSE, cancellable, error);
1237 }
1238
1239 static void
1240 sync_folder_done (struct _sync_folder_msg *m)
1241 {
1242 if (m->done)
1243 m->done (m->folder, m->data);
1244 }
1245
1246 static void
1247 sync_folder_free (struct _sync_folder_msg *m)
1248 {
1249 if (m->folder)
1250 g_object_unref (m->folder);
1251 }
1252
1253 static MailMsgInfo sync_folder_info = {
1254 sizeof (struct _sync_folder_msg),
1255 (MailMsgDescFunc) sync_folder_desc,
1256 (MailMsgExecFunc) sync_folder_exec,
1257 (MailMsgDoneFunc) sync_folder_done,
1258 (MailMsgFreeFunc) sync_folder_free
1259 };
1260
1261 void
1262 mail_sync_folder (CamelFolder *folder,
1263 void (*done) (CamelFolder *folder,
1264 gpointer data),
1265 gpointer data)
1266 {
1267 struct _sync_folder_msg *m;
1268
1269 m = mail_msg_new (&sync_folder_info);
1270 m->folder = g_object_ref (folder);
1271 m->data = data;
1272 m->done = done;
1273
1274 mail_msg_slow_ordered_push (m);
1275 }
1276
1277 /* ** SYNC STORE ********************************************************* */
1278
1279 struct _sync_store_msg {
1280 MailMsg base;
1281
1282 CamelStore *store;
1283 gint expunge;
1284 void (*done) (CamelStore *store, gpointer data);
1285 gpointer data;
1286 };
1287
1288 static gchar *
1289 sync_store_desc (struct _sync_store_msg *m)
1290 {
1291 CamelURL *url;
1292 gchar *uri, *res;
1293
1294 url = camel_service_new_camel_url (CAMEL_SERVICE (m->store));
1295 uri = camel_url_to_string (url, CAMEL_URL_HIDE_ALL);
1296 camel_url_free (url);
1297
1298 res = g_strdup_printf (
1299 m->expunge ?
1300 _("Expunging and storing account '%s'") :
1301 _("Storing account '%s'"),
1302 uri);
1303 g_free (uri);
1304
1305 return res;
1306 }
1307
1308 static void
1309 sync_store_exec (struct _sync_store_msg *m,
1310 GCancellable *cancellable,
1311 GError **error)
1312 {
1313 camel_store_synchronize_sync (
1314 m->store, m->expunge,
1315 cancellable, error);
1316 }
1317
1318 static void
1319 sync_store_done (struct _sync_store_msg *m)
1320 {
1321 if (m->done)
1322 m->done (m->store, m->data);
1323 }
1324
1325 static void
1326 sync_store_free (struct _sync_store_msg *m)
1327 {
1328 g_object_unref (m->store);
1329 }
1330
1331 static MailMsgInfo sync_store_info = {
1332 sizeof (struct _sync_store_msg),
1333 (MailMsgDescFunc) sync_store_desc,
1334 (MailMsgExecFunc) sync_store_exec,
1335 (MailMsgDoneFunc) sync_store_done,
1336 (MailMsgFreeFunc) sync_store_free
1337 };
1338
1339 void
1340 mail_sync_store (CamelStore *store,
1341 gint expunge,
1342 void (*done) (CamelStore *store,
1343 gpointer data),
1344 gpointer data)
1345 {
1346 struct _sync_store_msg *m;
1347
1348 m = mail_msg_new (&sync_store_info);
1349 m->store = g_object_ref (store);
1350 m->expunge = expunge;
1351 m->data = data;
1352 m->done = done;
1353
1354 mail_msg_slow_ordered_push (m);
1355 }
1356
1357 /* ******************************************************************************** */
1358
1359 struct _empty_trash_msg {
1360 MailMsg base;
1361
1362 CamelStore *store;
1363 };
1364
1365 static gchar *
1366 empty_trash_desc (struct _empty_trash_msg *m)
1367 {
1368 CamelService *service;
1369 const gchar *display_name;
1370
1371 service = CAMEL_SERVICE (m->store);
1372 display_name = camel_service_get_display_name (service);
1373
1374 return g_strdup_printf (
1375 _("Emptying trash in '%s'"), display_name);
1376 }
1377
1378 static void
1379 empty_trash_exec (struct _empty_trash_msg *m,
1380 GCancellable *cancellable,
1381 GError **error)
1382 {
1383 CamelService *service;
1384 CamelFolder *trash;
1385
1386 service = CAMEL_SERVICE (m->store);
1387
1388 if (!camel_service_connect_sync (service, cancellable, error))
1389 return;
1390
1391 trash = camel_store_get_trash_folder_sync (
1392 m->store, cancellable, error);
1393
1394 if (trash != NULL) {
1395 e_mail_folder_expunge_sync (trash, cancellable, error);
1396 g_object_unref (trash);
1397 }
1398 }
1399
1400 static void
1401 empty_trash_done (struct _empty_trash_msg *m)
1402 {
1403 }
1404
1405 static void
1406 empty_trash_free (struct _empty_trash_msg *m)
1407 {
1408 if (m->store)
1409 g_object_unref (m->store);
1410 }
1411
1412 static MailMsgInfo empty_trash_info = {
1413 sizeof (struct _empty_trash_msg),
1414 (MailMsgDescFunc) empty_trash_desc,
1415 (MailMsgExecFunc) empty_trash_exec,
1416 (MailMsgDoneFunc) empty_trash_done,
1417 (MailMsgFreeFunc) empty_trash_free
1418 };
1419
1420 void
1421 mail_empty_trash (CamelStore *store)
1422 {
1423 struct _empty_trash_msg *m;
1424
1425 g_return_if_fail (CAMEL_IS_STORE (store));
1426
1427 m = mail_msg_new (&empty_trash_info);
1428 m->store = g_object_ref (store);
1429
1430 mail_msg_slow_ordered_push (m);
1431 }
1432
1433 /* ** Execute Shell Command ************************************************ */
1434
1435 void
1436 mail_execute_shell_command (CamelFilterDriver *driver,
1437 gint argc,
1438 gchar **argv,
1439 gpointer data)
1440 {
1441 if (argc <= 0)
1442 return;
1443
1444 g_spawn_async (NULL, argv, NULL, 0, NULL, data, NULL, NULL);
1445 }