No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | e-mail-session.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None | |
clang-analyzer | no-output-found | e-mail-session.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /*
2 * e-mail-session.c
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 * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
20 *
21 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22 * Copyright (C) 2009 Intel Corporation
23 *
24 */
25
26 /* mail-session.c: handles the session information and resource manipulation */
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31
32 #include <errno.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include <glib/gi18n.h>
37 #include <glib/gstdio.h>
38
39 #include <gtk/gtk.h>
40
41 #ifdef HAVE_CANBERRA
42 #include <canberra-gtk.h>
43 #endif
44
45 #include <libebackend/libebackend.h>
46 #include <libedataserverui/libedataserverui.h>
47
48 #include "libemail-utils/mail-mt.h"
49
50 /* This is our hack, not part of libcamel. */
51 #include "camel-null-store.h"
52
53 #include "e-mail-authenticator.h"
54 #include "e-mail-junk-filter.h"
55 #include "e-mail-session.h"
56 #include "e-mail-folder-utils.h"
57 #include "e-mail-utils.h"
58 #include "mail-config.h"
59 #include "mail-ops.h"
60 #include "mail-tools.h"
61
62 #define E_MAIL_SESSION_GET_PRIVATE(obj) \
63 (G_TYPE_INSTANCE_GET_PRIVATE \
64 ((obj), E_TYPE_MAIL_SESSION, EMailSessionPrivate))
65
66 typedef struct _AsyncContext AsyncContext;
67
68 struct _EMailSessionPrivate {
69 MailFolderCache *folder_cache;
70 ESourceRegistry *registry;
71
72 /* ESource UID -> Timeout ID */
73 GHashTable *auto_refresh_table;
74
75 gulong source_added_handler_id;
76 gulong source_removed_handler_id;
77 gulong source_enabled_handler_id;
78 gulong source_disabled_handler_id;
79 gulong default_mail_account_handler_id;
80
81 CamelService *local_store;
82 CamelService *vfolder_store;
83
84 FILE *filter_logfile;
85 GHashTable *junk_filters;
86 EProxy *proxy;
87
88 /* Local folder cache. */
89 GPtrArray *local_folders;
90 GPtrArray *local_folder_uris;
91
92 guint preparing_flush;
93 GMutex *preparing_flush_lock;
94 };
95
96 struct _AsyncContext {
97 /* arguments */
98 CamelStoreGetFolderFlags flags;
99 gchar *uid;
100 gchar *uri;
101
102 /* results */
103 CamelFolder *folder;
104 };
105
106 enum {
107 PROP_0,
108 PROP_FOLDER_CACHE,
109 PROP_JUNK_FILTER_NAME,
110 PROP_LOCAL_STORE,
111 PROP_REGISTRY,
112 PROP_VFOLDER_STORE
113 };
114
115 static const gchar *local_folder_names[E_MAIL_NUM_LOCAL_FOLDERS] = {
116 N_("Inbox"), /* E_MAIL_LOCAL_FOLDER_INBOX */
117 N_("Drafts"), /* E_MAIL_LOCAL_FOLDER_DRAFTS */
118 N_("Outbox"), /* E_MAIL_LOCAL_FOLDER_OUTBOX */
119 N_("Sent"), /* E_MAIL_LOCAL_FOLDER_SENT */
120 N_("Templates"), /* E_MAIL_LOCAL_FOLDER_TEMPLATES */
121 "Inbox" /* E_MAIL_LOCAL_FOLDER_LOCAL_INBOX */
122 };
123
124 enum {
125 FLUSH_OUTBOX,
126 REFRESH_SERVICE,
127 STORE_ADDED,
128 STORE_REMOVED,
129 LAST_SIGNAL
130 };
131
132 static guint signals[LAST_SIGNAL];
133
134 static gchar *mail_data_dir;
135 static gchar *mail_cache_dir;
136 static gchar *mail_config_dir;
137
138 G_DEFINE_TYPE_WITH_CODE (
139 EMailSession,
140 e_mail_session,
141 CAMEL_TYPE_SESSION,
142 G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
143
144 /* Support for CamelSession.alert_user() *************************************/
145
146 static GQueue user_message_queue = { NULL, NULL, 0 };
147
148 struct _user_message_msg {
149 MailMsg base;
150
151 CamelSessionAlertType type;
152 gchar *prompt;
153 GSList *button_captions;
154 EFlag *done;
155
156 gint result;
157 guint ismain : 1;
158 };
159
160 static void user_message_exec (struct _user_message_msg *m,
161 GCancellable *cancellable,
162 GError **error);
163
164 static void
165 user_message_response_free (struct _user_message_msg *m)
166 {
167
168 /* check for pendings */
169 if (!g_queue_is_empty (&user_message_queue)) {
170 GCancellable *cancellable;
171
172 m = g_queue_pop_head (&user_message_queue);
173 cancellable = m->base.cancellable;
174 user_message_exec (m, cancellable, &m->base.error);
175 mail_msg_unref (m);
176 }
177 }
178
179 /* clicked, send back the reply */
180 static void
181 user_message_response (struct _user_message_msg *m)
182 {
183 /* if !allow_cancel, then we've already replied */
184 if (m->button_captions) {
185 m->result = TRUE; //If Accepted
186 e_flag_set (m->done);
187 }
188
189 user_message_response_free (m);
190 }
191
192 static void
193 user_message_exec (struct _user_message_msg *m,
194 GCancellable *cancellable,
195 GError **error)
196 {
197 /* XXX This is a case where we need to be able to construct
198 * custom EAlerts without a predefined XML definition. */
199 if (m->ismain) {
200 /* Use DBUS to raise dialogs in clients and reply back.
201 * For now say accept all. */
202 user_message_response (m);
203 } else
204 g_queue_push_tail (&user_message_queue, mail_msg_ref (m));
205 }
206
207 static void
208 user_message_free (struct _user_message_msg *m)
209 {
210 g_free (m->prompt);
211 g_slist_free_full (m->button_captions, g_free);
212 e_flag_free (m->done);
213 }
214
215 static MailMsgInfo user_message_info = {
216 sizeof (struct _user_message_msg),
217 (MailMsgDescFunc) NULL,
218 (MailMsgExecFunc) user_message_exec,
219 (MailMsgDoneFunc) NULL,
220 (MailMsgFreeFunc) user_message_free
221 };
222
223 /* Support for CamelSession.get_filter_driver () *****************************/
224
225 static CamelFolder *
226 get_folder (CamelFilterDriver *d,
227 const gchar *uri,
228 gpointer user_data,
229 GError **error)
230 {
231 EMailSession *session = E_MAIL_SESSION (user_data);
232
233 /* FIXME Not passing a GCancellable here. */
234 /* FIXME Need a camel_filter_driver_get_session(). */
235 return e_mail_session_uri_to_folder_sync (
236 session, uri, 0, NULL, error);
237 }
238
239 static CamelFilterDriver *
240 main_get_filter_driver (CamelSession *session,
241 const gchar *type,
242 GError **error)
243 {
244 CamelFilterDriver *driver;
245 EMailSession *ms = (EMailSession *) session;
246 GSettings *settings;
247
248 settings = g_settings_new ("org.gnome.evolution.mail");
249
250 driver = camel_filter_driver_new (session);
251 camel_filter_driver_set_folder_func (driver, get_folder, session);
252
253 if (g_settings_get_boolean (settings, "filters-log-actions")) {
254 if (ms->priv->filter_logfile == NULL) {
255 gchar *filename;
256
257 filename = g_settings_get_string (settings, "filters-log-file");
258 if (filename) {
259 ms->priv->filter_logfile = g_fopen (filename, "a+");
260 g_free (filename);
261 }
262 }
263
264 if (ms->priv->filter_logfile)
265 camel_filter_driver_set_logfile (driver, ms->priv->filter_logfile);
266 }
267
268 g_object_unref (settings);
269
270 return driver;
271 }
272
273 static gboolean
274 session_forward_to_flush_outbox_cb (gpointer user_data)
275 {
276 EMailSession *session = E_MAIL_SESSION (user_data);
277
278 g_mutex_lock (session->priv->preparing_flush_lock);
279 session->priv->preparing_flush = 0;
280 g_mutex_unlock (session->priv->preparing_flush_lock);
281
282 /* Connect to this and call mail_send in the main email client.*/
283 g_signal_emit (session, signals[FLUSH_OUTBOX], 0);
284
285 return FALSE;
286 }
287
288 static void
289 async_context_free (AsyncContext *context)
290 {
291 if (context->folder != NULL)
292 g_object_unref (context->folder);
293
294 g_free (context->uid);
295 g_free (context->uri);
296
297 g_slice_free (AsyncContext, context);
298 }
299
300 static gchar *
301 mail_session_resolve_popb4smtp (ESourceRegistry *registry,
302 CamelService *smtp_service)
303 {
304 GList *list, *link;
305 const gchar *extension_name;
306 const gchar *smtp_uid;
307 gchar *pop_uid = NULL;
308
309 /* Find a POP account that uses the given smtp_service as its
310 * transport. XXX This isn't foolproof though, since we don't
311 * check that the POP server is at the same domain as the SMTP
312 * server, which is kind of the point of POPB4SMTP. */
313
314 smtp_uid = camel_service_get_uid (smtp_service);
315 g_return_val_if_fail (smtp_uid != NULL, NULL);
316
317 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
318 list = e_source_registry_list_sources (registry, extension_name);
319
320 for (link = list; link != NULL; link = g_list_next (link)) {
321 ESource *source = E_SOURCE (link->data);
322 ESourceExtension *extension;
323 const gchar *backend_name;
324 gchar *uid;
325
326 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
327 extension = e_source_get_extension (source, extension_name);
328
329 /* We're only interested in POP accounts. */
330
331 backend_name = e_source_backend_get_backend_name (
332 E_SOURCE_BACKEND (extension));
333 if (g_strcmp0 (backend_name, "pop") != 0)
334 continue;
335
336 /* Get the mail account's default mail identity. */
337
338 uid = e_source_mail_account_dup_identity_uid (
339 E_SOURCE_MAIL_ACCOUNT (extension));
340 source = e_source_registry_ref_source (registry, uid);
341 g_free (uid);
342
343 if (source == NULL)
344 continue;
345
346 /* Get the mail identity's default mail transport. */
347
348 extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
349 extension = e_source_get_extension (source, extension_name);
350
351 uid = e_source_mail_submission_dup_transport_uid (
352 E_SOURCE_MAIL_SUBMISSION (extension));
353
354 g_object_unref (source);
355
356 if (g_strcmp0 (uid, smtp_uid) == 0) {
357 pop_uid = uid;
358 break;
359 }
360
361 g_free (uid);
362 }
363
364 g_list_free_full (list, (GDestroyNotify) g_object_unref);
365
366 return pop_uid;
367 }
368
369 static void
370 mail_session_check_junk_notify (GSettings *settings,
371 const gchar *key,
372 CamelSession *session)
373 {
374 if (strcmp (key, "junk-check-incoming") == 0)
375 camel_session_set_check_junk (
376 session, g_settings_get_boolean (settings, key));
377 }
378
379 static const gchar *
380 mail_session_get_junk_filter_name (EMailSession *session)
381 {
382 CamelJunkFilter *junk_filter;
383 GHashTableIter iter;
384 gpointer key, value;
385
386 /* XXX This property can be removed once Evolution moves to
387 * GSettings and can use transform functions when binding
388 * properties to settings. That's why this is private. */
389
390 g_hash_table_iter_init (&iter, session->priv->junk_filters);
391 junk_filter = camel_session_get_junk_filter (CAMEL_SESSION (session));
392
393 while (g_hash_table_iter_next (&iter, &key, &value)) {
394 if (junk_filter == CAMEL_JUNK_FILTER (value))
395 return (const gchar *) key;
396 }
397
398 if (junk_filter != NULL)
399 g_warning (
400 "Camel is using a junk filter "
401 "unknown to Evolution of type %s",
402 G_OBJECT_TYPE_NAME (junk_filter));
403
404 return "";
405 }
406
407 static void
408 mail_session_set_junk_filter_name (EMailSession *session,
409 const gchar *junk_filter_name)
410 {
411 CamelJunkFilter *junk_filter = NULL;
412
413 /* XXX This property can be removed once Evolution moves to
414 * GSettings and can use transform functions when binding
415 * properties to settings. That's why this is private. */
416
417 /* An empty string is equivalent to a NULL string. */
418 if (junk_filter_name != NULL && *junk_filter_name == '\0')
419 junk_filter_name = NULL;
420
421 if (junk_filter_name != NULL) {
422 junk_filter = g_hash_table_lookup (
423 session->priv->junk_filters, junk_filter_name);
424 if (junk_filter != NULL) {
425 if (!e_mail_junk_filter_available (
426 E_MAIL_JUNK_FILTER (junk_filter)))
427 junk_filter = NULL;
428 } else {
429 g_warning (
430 "Unrecognized junk filter name "
431 "'%s' in GSettings", junk_filter_name);
432 }
433 }
434
435 camel_session_set_junk_filter (CAMEL_SESSION (session), junk_filter);
436
437 /* XXX We emit the "notify" signal in mail_session_notify(). */
438 }
439
440 static void
441 mail_session_refresh_cb (ESource *source,
442 CamelSession *session)
443 {
444 CamelService *service;
445 const gchar *uid;
446
447 uid = e_source_get_uid (source);
448 service = camel_session_ref_service (session, uid);
449 g_return_if_fail (service != NULL);
450
451 g_signal_emit (session, signals[REFRESH_SERVICE], 0, service);
452
453 g_object_unref (service);
454 }
455
456 static gboolean
457 mail_session_check_goa_mail_disabled (EMailSession *session,
458 ESource *source)
459 {
460 ESource *goa_source;
461 ESourceRegistry *registry;
462 gboolean goa_mail_disabled = FALSE;
463
464 registry = e_mail_session_get_registry (session);
465
466 goa_source = e_source_registry_find_extension (
467 registry, source, E_SOURCE_EXTENSION_GOA);
468
469 if (goa_source != NULL) {
470 goa_mail_disabled = !e_source_get_enabled (source);
471 g_object_unref (goa_source);
472 }
473
474 return goa_mail_disabled;
475 }
476
477 static void
478 mail_session_add_from_source (EMailSession *session,
479 CamelProviderType type,
480 ESource *source)
481 {
482 ESourceBackend *extension;
483 CamelService *service;
484 const gchar *uid;
485 const gchar *backend_name;
486 const gchar *display_name;
487 const gchar *extension_name;
488 GError *error = NULL;
489
490 switch (type) {
491 case CAMEL_PROVIDER_STORE:
492 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
493 break;
494 case CAMEL_PROVIDER_TRANSPORT:
495 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
496 break;
497 default:
498 g_return_if_reached ();
499 }
500
501 uid = e_source_get_uid (source);
502 display_name = e_source_get_display_name (source);
503
504 extension = e_source_get_extension (source, extension_name);
505 backend_name = e_source_backend_get_backend_name (extension);
506
507 /* Sanity checks. */
508 g_return_if_fail (uid != NULL);
509 g_return_if_fail (backend_name != NULL);
510
511 /* Collection sources with a [GNOME Online Accounts] extension
512 * require special handling. If the collection's mail-enabled
513 * flag is FALSE, do not add a CamelService. The account must
514 * not appear anywhere, not even in the Mail Accounts list. */
515 if (mail_session_check_goa_mail_disabled (session, source))
516 return;
517
518 service = camel_session_add_service (
519 CAMEL_SESSION (session), uid,
520 backend_name, type, &error);
521
522 /* Our own CamelSession.add_service() method will handle the
523 * new CamelService, so we only need to unreference it here. */
524 if (service != NULL)
525 g_object_unref (service);
526
527 if (error != NULL) {
528 g_warning (
529 "Failed to add service '%s' (%s): %s",
530 display_name, uid, error->message);
531 g_error_free (error);
532 }
533
534 /* Set up auto-refresh. */
535 extension_name = E_SOURCE_EXTENSION_REFRESH;
536 if (e_source_has_extension (source, extension_name)) {
537 guint timeout_id;
538
539 /* Transports should not have a refresh extension. */
540 g_warn_if_fail (type != CAMEL_PROVIDER_TRANSPORT);
541
542 timeout_id = e_source_refresh_add_timeout (
543 source, NULL, (ESourceRefreshFunc)
544 mail_session_refresh_cb, session,
545 (GDestroyNotify) NULL);
546
547 g_hash_table_insert (
548 session->priv->auto_refresh_table,
549 g_strdup (uid),
550 GUINT_TO_POINTER (timeout_id));
551 }
552 }
553
554 static void
555 mail_session_source_added_cb (ESourceRegistry *registry,
556 ESource *source,
557 EMailSession *session)
558 {
559 CamelProviderType provider_type;
560 const gchar *extension_name;
561
562 provider_type = CAMEL_PROVIDER_STORE;
563 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
564
565 if (e_source_has_extension (source, extension_name))
566 mail_session_add_from_source (session, provider_type, source);
567
568 provider_type = CAMEL_PROVIDER_TRANSPORT;
569 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
570
571 if (e_source_has_extension (source, extension_name))
572 mail_session_add_from_source (session, provider_type, source);
573 }
574
575 static void
576 mail_session_source_removed_cb (ESourceRegistry *registry,
577 ESource *source,
578 EMailSession *session)
579 {
580 CamelSession *camel_session;
581 CamelService *service;
582 const gchar *uid;
583
584 camel_session = CAMEL_SESSION (session);
585
586 uid = e_source_get_uid (source);
587 service = camel_session_ref_service (camel_session, uid);
588
589 if (service != NULL) {
590 camel_session_remove_service (camel_session, service);
591 g_object_unref (service);
592 }
593 }
594
595 static void
596 mail_session_source_enabled_cb (ESourceRegistry *registry,
597 ESource *source,
598 EMailSession *session)
599 {
600 ESource *goa_source;
601
602 /* If the source is linked to a GNOME Online Account,
603 * enabling the source is equivalent to adding it. */
604
605 goa_source = e_source_registry_find_extension (
606 registry, source, E_SOURCE_EXTENSION_GOA);
607
608 if (goa_source != NULL) {
609 mail_session_source_added_cb (registry, source, session);
610 g_object_unref (goa_source);
611 }
612 }
613
614 static void
615 mail_session_source_disabled_cb (ESourceRegistry *registry,
616 ESource *source,
617 EMailSession *session)
618 {
619 ESource *goa_source;
620
621 /* If the source is linked to a GNOME Online Account,
622 * disabling the source is equivalent to removing it. */
623
624 goa_source = e_source_registry_find_extension (
625 registry, source, E_SOURCE_EXTENSION_GOA);
626
627 if (goa_source != NULL) {
628 mail_session_source_removed_cb (registry, source, session);
629 g_object_unref (goa_source);
630 }
631 }
632
633 static void
634 mail_session_default_mail_account_cb (ESourceRegistry *registry,
635 GParamSpec *pspec,
636 EMailSession *session)
637 {
638 ESource *source;
639 ESourceMailAccount *extension;
640 const gchar *extension_name;
641 gchar *uid;
642
643 /* If the default mail account names a valid mail
644 * identity, make it the default mail identity. */
645
646 /* XXX I debated whether to have ESourceRegistry do this
647 * itself but it seems like an Evolution policy to me
648 * right now. I may change my mind in the future, or
649 * decide not to do this synchronization at all. */
650
651 source = e_source_registry_ref_default_mail_account (registry);
652 g_return_if_fail (source != NULL);
653
654 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
655 extension = e_source_get_extension (source, extension_name);
656 uid = e_source_mail_account_dup_identity_uid (extension);
657
658 g_object_unref (source);
659 source = NULL;
660
661 if (uid != NULL) {
662 source = e_source_registry_ref_source (registry, uid);
663 g_free (uid);
664 }
665
666 if (source != NULL) {
667 e_source_registry_set_default_mail_identity (registry, source);
668 g_object_unref (source);
669 }
670 }
671
672 static void
673 mail_session_configure_local_store (EMailSession *session)
674 {
675 CamelLocalSettings *local_settings;
676 CamelSession *camel_session;
677 CamelSettings *settings;
678 CamelService *service;
679 const gchar *data_dir;
680 const gchar *uid;
681 gchar *path;
682 gint ii;
683
684 camel_session = CAMEL_SESSION (session);
685
686 uid = E_MAIL_SESSION_LOCAL_UID;
687 service = camel_session_ref_service (camel_session, uid);
688 session->priv->local_store = service; /* takes ownership */
689 g_return_if_fail (service != NULL);
690
691 settings = camel_service_ref_settings (service);
692
693 data_dir = camel_session_get_user_data_dir (camel_session);
694 path = g_build_filename (data_dir, E_MAIL_SESSION_LOCAL_UID, NULL);
695
696 local_settings = CAMEL_LOCAL_SETTINGS (settings);
697 camel_local_settings_set_path (local_settings, path);
698
699 g_free (path);
700
701 g_object_unref (settings);
702
703 /* Shouldn't need to worry about other mail applications
704 * altering files in our local mail store. */
705 g_object_set (service, "need-summary-check", FALSE, NULL);
706
707 /* Populate the local folder cache. */
708 for (ii = 0; ii < E_MAIL_NUM_LOCAL_FOLDERS; ii++) {
709 CamelFolder *folder;
710 gchar *folder_uri;
711 const gchar *display_name;
712 GError *error = NULL;
713
714 display_name = local_folder_names[ii];
715
716 /* XXX This blocks but should be fast. */
717 if (ii == E_MAIL_LOCAL_FOLDER_LOCAL_INBOX)
718 folder = camel_store_get_inbox_folder_sync (
719 CAMEL_STORE (service), NULL, &error);
720 else
721 folder = camel_store_get_folder_sync (
722 CAMEL_STORE (service), display_name,
723 CAMEL_STORE_FOLDER_CREATE, NULL, &error);
724
725 folder_uri = e_mail_folder_uri_build (
726 CAMEL_STORE (service), display_name);
727
728 /* The arrays take ownership of the items added. */
729 g_ptr_array_add (session->priv->local_folders, folder);
730 g_ptr_array_add (session->priv->local_folder_uris, folder_uri);
731
732 if (error != NULL) {
733 g_critical ("%s: %s", G_STRFUNC, error->message);
734 g_error_free (error);
735 }
736 }
737 }
738
739 static void
740 mail_session_configure_vfolder_store (EMailSession *session)
741 {
742 CamelSession *camel_session;
743 CamelService *service;
744 const gchar *uid;
745
746 camel_session = CAMEL_SESSION (session);
747
748 uid = E_MAIL_SESSION_VFOLDER_UID;
749 service = camel_session_ref_service (camel_session, uid);
750 session->priv->vfolder_store = service; /* takes ownership */
751 g_return_if_fail (service != NULL);
752
753 camel_service_connect_sync (service, NULL, NULL);
754
755 /* XXX There's more configuration to do in vfolder_load_storage()
756 * but it requires an EMailBackend, which we don't have access
757 * to from here, so it has to be called from elsewhere. Kinda
758 * thinking about reworking that... */
759 }
760
761 static void
762 mail_session_force_refresh (EMailSession *session)
763 {
764 ESourceRegistry *registry;
765 GHashTableIter iter;
766 GSettings *settings;
767 gboolean unconditionally;
768 gpointer key;
769
770 /* Only refresh when the session is online. */
771 if (!camel_session_get_online (CAMEL_SESSION (session)))
772 return;
773
774 /* FIXME EMailSession should define properties for these. */
775 settings = g_settings_new ("org.gnome.evolution.mail");
776 unconditionally =
777 g_settings_get_boolean (settings, "send-recv-on-start") &&
778 g_settings_get_boolean (settings, "send-recv-all-on-start");
779 g_object_unref (settings);
780
781 registry = e_mail_session_get_registry (session);
782 g_hash_table_iter_init (&iter, session->priv->auto_refresh_table);
783
784 while (g_hash_table_iter_next (&iter, &key, NULL)) {
785 ESource *source;
786 ESourceRefresh *extension;
787 const gchar *extension_name;
788 gboolean refresh_enabled;
789
790 /* The hash table key is the ESource UID. */
791 source = e_source_registry_ref_source (registry, key);
792
793 if (source == NULL)
794 continue;
795
796 extension_name = E_SOURCE_EXTENSION_REFRESH;
797 extension = e_source_get_extension (source, extension_name);
798 refresh_enabled = e_source_refresh_get_enabled (extension);
799
800 if (refresh_enabled || unconditionally)
801 e_source_refresh_force_timeout (source);
802
803 g_object_unref (source);
804 }
805 }
806
807 static void
808 mail_session_cancel_refresh (EMailSession *session)
809 {
810 ESourceRegistry *registry;
811 GHashTableIter iter;
812 gpointer key, value;
813
814 registry = e_mail_session_get_registry (session);
815 g_hash_table_iter_init (&iter, session->priv->auto_refresh_table);
816
817 while (g_hash_table_iter_next (&iter, &key, &value)) {
818 ESource *source;
819 guint timeout_id;
820
821 /* The hash table key is the ESource UID. */
822 source = e_source_registry_ref_source (registry, key);
823
824 /* The hash table value is the refresh timeout ID. */
825 timeout_id = GPOINTER_TO_UINT (value);
826
827 if (source == NULL)
828 continue;
829
830 e_source_refresh_remove_timeout (source, timeout_id);
831
832 g_object_unref (source);
833 }
834
835 /* All timeouts cancelled so clear the auto-refresh table. */
836 g_hash_table_remove_all (session->priv->auto_refresh_table);
837 }
838
839 static gboolean
840 mail_session_idle_refresh_cb (EMailSession *session)
841 {
842 /* This only runs once at startup (if settings allow). */
843
844 if (camel_session_get_online (CAMEL_SESSION (session))) {
845 mail_session_force_refresh (session);
846
847 /* Also flush the Outbox. */
848 g_signal_emit (session, signals[FLUSH_OUTBOX], 0);
849 }
850
851 /* Listen for network state changes and force a
852 * mail store refresh when coming back online. */
853 g_signal_connect (
854 session, "notify::online",
855 G_CALLBACK (mail_session_force_refresh), NULL);
856
857 return FALSE;
858 }
859
860 static void
861 mail_session_set_registry (EMailSession *session,
862 ESourceRegistry *registry)
863 {
864 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
865 g_return_if_fail (session->priv->registry == NULL);
866
867 session->priv->registry = g_object_ref (registry);
868 }
869
870 static void
871 mail_session_set_property (GObject *object,
872 guint property_id,
873 const GValue *value,
874 GParamSpec *pspec)
875 {
876 switch (property_id) {
877 case PROP_JUNK_FILTER_NAME:
878 mail_session_set_junk_filter_name (
879 E_MAIL_SESSION (object),
880 g_value_get_string (value));
881 return;
882
883 case PROP_REGISTRY:
884 mail_session_set_registry (
885 E_MAIL_SESSION (object),
886 g_value_get_object (value));
887 return;
888 }
889
890 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
891 }
892
893 static void
894 mail_session_get_property (GObject *object,
895 guint property_id,
896 GValue *value,
897 GParamSpec *pspec)
898 {
899 switch (property_id) {
900 case PROP_FOLDER_CACHE:
901 g_value_set_object (
902 value,
903 e_mail_session_get_folder_cache (
904 E_MAIL_SESSION (object)));
905 return;
906
907 case PROP_JUNK_FILTER_NAME:
908 g_value_set_string (
909 value,
910 mail_session_get_junk_filter_name (
911 E_MAIL_SESSION (object)));
912 return;
913
914 case PROP_LOCAL_STORE:
915 g_value_set_object (
916 value,
917 e_mail_session_get_local_store (
918 E_MAIL_SESSION (object)));
919 return;
920
921 case PROP_REGISTRY:
922 g_value_set_object (
923 value,
924 e_mail_session_get_registry (
925 E_MAIL_SESSION (object)));
926 return;
927
928 case PROP_VFOLDER_STORE:
929 g_value_set_object (
930 value,
931 e_mail_session_get_vfolder_store (
932 E_MAIL_SESSION (object)));
933 return;
934 }
935
936 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
937 }
938
939 static void
940 mail_session_dispose (GObject *object)
941 {
942 EMailSessionPrivate *priv;
943
944 priv = E_MAIL_SESSION_GET_PRIVATE (object);
945
946 if (priv->folder_cache != NULL) {
947 g_object_unref (priv->folder_cache);
948 priv->folder_cache = NULL;
949 }
950
951 if (priv->registry != NULL) {
952 g_signal_handler_disconnect (
953 priv->registry,
954 priv->source_added_handler_id);
955 g_signal_handler_disconnect (
956 priv->registry,
957 priv->source_removed_handler_id);
958 g_signal_handler_disconnect (
959 priv->registry,
960 priv->source_enabled_handler_id);
961 g_signal_handler_disconnect (
962 priv->registry,
963 priv->source_disabled_handler_id);
964 g_signal_handler_disconnect (
965 priv->registry,
966 priv->default_mail_account_handler_id);
967
968 /* This requires the registry. */
969 mail_session_cancel_refresh (E_MAIL_SESSION (object));
970
971 g_object_unref (priv->registry);
972 priv->registry = NULL;
973 }
974
975 if (priv->local_store != NULL) {
976 g_object_unref (priv->local_store);
977 priv->local_store = NULL;
978 }
979
980 if (priv->vfolder_store != NULL) {
981 g_object_unref (priv->vfolder_store);
982 priv->vfolder_store = NULL;
983 }
984
985 g_ptr_array_set_size (priv->local_folders, 0);
986 g_ptr_array_set_size (priv->local_folder_uris, 0);
987
988 if (priv->preparing_flush > 0) {
989 g_source_remove (priv->preparing_flush);
990 priv->preparing_flush = 0;
991 }
992
993 /* Chain up to parent's dispose() method. */
994 G_OBJECT_CLASS (e_mail_session_parent_class)->dispose (object);
995 }
996
997 static void
998 mail_session_finalize (GObject *object)
999 {
1000 EMailSessionPrivate *priv;
1001
1002 priv = E_MAIL_SESSION_GET_PRIVATE (object);
1003
1004 g_hash_table_destroy (priv->auto_refresh_table);
1005 g_hash_table_destroy (priv->junk_filters);
1006 g_object_unref (priv->proxy);
1007
1008 g_ptr_array_free (priv->local_folders, TRUE);
1009 g_ptr_array_free (priv->local_folder_uris, TRUE);
1010
1011 g_mutex_free (priv->preparing_flush_lock);
1012
1013 g_free (mail_data_dir);
1014 g_free (mail_config_dir);
1015
1016 /* Chain up to parent's finalize() method. */
1017 G_OBJECT_CLASS (e_mail_session_parent_class)->finalize (object);
1018 }
1019
1020 static void
1021 mail_session_notify (GObject *object,
1022 GParamSpec *pspec)
1023 {
1024 /* GObject does not implement this method; do not chain up. */
1025
1026 /* XXX Delete this once Evolution moves to GSettings and
1027 * we're able to get rid of PROP_JUNK_FILTER_NAME. */
1028 if (g_strcmp0 (pspec->name, "junk-filter") == 0)
1029 g_object_notify (object, "junk-filter-name");
1030 }
1031
1032 static void
1033 mail_session_constructed (GObject *object)
1034 {
1035 EMailSession *session;
1036 EExtensible *extensible;
1037 ESourceRegistry *registry;
1038 GType extension_type;
1039 GList *list, *link;
1040 GSettings *settings;
1041 CamelProviderType provider_type;
1042 const gchar *extension_name;
1043 gulong handler_id;
1044
1045 session = E_MAIL_SESSION (object);
1046 registry = e_mail_session_get_registry (session);
1047
1048 /* Chain up to parent's constructed() method. */
1049 G_OBJECT_CLASS (e_mail_session_parent_class)->constructed (object);
1050
1051 /* Add available mail accounts. */
1052
1053 provider_type = CAMEL_PROVIDER_STORE;
1054 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
1055
1056 list = e_source_registry_list_sources (registry, extension_name);
1057
1058 for (link = list; link != NULL; link = g_list_next (link)) {
1059 ESource *source = E_SOURCE (link->data);
1060
1061 mail_session_add_from_source (session, provider_type, source);
1062 }
1063
1064 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1065
1066 /* Add available mail transports. */
1067
1068 provider_type = CAMEL_PROVIDER_TRANSPORT;
1069 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
1070
1071 list = e_source_registry_list_sources (registry, extension_name);
1072
1073 for (link = list; link != NULL; link = g_list_next (link)) {
1074 ESource *source = E_SOURCE (link->data);
1075
1076 mail_session_add_from_source (session, provider_type, source);
1077 }
1078
1079 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1080
1081 /* Built-in stores require extra configuration. */
1082
1083 mail_session_configure_local_store (session);
1084 mail_session_configure_vfolder_store (session);
1085
1086 /* Listen for registry changes. */
1087
1088 handler_id = g_signal_connect (
1089 registry, "source-added",
1090 G_CALLBACK (mail_session_source_added_cb), session);
1091 session->priv->source_added_handler_id = handler_id;
1092
1093 handler_id = g_signal_connect (
1094 registry, "source-removed",
1095 G_CALLBACK (mail_session_source_removed_cb), session);
1096 session->priv->source_removed_handler_id = handler_id;
1097
1098 handler_id = g_signal_connect (
1099 registry, "source-enabled",
1100 G_CALLBACK (mail_session_source_enabled_cb), session);
1101 session->priv->source_enabled_handler_id = handler_id;
1102
1103 handler_id = g_signal_connect (
1104 registry, "source-disabled",
1105 G_CALLBACK (mail_session_source_disabled_cb), session);
1106 session->priv->source_disabled_handler_id = handler_id;
1107
1108 handler_id = g_signal_connect (
1109 registry, "notify::default-mail-account",
1110 G_CALLBACK (mail_session_default_mail_account_cb), session);
1111 session->priv->default_mail_account_handler_id = handler_id;
1112
1113 extensible = E_EXTENSIBLE (object);
1114 e_extensible_load_extensions (extensible);
1115
1116 /* Add junk filter extensions to an internal hash table. */
1117
1118 extension_type = E_TYPE_MAIL_JUNK_FILTER;
1119 list = e_extensible_list_extensions (extensible, extension_type);
1120
1121 for (link = list; link != NULL; link = g_list_next (link)) {
1122 EMailJunkFilter *junk_filter;
1123 EMailJunkFilterClass *class;
1124
1125 junk_filter = E_MAIL_JUNK_FILTER (link->data);
1126 class = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter);
1127
1128 if (!CAMEL_IS_JUNK_FILTER (junk_filter)) {
1129 g_warning (
1130 "Skipping %s: Does not implement "
1131 "CamelJunkFilterInterface",
1132 G_OBJECT_TYPE_NAME (junk_filter));
1133 continue;
1134 }
1135
1136 if (class->filter_name == NULL) {
1137 g_warning (
1138 "Skipping %s: filter_name unset",
1139 G_OBJECT_TYPE_NAME (junk_filter));
1140 continue;
1141 }
1142
1143 if (class->display_name == NULL) {
1144 g_warning (
1145 "Skipping %s: display_name unset",
1146 G_OBJECT_TYPE_NAME (junk_filter));
1147 continue;
1148 }
1149
1150 /* No need to reference the EMailJunkFilter since
1151 * EMailSession owns the reference to it already. */
1152 g_hash_table_insert (
1153 session->priv->junk_filters,
1154 (gpointer) class->filter_name,
1155 junk_filter);
1156 }
1157
1158 g_list_free (list);
1159
1160 settings = g_settings_new ("org.gnome.evolution.mail");
1161
1162 /* Bind the "junk-default-plugin" GSettings
1163 * key to our "junk-filter-name" property. */
1164
1165 g_settings_bind (
1166 settings, "junk-default-plugin",
1167 object, "junk-filter-name",
1168 G_SETTINGS_BIND_DEFAULT);
1169
1170 camel_session_set_check_junk (
1171 CAMEL_SESSION (session), g_settings_get_boolean (
1172 settings, "junk-check-incoming"));
1173 g_signal_connect (
1174 settings, "changed",
1175 G_CALLBACK (mail_session_check_junk_notify), session);
1176
1177 mail_config_reload_junk_headers (session);
1178
1179 e_proxy_setup_proxy (session->priv->proxy);
1180
1181 /* Initialize the legacy message-passing framework
1182 * before starting the first mail store refresh. */
1183 mail_msg_init ();
1184
1185 /* The application is not yet fully initialized at this point,
1186 * so run the first mail store refresh from an idle callback. */
1187 if (g_settings_get_boolean (settings, "send-recv-on-start"))
1188 g_idle_add_full (
1189 G_PRIORITY_DEFAULT,
1190 (GSourceFunc) mail_session_idle_refresh_cb,
1191 g_object_ref (session),
1192 (GDestroyNotify) g_object_unref);
1193
1194 g_object_unref (settings);
1195 }
1196
1197 static CamelService *
1198 mail_session_add_service (CamelSession *session,
1199 const gchar *uid,
1200 const gchar *protocol,
1201 CamelProviderType type,
1202 GError **error)
1203 {
1204 ESourceRegistry *registry;
1205 CamelService *service;
1206 const gchar *extension_name;
1207
1208 registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
1209 extension_name = e_source_camel_get_extension_name (protocol);
1210
1211 /* Chain up to parents add_service() method. */
1212 service = CAMEL_SESSION_CLASS (e_mail_session_parent_class)->
1213 add_service (session, uid, protocol, type, error);
1214
1215 /* Configure the CamelService from the corresponding ESource. */
1216
1217 if (CAMEL_IS_SERVICE (service)) {
1218 ESource *source;
1219 ESource *tmp_source;
1220
1221 /* Each CamelService has a corresponding ESource. */
1222 source = e_source_registry_ref_source (registry, uid);
1223 g_return_val_if_fail (source != NULL, service);
1224
1225 tmp_source = e_source_registry_find_extension (
1226 registry, source, extension_name);
1227 if (tmp_source != NULL) {
1228 g_object_unref (source);
1229 source = tmp_source;
1230 }
1231
1232 /* This handles all the messy property bindings. */
1233 e_source_camel_configure_service (source, service);
1234
1235 g_object_bind_property (
1236 source, "display-name",
1237 service, "display-name",
1238 G_BINDING_SYNC_CREATE);
1239
1240 /* Migrate files for this service from its old
1241 * URL-based directory to a UID-based directory
1242 * if necessary. */
1243 camel_service_migrate_files (service);
1244
1245 g_object_unref (source);
1246 }
1247
1248 return service;
1249 }
1250
1251 static gchar *
1252 mail_session_get_password (CamelSession *session,
1253 CamelService *service,
1254 const gchar *prompt,
1255 const gchar *item,
1256 guint32 flags,
1257 GError **error)
1258 {
1259 ESourceRegistry *registry;
1260 gchar *password = NULL;
1261
1262 /* XXX This method is now only for fringe cases. For normal
1263 * CamelService authentication, use authenticate_sync().
1264 *
1265 * The two known fringe cases that still need this are:
1266 *
1267 * 1) CamelSaslPOPB4SMTP, where the CamelService is an SMTP
1268 * transport and the item name is always "popb4smtp_uid".
1269 * (This is a dirty hack, Camel just needs some way to
1270 * pair up a CamelService and CamelTransport. Not sure
1271 * what that should look like just yet...)
1272 *
1273 * 2) CamelGpgContext, where the CamelService is NULL and
1274 * the item name is a user ID (I think). (Seahorse, or
1275 * one of its dependent libraries, ought to handle this
1276 * transparently once Camel fully transitions to GIO.)
1277 */
1278
1279 registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
1280
1281 /* Handle the CamelSaslPOPB4SMTP case. */
1282 if (g_strcmp0 (item, "popb4smtp_uid") == 0)
1283 return mail_session_resolve_popb4smtp (registry, service);
1284
1285 /* Otherwise this had better be the CamelGpgContext case. */
1286 g_return_val_if_fail (service == NULL, NULL);
1287
1288 password = e_passwords_get_password (NULL, item);
1289
1290 if (password == NULL || (flags & CAMEL_SESSION_PASSWORD_REPROMPT)) {
1291 gboolean remember;
1292 guint eflags = 0;
1293
1294 if (flags & CAMEL_SESSION_PASSWORD_STATIC)
1295 eflags |= E_PASSWORDS_REMEMBER_NEVER;
1296 else
1297 eflags |= E_PASSWORDS_REMEMBER_SESSION;
1298
1299 if (flags & CAMEL_SESSION_PASSWORD_REPROMPT)
1300 eflags |= E_PASSWORDS_REPROMPT;
1301
1302 if (flags & CAMEL_SESSION_PASSWORD_SECRET)
1303 eflags |= E_PASSWORDS_SECRET;
1304
1305 if (flags & CAMEL_SESSION_PASSPHRASE)
1306 eflags |= E_PASSWORDS_PASSPHRASE;
1307
1308 password = e_passwords_ask_password (
1309 "", NULL, item, prompt, eflags, &remember, NULL);
1310
1311 if (password == NULL)
1312 e_passwords_forget_password (NULL, item);
1313 }
1314
1315 if (password == NULL)
1316 g_set_error (
1317 error, G_IO_ERROR,
1318 G_IO_ERROR_CANCELLED,
1319 _("User cancelled operation"));
1320
1321 return password;
1322 }
1323
1324 static gboolean
1325 mail_session_forget_password (CamelSession *session,
1326 CamelService *service,
1327 const gchar *item,
1328 GError **error)
1329 {
1330 /* XXX The only remaining user of this method is CamelGpgContext,
1331 * which does not provide a CamelService. Use 'item' as the
1332 * password key. */
1333
1334 g_return_val_if_fail (service == NULL, FALSE);
1335
1336 e_passwords_forget_password (NULL, item);
1337
1338 return TRUE;
1339 }
1340
1341 static gint
1342 mail_session_alert_user (CamelSession *session,
1343 CamelSessionAlertType type,
1344 const gchar *prompt,
1345 GSList *button_captions)
1346 {
1347 struct _user_message_msg *m;
1348 GCancellable *cancellable;
1349 gint result = -1;
1350 GSList *iter;
1351
1352 m = mail_msg_new (&user_message_info);
1353 m->ismain = mail_in_main_thread ();
1354 m->type = type;
1355 m->prompt = g_strdup (prompt);
1356 m->done = e_flag_new ();
1357 m->button_captions = g_slist_copy (button_captions);
1358
1359 for (iter = m->button_captions; iter; iter = iter->next)
1360 iter->data = g_strdup (iter->data);
1361
1362 if (g_slist_length (button_captions) > 1)
1363 mail_msg_ref (m);
1364
1365 cancellable = m->base.cancellable;
1366
1367 if (m->ismain)
1368 user_message_exec (m, cancellable, &m->base.error);
1369 else
1370 mail_msg_main_loop_push (m);
1371
1372 if (g_slist_length (button_captions) > 1) {
1373 e_flag_wait (m->done);
1374 result = m->result;
1375 mail_msg_unref (m);
1376 } else if (m->ismain)
1377 mail_msg_unref (m);
1378
1379 return result;
1380 }
1381
1382 static CamelFilterDriver *
1383 mail_session_get_filter_driver (CamelSession *session,
1384 const gchar *type,
1385 GError **error)
1386 {
1387 return (CamelFilterDriver *) mail_call_main (
1388 MAIL_CALL_p_ppp, (MailMainFunc) main_get_filter_driver,
1389 session, type, error);
1390 }
1391
1392 static gboolean
1393 mail_session_lookup_addressbook (CamelSession *session,
1394 const gchar *name)
1395 {
1396 ESourceRegistry *registry;
1397 CamelInternetAddress *addr;
1398 gboolean ret;
1399
1400 if (!mail_config_get_lookup_book ())
1401 return FALSE;
1402
1403 registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
1404
1405 addr = camel_internet_address_new ();
1406 camel_address_decode ((CamelAddress *) addr, name);
1407 ret = em_utils_in_addressbook (
1408 registry, addr, mail_config_get_lookup_book_local_only (), NULL);
1409 g_object_unref (addr);
1410
1411 return ret;
1412 }
1413
1414 static void
1415 mail_session_get_socks_proxy (CamelSession *session,
1416 const gchar *for_host,
1417 gchar **host_ret,
1418 gint *port_ret)
1419 {
1420 EMailSession *mail_session;
1421 gchar *uri;
1422
1423 g_return_if_fail (session != NULL);
1424 g_return_if_fail (for_host != NULL);
1425 g_return_if_fail (host_ret != NULL);
1426 g_return_if_fail (port_ret != NULL);
1427
1428 mail_session = E_MAIL_SESSION (session);
1429 g_return_if_fail (mail_session != NULL);
1430 g_return_if_fail (mail_session->priv != NULL);
1431
1432 *host_ret = NULL;
1433 *port_ret = 0;
1434
1435 uri = g_strconcat ("socks://", for_host, NULL);
1436
1437 if (e_proxy_require_proxy_for_uri (mail_session->priv->proxy, uri)) {
1438 SoupURI *suri;
1439
1440 suri = e_proxy_peek_uri_for (mail_session->priv->proxy, uri);
1441 if (suri) {
1442 *host_ret = g_strdup (suri->host);
1443 *port_ret = suri->port;
1444 }
1445 }
1446
1447 g_free (uri);
1448 }
1449
1450 static gboolean
1451 mail_session_authenticate_sync (CamelSession *session,
1452 CamelService *service,
1453 const gchar *mechanism,
1454 GCancellable *cancellable,
1455 GError **error)
1456 {
1457 ESource *source;
1458 ESourceRegistry *registry;
1459 ESourceAuthenticator *auth;
1460 CamelServiceAuthType *authtype = NULL;
1461 CamelAuthenticationResult result;
1462 const gchar *uid;
1463 gboolean authenticated;
1464 GError *local_error = NULL;
1465
1466 /* Do not chain up. Camel's default method is only an example for
1467 * subclasses to follow. Instead we mimic most of its logic here. */
1468
1469 registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
1470
1471 /* Treat a mechanism name of "none" as NULL. */
1472 if (g_strcmp0 (mechanism, "none") == 0)
1473 mechanism = NULL;
1474
1475 /* APOP is one case where a non-SASL mechanism name is passed, so
1476 * don't bail if the CamelServiceAuthType struct comes back NULL. */
1477 if (mechanism != NULL)
1478 authtype = camel_sasl_authtype (mechanism);
1479
1480 /* If the SASL mechanism does not involve a user
1481 * password, then it gets one shot to authenticate. */
1482 if (authtype != NULL && !authtype->need_password) {
1483 result = camel_service_authenticate_sync (
1484 service, mechanism, cancellable, error);
1485 if (result == CAMEL_AUTHENTICATION_REJECTED)
1486 g_set_error (
1487 error, CAMEL_SERVICE_ERROR,
1488 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
1489 _("%s authentication failed"), mechanism);
1490 return (result == CAMEL_AUTHENTICATION_ACCEPTED);
1491 }
1492
1493 /* Some SASL mechanisms can attempt to authenticate without a
1494 * user password being provided (e.g. single-sign-on credentials),
1495 * but can fall back to a user password. Handle that case next. */
1496 if (mechanism != NULL) {
1497 CamelProvider *provider;
1498 CamelSasl *sasl;
1499 const gchar *service_name;
1500 gboolean success = FALSE;
1501
1502 provider = camel_service_get_provider (service);
1503 service_name = provider->protocol;
1504
1505 /* XXX Would be nice if camel_sasl_try_empty_password_sync()
1506 * returned CamelAuthenticationResult so it's easier to
1507 * detect errors. */
1508 sasl = camel_sasl_new (service_name, mechanism, service);
1509 if (sasl != NULL) {
1510 success = camel_sasl_try_empty_password_sync (
1511 sasl, cancellable, &local_error);
1512 g_object_unref (sasl);
1513 }
1514
1515 if (success)
1516 return TRUE;
1517 }
1518
1519 /* Abort authentication if we got cancelled.
1520 * Otherwise clear any errors and press on. */
1521 if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1522 return FALSE;
1523
1524 g_clear_error (&local_error);
1525
1526 /* Find a matching ESource for this CamelService. */
1527 uid = camel_service_get_uid (service);
1528 source = e_source_registry_ref_source (registry, uid);
1529
1530 if (source == NULL) {
1531 g_set_error (
1532 error, CAMEL_SERVICE_ERROR,
1533 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
1534 _("No data source found for UID '%s'"), uid);
1535 return FALSE;
1536 }
1537
1538 auth = e_mail_authenticator_new (service, mechanism);
1539
1540 authenticated = e_source_registry_authenticate_sync (
1541 registry, source, auth, cancellable, error);
1542
1543 g_object_unref (auth);
1544
1545 g_object_unref (source);
1546
1547 return authenticated;
1548 }
1549
1550 static gboolean
1551 mail_session_forward_to_sync (CamelSession *session,
1552 CamelFolder *folder,
1553 CamelMimeMessage *message,
1554 const gchar *address,
1555 GCancellable *cancellable,
1556 GError **error)
1557 {
1558 EMailSessionPrivate *priv;
1559 ESource *source;
1560 ESourceRegistry *registry;
1561 ESourceMailIdentity *extension;
1562 CamelMimeMessage *forward;
1563 CamelStream *mem;
1564 CamelInternetAddress *addr;
1565 CamelFolder *out_folder;
1566 CamelMessageInfo *info;
1567 CamelMedium *medium;
1568 const gchar *extension_name;
1569 const gchar *from_address;
1570 const gchar *from_name;
1571 const gchar *header_name;
1572 struct _camel_header_raw *xev;
1573 gboolean success;
1574 gchar *subject;
1575
1576 g_return_val_if_fail (folder != NULL, FALSE);
1577 g_return_val_if_fail (message != NULL, FALSE);
1578 g_return_val_if_fail (address != NULL, FALSE);
1579
1580 priv = E_MAIL_SESSION_GET_PRIVATE (session);
1581
1582 if (!*address) {
1583 g_set_error (
1584 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1585 _("No destination address provided, forwarding "
1586 "of the message has been cancelled."));
1587 return FALSE;
1588 }
1589
1590 registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
1591
1592 /* This returns a new ESource reference. */
1593 source = em_utils_guess_mail_identity_with_recipients (
1594 registry, message, folder, NULL);
1595 if (source == NULL) {
1596 g_set_error (
1597 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1598 _("No identity found to use, forwarding "
1599 "of the message has been cancelled."));
1600 return FALSE;
1601 }
1602
1603 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
1604 extension = e_source_get_extension (source, extension_name);
1605 from_address = e_source_mail_identity_get_address (extension);
1606 from_name = e_source_mail_identity_get_name (extension);
1607
1608 forward = camel_mime_message_new ();
1609
1610 /* make copy of the message, because we are going to modify it */
1611 mem = camel_stream_mem_new ();
1612 camel_data_wrapper_write_to_stream_sync (
1613 CAMEL_DATA_WRAPPER (message), mem, NULL, NULL);
1614 g_seekable_seek (G_SEEKABLE (mem), 0, G_SEEK_SET, NULL, NULL);
1615 camel_data_wrapper_construct_from_stream_sync (
1616 CAMEL_DATA_WRAPPER (forward), mem, NULL, NULL);
1617 g_object_unref (mem);
1618
1619 /* clear previous recipients */
1620 camel_mime_message_set_recipients (
1621 forward, CAMEL_RECIPIENT_TYPE_TO, NULL);
1622 camel_mime_message_set_recipients (
1623 forward, CAMEL_RECIPIENT_TYPE_CC, NULL);
1624 camel_mime_message_set_recipients (
1625 forward, CAMEL_RECIPIENT_TYPE_BCC, NULL);
1626 camel_mime_message_set_recipients (
1627 forward, CAMEL_RECIPIENT_TYPE_RESENT_TO, NULL);
1628 camel_mime_message_set_recipients (
1629 forward, CAMEL_RECIPIENT_TYPE_RESENT_CC, NULL);
1630 camel_mime_message_set_recipients (
1631 forward, CAMEL_RECIPIENT_TYPE_RESENT_BCC, NULL);
1632
1633 medium = CAMEL_MEDIUM (forward);
1634
1635 /* remove all delivery and notification headers */
1636 header_name = "Disposition-Notification-To";
1637 while (camel_medium_get_header (medium, header_name))
1638 camel_medium_remove_header (medium, header_name);
1639
1640 header_name = "Delivered-To";
1641 while (camel_medium_get_header (medium, header_name))
1642 camel_medium_remove_header (medium, header_name);
1643
1644 /* remove any X-Evolution-* headers that may have been set */
1645 xev = mail_tool_remove_xevolution_headers (forward);
1646 camel_header_raw_clear (&xev);
1647
1648 /* from */
1649 addr = camel_internet_address_new ();
1650 camel_internet_address_add (addr, from_name, from_address);
1651 camel_mime_message_set_from (forward, addr);
1652 g_object_unref (addr);
1653
1654 /* to */
1655 addr = camel_internet_address_new ();
1656 camel_address_decode (CAMEL_ADDRESS (addr), address);
1657 camel_mime_message_set_recipients (
1658 forward, CAMEL_RECIPIENT_TYPE_TO, addr);
1659 g_object_unref (addr);
1660
1661 /* subject */
1662 subject = mail_tool_generate_forward_subject (message);
1663 camel_mime_message_set_subject (forward, subject);
1664 g_free (subject);
1665
1666 /* and send it */
1667 info = camel_message_info_new (NULL);
1668 out_folder = e_mail_session_get_local_folder (
1669 E_MAIL_SESSION (session), E_MAIL_LOCAL_FOLDER_OUTBOX);
1670 camel_message_info_set_flags (
1671 info, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
1672
1673 success = e_mail_folder_append_message_sync (
1674 out_folder, forward, info, NULL, cancellable, error);
1675
1676 if (success) {
1677 GSettings *settings;
1678 gboolean flush_outbox;
1679
1680 settings = g_settings_new ("org.gnome.evolution.mail");
1681 flush_outbox = g_settings_get_boolean (settings, "flush-outbox");
1682 g_object_unref (settings);
1683
1684 g_mutex_lock (priv->preparing_flush_lock);
1685
1686 if (priv->preparing_flush > 0) {
1687 g_source_remove (priv->preparing_flush);
1688 flush_outbox = TRUE;
1689 }
1690
1691 if (flush_outbox) {
1692 GMainContext *main_context;
1693 GSource *timeout_source;
1694
1695 main_context =
1696 camel_session_get_main_context (session);
1697
1698 timeout_source =
1699 g_timeout_source_new_seconds (60);
1700 g_source_set_callback (
1701 timeout_source,
1702 session_forward_to_flush_outbox_cb,
1703 session, (GDestroyNotify) NULL);
1704 priv->preparing_flush = g_source_attach (
1705 timeout_source, main_context);
1706 g_source_unref (timeout_source);
1707 }
1708
1709 g_mutex_unlock (priv->preparing_flush_lock);
1710 }
1711
1712 camel_message_info_free (info);
1713
1714 g_object_unref (source);
1715
1716 return success;
1717 }
1718
1719 static EMVFolderContext *
1720 mail_session_create_vfolder_context (EMailSession *session)
1721 {
1722 return em_vfolder_context_new ();
1723 }
1724
1725 static void
1726 e_mail_session_class_init (EMailSessionClass *class)
1727 {
1728 GObjectClass *object_class;
1729 CamelSessionClass *session_class;
1730
1731 g_type_class_add_private (class, sizeof (EMailSessionPrivate));
1732
1733 object_class = G_OBJECT_CLASS (class);
1734 object_class->set_property = mail_session_set_property;
1735 object_class->get_property = mail_session_get_property;
1736 object_class->dispose = mail_session_dispose;
1737 object_class->finalize = mail_session_finalize;
1738 object_class->notify = mail_session_notify;
1739 object_class->constructed = mail_session_constructed;
1740
1741 session_class = CAMEL_SESSION_CLASS (class);
1742 session_class->add_service = mail_session_add_service;
1743 session_class->get_password = mail_session_get_password;
1744 session_class->forget_password = mail_session_forget_password;
1745 session_class->alert_user = mail_session_alert_user;
1746 session_class->get_filter_driver = mail_session_get_filter_driver;
1747 session_class->lookup_addressbook = mail_session_lookup_addressbook;
1748 session_class->get_socks_proxy = mail_session_get_socks_proxy;
1749 session_class->authenticate_sync = mail_session_authenticate_sync;
1750 session_class->forward_to_sync = mail_session_forward_to_sync;
1751
1752 class->create_vfolder_context = mail_session_create_vfolder_context;
1753
1754 g_object_class_install_property (
1755 object_class,
1756 PROP_FOLDER_CACHE,
1757 g_param_spec_object (
1758 "folder-cache",
1759 NULL,
1760 NULL,
1761 MAIL_TYPE_FOLDER_CACHE,
1762 G_PARAM_READABLE |
1763 G_PARAM_STATIC_STRINGS));
1764
1765 /* XXX This property can be removed once Evolution moves to
1766 * GSettings and can use transform functions when binding
1767 * properties to settings. */
1768 g_object_class_install_property (
1769 object_class,
1770 PROP_JUNK_FILTER_NAME,
1771 g_param_spec_string (
1772 "junk-filter-name",
1773 NULL,
1774 NULL,
1775 NULL,
1776 G_PARAM_READWRITE |
1777 G_PARAM_STATIC_STRINGS));
1778
1779 g_object_class_install_property (
1780 object_class,
1781 PROP_LOCAL_STORE,
1782 g_param_spec_object (
1783 "local-store",
1784 "Local Store",
1785 "Built-in local store",
1786 CAMEL_TYPE_STORE,
1787 G_PARAM_READABLE |
1788 G_PARAM_STATIC_STRINGS));
1789
1790 g_object_class_install_property (
1791 object_class,
1792 PROP_REGISTRY,
1793 g_param_spec_object (
1794 "registry",
1795 "Registry",
1796 "Data source registry",
1797 E_TYPE_SOURCE_REGISTRY,
1798 G_PARAM_READWRITE |
1799 G_PARAM_CONSTRUCT_ONLY |
1800 G_PARAM_STATIC_STRINGS));
1801
1802 g_object_class_install_property (
1803 object_class,
1804 PROP_VFOLDER_STORE,
1805 g_param_spec_object (
1806 "vfolder-store",
1807 "Search Folder Store",
1808 "Built-in search folder store",
1809 CAMEL_TYPE_STORE,
1810 G_PARAM_READABLE |
1811 G_PARAM_STATIC_STRINGS));
1812
1813 /**
1814 * EMailSession::flush-outbox
1815 * @session: the email session
1816 *
1817 * Emitted if the send folder should be flushed.
1818 **/
1819 signals[FLUSH_OUTBOX] = g_signal_new (
1820 "flush-outbox",
1821 G_OBJECT_CLASS_TYPE (object_class),
1822 G_SIGNAL_RUN_FIRST,
1823 G_STRUCT_OFFSET (EMailSessionClass, flush_outbox),
1824 NULL, NULL,
1825 g_cclosure_marshal_VOID__VOID,
1826 G_TYPE_NONE, 0);
1827
1828 /**
1829 * EMailSession::refresh-service
1830 * @session: the #EMailSession that emitted the signal
1831 * @service: a #CamelService
1832 *
1833 * Emitted when @service should be refreshed.
1834 **/
1835 signals[REFRESH_SERVICE] = g_signal_new (
1836 "refresh-service",
1837 G_OBJECT_CLASS_TYPE (object_class),
1838 G_SIGNAL_RUN_LAST,
1839 G_STRUCT_OFFSET (EMailSessionClass, refresh_service),
1840 NULL, NULL,
1841 g_cclosure_marshal_VOID__OBJECT,
1842 G_TYPE_NONE, 1,
1843 CAMEL_TYPE_SERVICE);
1844
1845 /**
1846 * EMailSession::store-added
1847 * @session: the #EMailSession that emitted the signal
1848 * @store: a #CamelStore
1849 *
1850 * Emitted when a store is added
1851 **/
1852 signals[STORE_ADDED] = g_signal_new (
1853 "store-added",
1854 G_OBJECT_CLASS_TYPE (object_class),
1855 G_SIGNAL_RUN_FIRST,
1856 G_STRUCT_OFFSET (EMailSessionClass, store_added),
1857 NULL, NULL,
1858 g_cclosure_marshal_VOID__OBJECT,
1859 G_TYPE_NONE, 1,
1860 CAMEL_TYPE_STORE);
1861
1862 /**
1863 * EMailSession::store-removed
1864 * @session: the #EMailSession that emitted the signal
1865 * @store: a #CamelStore
1866 *
1867 * Emitted when a store is removed
1868 **/
1869 signals[STORE_REMOVED] = g_signal_new (
1870 "store-removed",
1871 G_OBJECT_CLASS_TYPE (object_class),
1872 G_SIGNAL_RUN_FIRST,
1873 G_STRUCT_OFFSET (EMailSessionClass, store_removed),
1874 NULL, NULL,
1875 g_cclosure_marshal_VOID__OBJECT,
1876 G_TYPE_NONE, 1,
1877 CAMEL_TYPE_STORE);
1878
1879 camel_null_store_register_provider ();
1880
1881 /* Make sure ESourceCamel picks up the "none" provider. */
1882 e_source_camel_generate_subtype ("none", CAMEL_TYPE_SETTINGS);
1883 }
1884
1885 static void
1886 e_mail_session_init (EMailSession *session)
1887 {
1888 GHashTable *auto_refresh_table;
1889 GHashTable *junk_filters;
1890
1891 auto_refresh_table = g_hash_table_new_full (
1892 (GHashFunc) g_str_hash,
1893 (GEqualFunc) g_str_equal,
1894 (GDestroyNotify) g_free,
1895 (GDestroyNotify) NULL);
1896
1897 junk_filters = g_hash_table_new (
1898 (GHashFunc) g_str_hash,
1899 (GEqualFunc) g_str_equal);
1900
1901 session->priv = E_MAIL_SESSION_GET_PRIVATE (session);
1902 session->priv->folder_cache = mail_folder_cache_new (session);
1903 session->priv->auto_refresh_table = auto_refresh_table;
1904 session->priv->junk_filters = junk_filters;
1905 session->priv->proxy = e_proxy_new ();
1906
1907 session->priv->local_folders =
1908 g_ptr_array_new_with_free_func (
1909 (GDestroyNotify) g_object_unref);
1910 session->priv->local_folder_uris =
1911 g_ptr_array_new_with_free_func (
1912 (GDestroyNotify) g_free);
1913
1914 session->priv->preparing_flush_lock = g_mutex_new ();
1915 }
1916
1917 EMailSession *
1918 e_mail_session_new (ESourceRegistry *registry)
1919 {
1920 const gchar *user_data_dir;
1921 const gchar *user_cache_dir;
1922
1923 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
1924
1925 user_data_dir = mail_session_get_data_dir ();
1926 user_cache_dir = mail_session_get_cache_dir ();
1927
1928 return g_object_new (
1929 E_TYPE_MAIL_SESSION,
1930 "user-data-dir", user_data_dir,
1931 "user-cache-dir", user_cache_dir,
1932 "registry", registry,
1933 NULL);
1934 }
1935
1936 ESourceRegistry *
1937 e_mail_session_get_registry (EMailSession *session)
1938 {
1939 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1940
1941 return session->priv->registry;
1942 }
1943
1944 MailFolderCache *
1945 e_mail_session_get_folder_cache (EMailSession *session)
1946 {
1947 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1948
1949 return session->priv->folder_cache;
1950 }
1951
1952 CamelStore *
1953 e_mail_session_get_local_store (EMailSession *session)
1954 {
1955 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1956
1957 return CAMEL_STORE (session->priv->local_store);
1958 }
1959
1960 CamelFolder *
1961 e_mail_session_get_local_folder (EMailSession *session,
1962 EMailLocalFolder type)
1963 {
1964 GPtrArray *local_folders;
1965 CamelFolder *folder;
1966
1967 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1968
1969 local_folders = session->priv->local_folders;
1970 g_return_val_if_fail (type < local_folders->len, NULL);
1971
1972 folder = g_ptr_array_index (local_folders, type);
1973 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1974
1975 return folder;
1976 }
1977
1978 const gchar *
1979 e_mail_session_get_local_folder_uri (EMailSession *session,
1980 EMailLocalFolder type)
1981 {
1982 GPtrArray *local_folder_uris;
1983 const gchar *folder_uri;
1984
1985 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1986
1987 local_folder_uris = session->priv->local_folder_uris;
1988 g_return_val_if_fail (type < local_folder_uris->len, NULL);
1989
1990 folder_uri = g_ptr_array_index (local_folder_uris, type);
1991 g_return_val_if_fail (folder_uri != NULL, NULL);
1992
1993 return folder_uri;
1994 }
1995
1996 GList *
1997 e_mail_session_get_available_junk_filters (EMailSession *session)
1998 {
1999 GList *list, *link;
2000 GQueue trash = G_QUEUE_INIT;
2001
2002 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2003
2004 list = g_hash_table_get_values (session->priv->junk_filters);
2005
2006 /* Discard unavailable junk filters. (e.g. Junk filter
2007 * requires Bogofilter but Bogofilter is not installed,
2008 * hence the junk filter is unavailable.) */
2009
2010 for (link = list; link != NULL; link = g_list_next (link)) {
2011 EMailJunkFilter *junk_filter;
2012
2013 junk_filter = E_MAIL_JUNK_FILTER (link->data);
2014 if (!e_mail_junk_filter_available (junk_filter))
2015 g_queue_push_tail (&trash, link);
2016 }
2017
2018 while ((link = g_queue_pop_head (&trash)) != NULL)
2019 list = g_list_delete_link (list, link);
2020
2021 /* Sort the remaining junk filters by display name. */
2022
2023 return g_list_sort (list, (GCompareFunc) e_mail_junk_filter_compare);
2024 }
2025
2026 static void
2027 mail_session_get_inbox_thread (GSimpleAsyncResult *simple,
2028 EMailSession *session,
2029 GCancellable *cancellable)
2030 {
2031 AsyncContext *context;
2032 GError *error = NULL;
2033
2034 context = g_simple_async_result_get_op_res_gpointer (simple);
2035
2036 context->folder = e_mail_session_get_inbox_sync (
2037 session, context->uid, cancellable, &error);
2038
2039 if (error != NULL)
2040 g_simple_async_result_take_error (simple, error);
2041 }
2042
2043 CamelFolder *
2044 e_mail_session_get_inbox_sync (EMailSession *session,
2045 const gchar *service_uid,
2046 GCancellable *cancellable,
2047 GError **error)
2048 {
2049 CamelService *service;
2050 CamelFolder *folder = NULL;
2051
2052 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2053 g_return_val_if_fail (service_uid != NULL, NULL);
2054
2055 service = camel_session_ref_service (
2056 CAMEL_SESSION (session), service_uid);
2057
2058 if (service == NULL)
2059 return NULL;
2060
2061 if (!CAMEL_IS_STORE (service))
2062 goto exit;
2063
2064 if (!camel_service_connect_sync (service, cancellable, error))
2065 goto exit;
2066
2067 folder = camel_store_get_inbox_folder_sync (
2068 CAMEL_STORE (service), cancellable, error);
2069
2070 exit:
2071 g_object_unref (service);
2072
2073 return folder;
2074 }
2075
2076 void
2077 e_mail_session_get_inbox (EMailSession *session,
2078 const gchar *service_uid,
2079 gint io_priority,
2080 GCancellable *cancellable,
2081 GAsyncReadyCallback callback,
2082 gpointer user_data)
2083 {
2084 GSimpleAsyncResult *simple;
2085 AsyncContext *context;
2086
2087 g_return_if_fail (E_IS_MAIL_SESSION (session));
2088 g_return_if_fail (service_uid != NULL);
2089
2090 context = g_slice_new0 (AsyncContext);
2091 context->uid = g_strdup (service_uid);
2092
2093 simple = g_simple_async_result_new (
2094 G_OBJECT (session), callback,
2095 user_data, e_mail_session_get_inbox);
2096
2097 g_simple_async_result_set_check_cancellable (simple, cancellable);
2098
2099 g_simple_async_result_set_op_res_gpointer (
2100 simple, context, (GDestroyNotify) async_context_free);
2101
2102 g_simple_async_result_run_in_thread (
2103 simple, (GSimpleAsyncThreadFunc)
2104 mail_session_get_inbox_thread,
2105 io_priority, cancellable);
2106
2107 g_object_unref (simple);
2108 }
2109
2110 CamelFolder *
2111 e_mail_session_get_inbox_finish (EMailSession *session,
2112 GAsyncResult *result,
2113 GError **error)
2114 {
2115 GSimpleAsyncResult *simple;
2116 AsyncContext *context;
2117
2118 g_return_val_if_fail (
2119 g_simple_async_result_is_valid (
2120 result, G_OBJECT (session),
2121 e_mail_session_get_inbox), NULL);
2122
2123 simple = G_SIMPLE_ASYNC_RESULT (result);
2124 context = g_simple_async_result_get_op_res_gpointer (simple);
2125
2126 if (g_simple_async_result_propagate_error (simple, error))
2127 return NULL;
2128
2129 g_return_val_if_fail (CAMEL_IS_FOLDER (context->folder), NULL);
2130
2131 return g_object_ref (context->folder);
2132 }
2133
2134 static void
2135 mail_session_get_trash_thread (GSimpleAsyncResult *simple,
2136 EMailSession *session,
2137 GCancellable *cancellable)
2138 {
2139 AsyncContext *context;
2140 GError *error = NULL;
2141
2142 context = g_simple_async_result_get_op_res_gpointer (simple);
2143
2144 context->folder = e_mail_session_get_trash_sync (
2145 session, context->uid, cancellable, &error);
2146
2147 if (error != NULL)
2148 g_simple_async_result_take_error (simple, error);
2149 }
2150
2151 CamelFolder *
2152 e_mail_session_get_trash_sync (EMailSession *session,
2153 const gchar *service_uid,
2154 GCancellable *cancellable,
2155 GError **error)
2156 {
2157 CamelService *service;
2158 CamelFolder *folder = NULL;
2159
2160 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2161 g_return_val_if_fail (service_uid != NULL, NULL);
2162
2163 service = camel_session_ref_service (
2164 CAMEL_SESSION (session), service_uid);
2165
2166 if (service == NULL)
2167 return NULL;
2168
2169 if (!CAMEL_IS_STORE (service))
2170 goto exit;
2171
2172 if (!camel_service_connect_sync (service, cancellable, error))
2173 goto exit;
2174
2175 folder = camel_store_get_trash_folder_sync (
2176 CAMEL_STORE (service), cancellable, error);
2177
2178 exit:
2179 g_object_unref (service);
2180
2181 return folder;
2182 }
2183
2184 void
2185 e_mail_session_get_trash (EMailSession *session,
2186 const gchar *service_uid,
2187 gint io_priority,
2188 GCancellable *cancellable,
2189 GAsyncReadyCallback callback,
2190 gpointer user_data)
2191 {
2192 GSimpleAsyncResult *simple;
2193 AsyncContext *context;
2194
2195 g_return_if_fail (E_IS_MAIL_SESSION (session));
2196 g_return_if_fail (service_uid != NULL);
2197
2198 context = g_slice_new0 (AsyncContext);
2199 context->uid = g_strdup (service_uid);
2200
2201 simple = g_simple_async_result_new (
2202 G_OBJECT (session), callback,
2203 user_data, e_mail_session_get_trash);
2204
2205 g_simple_async_result_set_check_cancellable (simple, cancellable);
2206
2207 g_simple_async_result_set_op_res_gpointer (
2208 simple, context, (GDestroyNotify) async_context_free);
2209
2210 g_simple_async_result_run_in_thread (
2211 simple, (GSimpleAsyncThreadFunc)
2212 mail_session_get_trash_thread,
2213 io_priority, cancellable);
2214
2215 g_object_unref (simple);
2216 }
2217
2218 CamelFolder *
2219 e_mail_session_get_trash_finish (EMailSession *session,
2220 GAsyncResult *result,
2221 GError **error)
2222 {
2223 GSimpleAsyncResult *simple;
2224 AsyncContext *context;
2225
2226 g_return_val_if_fail (
2227 g_simple_async_result_is_valid (
2228 result, G_OBJECT (session),
2229 e_mail_session_get_trash), NULL);
2230
2231 simple = G_SIMPLE_ASYNC_RESULT (result);
2232 context = g_simple_async_result_get_op_res_gpointer (simple);
2233
2234 if (g_simple_async_result_propagate_error (simple, error))
2235 return NULL;
2236
2237 g_return_val_if_fail (CAMEL_IS_FOLDER (context->folder), NULL);
2238
2239 return g_object_ref (context->folder);
2240 }
2241
2242 static void
2243 mail_session_uri_to_folder_thread (GSimpleAsyncResult *simple,
2244 EMailSession *session,
2245 GCancellable *cancellable)
2246 {
2247 AsyncContext *context;
2248 GError *error = NULL;
2249
2250 context = g_simple_async_result_get_op_res_gpointer (simple);
2251
2252 context->folder = e_mail_session_uri_to_folder_sync (
2253 session, context->uri, context->flags,
2254 cancellable, &error);
2255
2256 if (error != NULL)
2257 g_simple_async_result_take_error (simple, error);
2258 }
2259
2260 CamelFolder *
2261 e_mail_session_uri_to_folder_sync (EMailSession *session,
2262 const gchar *folder_uri,
2263 CamelStoreGetFolderFlags flags,
2264 GCancellable *cancellable,
2265 GError **error)
2266 {
2267 CamelStore *store;
2268 CamelFolder *folder;
2269 gchar *folder_name;
2270 gboolean success;
2271
2272 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2273 g_return_val_if_fail (folder_uri != NULL, NULL);
2274
2275 success = e_mail_folder_uri_parse (
2276 CAMEL_SESSION (session), folder_uri,
2277 &store, &folder_name, error);
2278
2279 if (!success)
2280 return NULL;
2281
2282 folder = camel_store_get_folder_sync (
2283 store, folder_name, flags, cancellable, error);
2284
2285 if (folder != NULL) {
2286 MailFolderCache *folder_cache;
2287 folder_cache = e_mail_session_get_folder_cache (session);
2288 mail_folder_cache_note_folder (folder_cache, folder);
2289 }
2290
2291 g_free (folder_name);
2292 g_object_unref (store);
2293
2294 return folder;
2295 }
2296
2297 void
2298 e_mail_session_uri_to_folder (EMailSession *session,
2299 const gchar *folder_uri,
2300 CamelStoreGetFolderFlags flags,
2301 gint io_priority,
2302 GCancellable *cancellable,
2303 GAsyncReadyCallback callback,
2304 gpointer user_data)
2305 {
2306 GSimpleAsyncResult *simple;
2307 AsyncContext *context;
2308
2309 g_return_if_fail (E_IS_MAIL_SESSION (session));
2310 g_return_if_fail (folder_uri != NULL);
2311
2312 context = g_slice_new0 (AsyncContext);
2313 context->uri = g_strdup (folder_uri);
2314 context->flags = flags;
2315
2316 simple = g_simple_async_result_new (
2317 G_OBJECT (session), callback,
2318 user_data, e_mail_session_uri_to_folder);
2319
2320 g_simple_async_result_set_check_cancellable (simple, cancellable);
2321
2322 g_simple_async_result_set_op_res_gpointer (
2323 simple, context, (GDestroyNotify) async_context_free);
2324
2325 g_simple_async_result_run_in_thread (
2326 simple, (GSimpleAsyncThreadFunc)
2327 mail_session_uri_to_folder_thread,
2328 io_priority, cancellable);
2329
2330 g_object_unref (simple);
2331 }
2332
2333 CamelFolder *
2334 e_mail_session_uri_to_folder_finish (EMailSession *session,
2335 GAsyncResult *result,
2336 GError **error)
2337 {
2338 GSimpleAsyncResult *simple;
2339 AsyncContext *context;
2340
2341 g_return_val_if_fail (
2342 g_simple_async_result_is_valid (
2343 result, G_OBJECT (session),
2344 e_mail_session_uri_to_folder), NULL);
2345
2346 simple = G_SIMPLE_ASYNC_RESULT (result);
2347 context = g_simple_async_result_get_op_res_gpointer (simple);
2348
2349 if (g_simple_async_result_propagate_error (simple, error))
2350 return NULL;
2351
2352 g_return_val_if_fail (CAMEL_IS_FOLDER (context->folder), NULL);
2353
2354 return g_object_ref (context->folder);
2355 }
2356
2357 gboolean
2358 e_binding_transform_service_to_source (GBinding *binding,
2359 const GValue *source_value,
2360 GValue *target_value,
2361 gpointer session)
2362 {
2363 CamelService *service;
2364 ESourceRegistry *registry;
2365 ESource *source;
2366 const gchar *uid;
2367 gboolean success = FALSE;
2368
2369 g_return_val_if_fail (G_IS_BINDING (binding), FALSE);
2370 g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
2371
2372 service = g_value_get_object (source_value);
2373
2374 if (!CAMEL_IS_SERVICE (service))
2375 return FALSE;
2376
2377 uid = camel_service_get_uid (service);
2378 registry = e_mail_session_get_registry (session);
2379 source = e_source_registry_ref_source (registry, uid);
2380
2381 if (source != NULL) {
2382 g_value_take_object (target_value, source);
2383 success = TRUE;
2384 }
2385
2386 return success;
2387 }
2388
2389 gboolean
2390 e_binding_transform_source_to_service (GBinding *binding,
2391 const GValue *source_value,
2392 GValue *target_value,
2393 gpointer session)
2394 {
2395 CamelService *service;
2396 ESource *source;
2397 const gchar *uid;
2398
2399 g_return_val_if_fail (G_IS_BINDING (binding), FALSE);
2400 g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
2401
2402 source = g_value_get_object (source_value);
2403
2404 if (!E_IS_SOURCE (source))
2405 return FALSE;
2406
2407 uid = e_source_get_uid (source);
2408 service = camel_session_ref_service (session, uid);
2409
2410 if (service == NULL)
2411 return FALSE;
2412
2413 g_value_take_object (target_value, service);
2414
2415 return TRUE;
2416 }
2417
2418 /******************************** Legacy API *********************************/
2419
2420 void
2421 mail_session_flush_filter_log (EMailSession *session)
2422 {
2423 g_return_if_fail (E_IS_MAIL_SESSION (session));
2424
2425 if (session->priv->filter_logfile)
2426 fflush (session->priv->filter_logfile);
2427 }
2428
2429 const gchar *
2430 mail_session_get_data_dir (void)
2431 {
2432 if (G_UNLIKELY (mail_data_dir == NULL))
2433 mail_data_dir = g_build_filename (
2434 e_get_user_data_dir (), "mail", NULL);
2435
2436 return mail_data_dir;
2437 }
2438
2439 const gchar *
2440 mail_session_get_cache_dir (void)
2441 {
2442 if (G_UNLIKELY (mail_cache_dir == NULL))
2443 mail_cache_dir = g_build_filename (
2444 e_get_user_cache_dir (), "mail", NULL);
2445
2446 return mail_cache_dir;
2447 }
2448
2449 const gchar *
2450 mail_session_get_config_dir (void)
2451 {
2452 if (G_UNLIKELY (mail_config_dir == NULL))
2453 mail_config_dir = g_build_filename (
2454 e_get_user_config_dir (), "mail", NULL);
2455
2456 return mail_config_dir;
2457 }
2458
2459 CamelStore *
2460 e_mail_session_get_vfolder_store (EMailSession *session)
2461 {
2462 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2463
2464 return CAMEL_STORE (session->priv->vfolder_store);
2465 }
2466
2467 EMVFolderContext *
2468 e_mail_session_create_vfolder_context (EMailSession *session)
2469 {
2470 EMailSessionClass *class;
2471
2472 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2473
2474 class = E_MAIL_SESSION_GET_CLASS (session);
2475 g_return_val_if_fail (class->create_vfolder_context != NULL, NULL);
2476
2477 return class->create_vfolder_context (session);
2478 }