1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-imap-store.c : class for a imap store */
4 * Authors: Michael Zucchi <notzed@ximian.com>
6 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
8 * This library is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation.
12 * This library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, see <http://www.gnu.org/licenses/>.
25 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
37 #include <glib/gstdio.h>
38 #include <glib/gi18n-lib.h>
40 #include "camel-imapx-conn-manager.h"
41 #include "camel-imapx-folder.h"
42 #include "camel-imapx-job.h"
43 #include "camel-imapx-server.h"
44 #include "camel-imapx-settings.h"
45 #include "camel-imapx-store.h"
46 #include "camel-imapx-summary.h"
47 #include "camel-imapx-utils.h"
49 /* Specified in RFC 2060 section 2.1 */
51 #define IMAPS_PORT 993
53 #define FINFO_REFRESH_INTERVAL 60
55 #define CAMEL_IMAPX_STORE_GET_PRIVATE(obj) \
56 (G_TYPE_INSTANCE_GET_PRIVATE \
57 ((obj), CAMEL_TYPE_IMAPX_STORE, CamelIMAPXStorePrivate))
59 #define e(...) camel_imapx_debug(extra, __VA_ARGS__)
61 struct _CamelIMAPXStorePrivate {
62 CamelIMAPXConnManager *con_man;
64 CamelIMAPXServer *connecting_server;
65 gboolean is_concurrent_connection;
69 GHashTable *quota_info;
70 GMutex quota_info_lock;
73 CamelSettings *settings;
74 gulong settings_notify_handler_id;
76 /* Used for synchronizing get_folder_info_sync(). */
77 GMutex get_finfo_lock;
78 time_t last_refresh_time;
79 volatile gint syncing_folders;
81 CamelIMAPXNamespaceResponse *namespaces;
82 GMutex namespaces_lock;
84 GHashTable *mailboxes;
85 GMutex mailboxes_lock;
101 static guint signals[LAST_SIGNAL];
103 static GInitableIface *parent_initable_interface;
105 /* Forward Declarations */
106 static void camel_imapx_store_initable_init (GInitableIface *iface);
107 static void camel_network_service_init (CamelNetworkServiceInterface *iface);
108 static void camel_subscribable_init (CamelSubscribableInterface *iface);
110 G_DEFINE_TYPE_WITH_CODE (
113 CAMEL_TYPE_OFFLINE_STORE,
114 G_IMPLEMENT_INTERFACE (
116 camel_imapx_store_initable_init)
117 G_IMPLEMENT_INTERFACE (
118 CAMEL_TYPE_NETWORK_SERVICE,
119 camel_network_service_init)
120 G_IMPLEMENT_INTERFACE (
121 CAMEL_TYPE_SUBSCRIBABLE,
122 camel_subscribable_init))
125 imapx_name_hash (gconstpointer key)
127 const gchar *mailbox = key;
129 if (camel_imapx_mailbox_is_inbox (mailbox))
132 return g_str_hash (mailbox);
136 imapx_name_equal (gconstpointer a,
139 const gchar *mailbox_a = a;
140 const gchar *mailbox_b = b;
142 if (camel_imapx_mailbox_is_inbox (mailbox_a))
145 if (camel_imapx_mailbox_is_inbox (mailbox_b))
148 return g_str_equal (mailbox_a, mailbox_b);
152 imapx_store_update_store_flags (CamelStore *store)
154 CamelService *service;
155 CamelSettings *settings;
156 CamelIMAPXSettings *imapx_settings;
158 service = CAMEL_SERVICE (store);
159 settings = camel_service_ref_settings (service);
160 imapx_settings = CAMEL_IMAPX_SETTINGS (settings);
162 if (camel_imapx_settings_get_use_real_junk_path (imapx_settings)) {
163 store->flags &= ~CAMEL_STORE_VJUNK;
164 store->flags |= CAMEL_STORE_REAL_JUNK_FOLDER;
166 store->flags |= CAMEL_STORE_VJUNK;
167 store->flags &= ~CAMEL_STORE_REAL_JUNK_FOLDER;
170 if (camel_imapx_settings_get_use_real_trash_path (imapx_settings))
171 store->flags &= ~CAMEL_STORE_VTRASH;
173 store->flags |= CAMEL_STORE_VTRASH;
175 g_object_unref (settings);
179 imapx_store_settings_notify_cb (CamelSettings *settings,
183 if (g_str_equal (pspec->name, "use-real-junk-path")) {
184 imapx_store_update_store_flags (store);
185 camel_store_folder_info_stale (store);
188 if (g_str_equal (pspec->name, "use-real-trash-path")) {
189 imapx_store_update_store_flags (store);
190 camel_store_folder_info_stale (store);
193 if (g_str_equal (pspec->name, "use-subscriptions")) {
194 camel_store_folder_info_stale (store);
198 static CamelFolderInfo *
199 imapx_store_build_folder_info (CamelIMAPXStore *imapx_store,
200 const gchar *folder_path,
201 CamelFolderInfoFlags flags)
203 CamelStore *store = (CamelStore *) imapx_store;
204 CamelSettings *settings;
208 store = CAMEL_STORE (imapx_store);
209 settings = camel_service_ref_settings (CAMEL_SERVICE (store));
211 fi = camel_folder_info_new ();
212 fi->full_name = g_strdup (folder_path);
217 name = strrchr (fi->full_name, '/');
219 name = fi->full_name;
223 if (camel_imapx_mailbox_is_inbox (fi->full_name)) {
224 fi->display_name = g_strdup (_("Inbox"));
225 fi->flags |= CAMEL_FOLDER_SYSTEM;
226 fi->flags |= CAMEL_FOLDER_TYPE_INBOX;
228 fi->display_name = g_strdup (name);
231 if ((store->flags & CAMEL_STORE_VTRASH) == 0) {
232 const gchar *trash_path;
234 trash_path = camel_imapx_settings_get_real_trash_path (
235 CAMEL_IMAPX_SETTINGS (settings));
236 if (g_strcmp0 (trash_path, folder_path) == 0)
237 fi->flags |= CAMEL_FOLDER_TYPE_TRASH;
240 if ((store->flags & CAMEL_STORE_REAL_JUNK_FOLDER) != 0) {
241 const gchar *junk_path;
243 junk_path = camel_imapx_settings_get_real_junk_path (
244 CAMEL_IMAPX_SETTINGS (settings));
245 if (g_strcmp0 (junk_path, folder_path) == 0)
246 fi->flags |= CAMEL_FOLDER_TYPE_JUNK;
249 g_object_unref (settings);
255 imapx_store_rename_folder_info (CamelIMAPXStore *imapx_store,
256 const gchar *old_folder_path,
257 const gchar *new_folder_path)
260 gint olen = strlen (old_folder_path);
263 array = camel_store_summary_array (imapx_store->summary);
265 for (ii = 0; ii < array->len; ii++) {
267 CamelIMAPXStoreInfo *imapx_si;
270 gchar *new_mailbox_name;
272 si = g_ptr_array_index (array, ii);
273 path = camel_store_info_path (imapx_store->summary, si);
275 /* We need to adjust not only the entry for the renamed
276 * folder, but also the entries for all the descendants
277 * of the renamed folder. */
279 if (!g_str_has_prefix (path, old_folder_path))
282 if (strlen (path) > olen)
283 new_path = g_strdup_printf (
284 "%s/%s", new_folder_path, path + olen + 1);
286 new_path = g_strdup (new_folder_path);
288 camel_store_info_set_string (
289 imapx_store->summary, si,
290 CAMEL_STORE_INFO_PATH, new_path);
292 imapx_si = (CamelIMAPXStoreInfo *) si;
293 g_warn_if_fail (imapx_si->separator != '\0');
296 camel_imapx_folder_path_to_mailbox (
297 new_path, imapx_si->separator);
299 /* Takes ownership of new_mailbox_name. */
300 g_free (imapx_si->mailbox_name);
301 imapx_si->mailbox_name = new_mailbox_name;
303 camel_store_summary_touch (imapx_store->summary);
308 camel_store_summary_array_free (imapx_store->summary, array);
312 imapx_store_rename_storage_path (CamelIMAPXStore *imapx_store,
313 const gchar *old_mailbox,
314 const gchar *new_mailbox)
316 CamelService *service;
317 const gchar *user_cache_dir;
318 gchar *root_storage_path;
319 gchar *old_storage_path;
320 gchar *new_storage_path;
322 service = CAMEL_SERVICE (imapx_store);
323 user_cache_dir = camel_service_get_user_cache_dir (service);
324 root_storage_path = g_build_filename (user_cache_dir, "folders", NULL);
327 imapx_path_to_physical (root_storage_path, old_mailbox);
329 imapx_path_to_physical (root_storage_path, new_mailbox);
331 if (g_rename (old_storage_path, new_storage_path) == -1) {
333 "Could not rename message cache "
334 "'%s' to '%s: %s: cache reset",
340 g_free (root_storage_path);
341 g_free (old_storage_path);
342 g_free (new_storage_path);
346 imapx_store_add_mailbox_to_folder (CamelIMAPXStore *store,
347 CamelIMAPXMailbox *mailbox)
349 CamelIMAPXFolder *folder;
352 /* Add the CamelIMAPXMailbox to a cached CamelIMAPXFolder. */
354 folder_path = camel_imapx_mailbox_dup_folder_path (mailbox);
356 folder = camel_object_bag_get (
357 CAMEL_STORE (store)->folders, folder_path);
359 if (folder != NULL) {
360 camel_imapx_folder_set_mailbox (folder, mailbox);
361 g_object_unref (folder);
364 g_free (folder_path);
367 static CamelStoreInfoFlags
368 imapx_store_mailbox_attributes_to_flags (CamelIMAPXMailbox *mailbox)
370 CamelStoreInfoFlags store_info_flags = 0;
371 const gchar *attribute;
373 attribute = CAMEL_IMAPX_LIST_ATTR_NOSELECT;
374 if (camel_imapx_mailbox_has_attribute (mailbox, attribute))
375 store_info_flags |= CAMEL_STORE_INFO_FOLDER_NOSELECT;
377 attribute = CAMEL_IMAPX_LIST_ATTR_NOINFERIORS;
378 if (camel_imapx_mailbox_has_attribute (mailbox, attribute))
379 store_info_flags |= CAMEL_STORE_INFO_FOLDER_NOINFERIORS;
381 attribute = CAMEL_IMAPX_LIST_ATTR_HASCHILDREN;
382 if (camel_imapx_mailbox_has_attribute (mailbox, attribute))
383 store_info_flags |= CAMEL_STORE_INFO_FOLDER_CHILDREN;
385 attribute = CAMEL_IMAPX_LIST_ATTR_HASNOCHILDREN;
386 if (camel_imapx_mailbox_has_attribute (mailbox, attribute))
387 store_info_flags |= CAMEL_STORE_INFO_FOLDER_NOCHILDREN;
389 attribute = CAMEL_IMAPX_LIST_ATTR_SUBSCRIBED;
390 if (camel_imapx_mailbox_has_attribute (mailbox, attribute))
391 store_info_flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
393 /* XXX Does "\Marked" mean CAMEL_STORE_INFO_FOLDER_FLAGGED?
394 * Who the heck knows; the enum value is undocumented. */
396 return store_info_flags;
400 imapx_store_process_mailbox_attributes (CamelIMAPXStore *store,
401 CamelIMAPXMailbox *mailbox,
402 const gchar *oldname)
405 CamelIMAPXStoreInfo *si;
406 CamelStoreInfoFlags flags;
407 CamelSettings *settings;
408 gboolean use_subscriptions;
409 gboolean mailbox_is_subscribed;
410 gboolean mailbox_is_nonexistent;
411 gboolean mailbox_was_in_summary;
412 gboolean mailbox_was_subscribed;
413 gboolean emit_folder_created_subscribed = FALSE;
414 gboolean emit_folder_unsubscribed_deleted = FALSE;
415 gboolean emit_folder_renamed = FALSE;
416 const gchar *folder_path;
417 const gchar *mailbox_name;
420 settings = camel_service_ref_settings (CAMEL_SERVICE (store));
421 use_subscriptions = camel_imapx_settings_get_use_subscriptions (
422 CAMEL_IMAPX_SETTINGS (settings));
423 g_object_unref (settings);
425 mailbox_name = camel_imapx_mailbox_get_name (mailbox);
426 separator = camel_imapx_mailbox_get_separator (mailbox);
428 mailbox_is_subscribed =
429 camel_imapx_mailbox_has_attribute (
430 mailbox, CAMEL_IMAPX_LIST_ATTR_SUBSCRIBED) ||
431 camel_imapx_mailbox_is_inbox (mailbox_name);
433 mailbox_is_nonexistent =
434 camel_imapx_mailbox_has_attribute (
435 mailbox, CAMEL_IMAPX_LIST_ATTR_NONEXISTENT);
437 /* XXX The flags type transforms from CamelStoreInfoFlags
438 * to CamelFolderInfoFlags about half-way through this.
439 * We should really eliminate the confusing redundancy. */
440 flags = imapx_store_mailbox_attributes_to_flags (mailbox);
442 /* Summary retains ownership of the returned CamelStoreInfo. */
443 si = camel_imapx_store_summary_mailbox (store->summary, mailbox_name);
445 mailbox_was_in_summary = TRUE;
446 if (si->info.flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)
447 mailbox_was_subscribed = TRUE;
449 mailbox_was_subscribed = FALSE;
451 /* XXX Shouldn't this take a GError if it can fail? */
452 si = camel_imapx_store_summary_add_from_mailbox (
453 store->summary, mailbox);
454 g_return_if_fail (si != NULL);
455 mailbox_was_in_summary = FALSE;
456 mailbox_was_subscribed = FALSE;
459 /* Check whether the flags disagree. */
460 if (si->info.flags != flags) {
461 si->info.flags = flags;
462 camel_store_summary_touch (store->summary);
465 folder_path = camel_store_info_path (
466 store->summary, (CamelStoreInfo *) si);
467 fi = imapx_store_build_folder_info (store, folder_path, flags);
469 /* Figure out which signals to emit, if any. */
470 if (use_subscriptions) {
471 /* If we are honoring folder subscriptions, then
472 * subscription changes are equivalent to folder
473 * creation / deletion as far as we're concerned. */
474 if (mailbox_is_subscribed && !mailbox_is_nonexistent) {
475 if (oldname != NULL) {
476 emit_folder_renamed = TRUE;
477 } else if (!mailbox_was_subscribed) {
478 emit_folder_created_subscribed = TRUE;
481 if (!mailbox_is_subscribed && mailbox_was_subscribed)
482 emit_folder_unsubscribed_deleted = TRUE;
483 if (mailbox_is_nonexistent && mailbox_was_subscribed)
484 emit_folder_unsubscribed_deleted = TRUE;
486 if (!mailbox_is_nonexistent) {
487 if (oldname != NULL) {
488 emit_folder_renamed = TRUE;
489 } else if (!mailbox_was_in_summary) {
490 emit_folder_created_subscribed = TRUE;
493 if (mailbox_is_nonexistent && mailbox_was_in_summary)
494 emit_folder_unsubscribed_deleted = TRUE;
497 /* Suppress all signal emissions when synchronizing folders. */
498 if (g_atomic_int_get (&store->priv->syncing_folders) > 0) {
499 emit_folder_created_subscribed = FALSE;
500 emit_folder_unsubscribed_deleted = FALSE;
501 emit_folder_renamed = FALSE;
504 /* At most one signal emission flag should be set. */
506 (emit_folder_created_subscribed ? 1 : 0) +
507 (emit_folder_unsubscribed_deleted ? 1 : 0) +
508 (emit_folder_renamed ? 1 : 0) <= 1);
510 if (emit_folder_created_subscribed) {
511 camel_store_folder_created (
512 CAMEL_STORE (store), fi);
513 camel_subscribable_folder_subscribed (
514 CAMEL_SUBSCRIBABLE (store), fi);
517 if (emit_folder_unsubscribed_deleted) {
518 camel_subscribable_folder_unsubscribed (
519 CAMEL_SUBSCRIBABLE (store), fi);
520 camel_store_folder_deleted (
521 CAMEL_STORE (store), fi);
524 if (emit_folder_renamed) {
525 gchar *old_folder_path;
526 gchar *new_folder_path;
528 old_folder_path = camel_imapx_mailbox_to_folder_path (
530 new_folder_path = camel_imapx_mailbox_to_folder_path (
531 mailbox_name, separator);
533 imapx_store_rename_folder_info (
534 store, old_folder_path, new_folder_path);
535 imapx_store_rename_storage_path (
536 store, old_folder_path, new_folder_path);
538 camel_store_folder_renamed (
539 CAMEL_STORE (store), old_folder_path, fi);
541 g_free (old_folder_path);
542 g_free (new_folder_path);
545 camel_folder_info_free (fi);
549 imapx_store_process_mailbox_status (CamelIMAPXStore *imapx_store,
550 CamelIMAPXMailbox *mailbox)
556 folder_path = camel_imapx_mailbox_dup_folder_path (mailbox);
557 store = CAMEL_STORE (imapx_store);
559 /* Update only already opened folders */
560 folder = camel_object_bag_reserve (store->folders, folder_path);
561 if (folder != NULL) {
562 CamelIMAPXFolder *imapx_folder;
563 CamelIMAPXSummary *imapx_summary;
566 imapx_folder = CAMEL_IMAPX_FOLDER (folder);
567 imapx_summary = CAMEL_IMAPX_SUMMARY (folder->summary);
569 uidvalidity = camel_imapx_mailbox_get_uidvalidity (mailbox);
571 if (uidvalidity > 0 && uidvalidity != imapx_summary->validity)
572 camel_imapx_folder_invalidate_local_cache (
573 imapx_folder, uidvalidity);
575 g_object_unref (folder);
577 camel_object_bag_abort (store->folders, folder_path);
580 g_free (folder_path);
584 imapx_store_connect_to_settings (CamelStore *store)
586 CamelIMAPXStorePrivate *priv;
587 CamelSettings *settings;
590 /* XXX I considered calling camel_store_folder_info_stale()
591 * here, but I suspect it would create unnecessary extra
592 * work for applications during startup since the signal
593 * is not emitted immediately.
595 * Let's just say whomever replaces the settings object
596 * in a CamelService is reponsible for deciding whether
597 * camel_store_folder_info_stale() should be called. */
599 priv = CAMEL_IMAPX_STORE_GET_PRIVATE (store);
601 settings = camel_service_ref_settings (CAMEL_SERVICE (store));
603 g_mutex_lock (&priv->settings_lock);
605 if (priv->settings != NULL) {
606 g_signal_handler_disconnect (
608 priv->settings_notify_handler_id);
609 priv->settings_notify_handler_id = 0;
610 g_clear_object (&priv->settings);
613 priv->settings = g_object_ref (settings);
615 handler_id = g_signal_connect (
617 G_CALLBACK (imapx_store_settings_notify_cb), store);
618 priv->settings_notify_handler_id = handler_id;
620 g_mutex_unlock (&priv->settings_lock);
622 g_object_unref (settings);
626 imapx_store_set_property (GObject *object,
631 switch (property_id) {
632 case PROP_CONNECTABLE:
633 camel_network_service_set_connectable (
634 CAMEL_NETWORK_SERVICE (object),
635 g_value_get_object (value));
639 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
643 imapx_store_get_property (GObject *object,
648 switch (property_id) {
649 case PROP_CONNECTABLE:
650 g_value_take_object (
652 camel_network_service_ref_connectable (
653 CAMEL_NETWORK_SERVICE (object)));
656 case PROP_HOST_REACHABLE:
657 g_value_set_boolean (
659 camel_network_service_get_host_reachable (
660 CAMEL_NETWORK_SERVICE (object)));
664 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
668 imapx_store_dispose (GObject *object)
670 CamelIMAPXStore *imapx_store = CAMEL_IMAPX_STORE (object);
672 /* Force disconnect so we don't have it run later,
673 * after we've cleaned up some stuff. */
674 if (imapx_store->priv->con_man != NULL) {
675 camel_service_disconnect_sync (CAMEL_SERVICE (imapx_store), FALSE, NULL, NULL);
676 g_clear_object (&imapx_store->priv->con_man);
679 if (imapx_store->priv->settings_notify_handler_id > 0) {
680 g_signal_handler_disconnect (
681 imapx_store->priv->settings,
682 imapx_store->priv->settings_notify_handler_id);
683 imapx_store->priv->settings_notify_handler_id = 0;
686 g_clear_object (&imapx_store->summary);
688 g_clear_object (&imapx_store->priv->connecting_server);
689 g_clear_object (&imapx_store->priv->settings);
690 g_clear_object (&imapx_store->priv->namespaces);
692 g_hash_table_remove_all (imapx_store->priv->mailboxes);
694 /* Chain up to parent's dispose() method. */
695 G_OBJECT_CLASS (camel_imapx_store_parent_class)->dispose (object);
699 imapx_store_finalize (GObject *object)
701 CamelIMAPXStorePrivate *priv;
703 priv = CAMEL_IMAPX_STORE_GET_PRIVATE (object);
705 g_mutex_clear (&priv->get_finfo_lock);
707 g_mutex_clear (&priv->server_lock);
709 g_hash_table_destroy (priv->quota_info);
710 g_mutex_clear (&priv->quota_info_lock);
712 g_mutex_clear (&priv->settings_lock);
714 g_mutex_clear (&priv->namespaces_lock);
716 g_hash_table_destroy (priv->mailboxes);
717 g_mutex_clear (&priv->mailboxes_lock);
719 /* Chain up to parent's finalize() method. */
720 G_OBJECT_CLASS (camel_imapx_store_parent_class)->finalize (object);
724 imapx_store_notify (GObject *object,
727 if (g_str_equal (pspec->name, "settings")) {
728 imapx_store_connect_to_settings (CAMEL_STORE (object));
729 imapx_store_update_store_flags (CAMEL_STORE (object));
732 /* Chain up to parent's notify() method. */
733 G_OBJECT_CLASS (camel_imapx_store_parent_class)->
734 notify (object, pspec);
738 imapx_get_name (CamelService *service,
741 CamelNetworkSettings *network_settings;
742 CamelSettings *settings;
747 settings = camel_service_ref_settings (service);
749 network_settings = CAMEL_NETWORK_SETTINGS (settings);
750 host = camel_network_settings_dup_host (network_settings);
751 user = camel_network_settings_dup_user (network_settings);
753 g_object_unref (settings);
756 name = g_strdup_printf (
757 _("IMAP server %s"), host);
759 name = g_strdup_printf (
760 _("IMAP service for %s on %s"), user, host);
769 imapx_connect_sync (CamelService *service,
770 GCancellable *cancellable,
773 CamelIMAPXStore *imapx_store;
774 CamelIMAPXServer *imapx_server;
777 imapx_store = CAMEL_IMAPX_STORE (service);
779 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
780 success = imapx_server != NULL;
782 g_clear_object (&imapx_server);
788 imapx_disconnect_sync (CamelService *service,
790 GCancellable *cancellable,
793 CamelIMAPXStorePrivate *priv;
795 priv = CAMEL_IMAPX_STORE_GET_PRIVATE (service);
797 if (priv->con_man != NULL)
798 camel_imapx_conn_manager_close_connections (priv->con_man, NULL);
800 g_mutex_lock (&priv->server_lock);
802 g_clear_object (&priv->connecting_server);
804 g_mutex_unlock (&priv->server_lock);
809 static CamelAuthenticationResult
810 imapx_authenticate_sync (CamelService *service,
811 const gchar *mechanism,
812 GCancellable *cancellable,
815 CamelIMAPXStorePrivate *priv;
816 CamelIMAPXServer *imapx_server;
817 CamelAuthenticationResult result;
819 priv = CAMEL_IMAPX_STORE_GET_PRIVATE (service);
821 /* This should have been set for us by connect_sync(). */
822 g_mutex_lock (&priv->server_lock);
823 imapx_server = g_object_ref (priv->connecting_server);
824 g_mutex_unlock (&priv->server_lock);
826 result = camel_imapx_server_authenticate (
827 imapx_server, mechanism, cancellable, error);
829 g_clear_object (&imapx_server);
834 CamelServiceAuthType camel_imapx_password_authtype = {
837 N_("This option will connect to the IMAP server using a "
838 "plaintext password."),
845 imapx_query_auth_types_sync (CamelService *service,
846 GCancellable *cancellable,
849 CamelServiceAuthType *authtype;
850 GList *sasl_types = NULL;
852 CamelIMAPXServer *server;
854 server = camel_imapx_server_new (CAMEL_IMAPX_STORE (service));
856 if (!imapx_connect_to_server (server, cancellable, error))
859 sasl_types = camel_sasl_authtype_list (FALSE);
860 for (t = sasl_types; t; t = next) {
864 if (!server->cinfo || !g_hash_table_lookup (server->cinfo->auth_types, authtype->authproto)) {
865 sasl_types = g_list_remove_link (sasl_types, t);
870 sasl_types = g_list_prepend (
871 sasl_types, &camel_imapx_password_authtype);
874 g_object_unref (server);
880 get_folder_offline (CamelStore *store,
881 const gchar *folder_name,
882 CamelStoreGetFolderFlags flags,
885 CamelIMAPXStore *imapx_store = CAMEL_IMAPX_STORE (store);
886 CamelFolder *new_folder = NULL;
888 CamelService *service;
889 const gchar *user_cache_dir;
891 service = CAMEL_SERVICE (store);
892 user_cache_dir = camel_service_get_user_cache_dir (service);
894 si = camel_store_summary_path (imapx_store->summary, folder_name);
900 base_dir = g_build_filename (user_cache_dir, "folders", NULL);
901 folder_dir = imapx_path_to_physical (base_dir, folder_name);
902 new_folder = camel_imapx_folder_new (
903 store, folder_dir, folder_name, error);
907 camel_store_summary_info_unref (imapx_store->summary, si);
910 error, CAMEL_STORE_ERROR,
911 CAMEL_STORE_ERROR_NO_FOLDER,
912 _("No such folder %s"), folder_name);
919 fill_fi (CamelStore *store,
924 folder = camel_object_bag_peek (store->folders, fi->full_name);
926 CamelIMAPXFolder *imapx_folder;
927 CamelIMAPXSummary *ims;
928 CamelIMAPXMailbox *mailbox;
931 ims = (CamelIMAPXSummary *) folder->summary;
933 ims = (CamelIMAPXSummary *) camel_imapx_summary_new (folder);
935 imapx_folder = CAMEL_IMAPX_FOLDER (folder);
936 mailbox = camel_imapx_folder_ref_mailbox (imapx_folder);
938 fi->unread = camel_folder_summary_get_unread_count ((CamelFolderSummary *) ims);
939 fi->total = camel_folder_summary_get_saved_count ((CamelFolderSummary *) ims);
941 g_clear_object (&mailbox);
943 if (!folder->summary)
944 g_object_unref (ims);
945 g_object_unref (folder);
950 imapx_delete_folder_from_cache (CamelIMAPXStore *imapx_store,
951 const gchar *folder_path)
954 gchar *folder_dir, *storage_path;
956 CamelService *service;
957 const gchar *user_cache_dir;
959 service = CAMEL_SERVICE (imapx_store);
960 user_cache_dir = camel_service_get_user_cache_dir (service);
962 storage_path = g_build_filename (user_cache_dir, "folders", NULL);
963 folder_dir = imapx_path_to_physical (storage_path, folder_path);
964 g_free (storage_path);
965 if (g_access (folder_dir, F_OK) != 0) {
970 /* Delete summary and all the data */
971 state_file = g_build_filename (folder_dir, "cmeta", NULL);
972 g_unlink (state_file);
975 camel_db_delete_folder (
976 CAMEL_STORE (imapx_store)->cdb_w, folder_path, NULL);
977 g_rmdir (folder_dir);
979 state_file = g_build_filename (folder_dir, "subfolders", NULL);
980 g_rmdir (state_file);
983 g_rmdir (folder_dir);
987 camel_store_summary_remove_path (imapx_store->summary, folder_path);
988 camel_store_summary_save (imapx_store->summary);
990 fi = imapx_store_build_folder_info (imapx_store, folder_path, 0);
991 camel_store_folder_deleted (CAMEL_STORE (imapx_store), fi);
992 camel_folder_info_free (fi);
995 static CamelFolderInfo *
996 get_folder_info_offline (CamelStore *store,
998 CamelStoreGetFolderInfoFlags flags,
1001 CamelIMAPXStore *imapx_store = CAMEL_IMAPX_STORE (store);
1002 CamelService *service;
1003 CamelSettings *settings;
1004 gboolean include_inbox = FALSE;
1005 CamelFolderInfo *fi;
1008 gboolean use_subscriptions;
1011 service = CAMEL_SERVICE (store);
1013 settings = camel_service_ref_settings (service);
1015 use_subscriptions = camel_imapx_settings_get_use_subscriptions (
1016 CAMEL_IMAPX_SETTINGS (settings));
1018 g_object_unref (settings);
1020 /* FIXME: obey other flags */
1022 folders = g_ptr_array_new ();
1024 if (top == NULL || top[0] == '\0') {
1025 include_inbox = TRUE;
1029 /* folder_info_build will insert parent nodes as necessary and mark
1030 * them as noselect, which is information we actually don't have at
1031 * the moment. So let it do the right thing by bailing out if it's
1032 * not a folder we're explicitly interested in. */
1034 array = camel_store_summary_array (imapx_store->summary);
1036 for (ii = 0; ii < array->len; ii++) {
1038 const gchar *folder_path;
1039 gboolean si_is_inbox;
1040 gboolean si_is_match;
1042 si = g_ptr_array_index (array, ii);
1043 folder_path = camel_store_info_path (imapx_store->summary, si);
1044 si_is_inbox = (g_ascii_strcasecmp (folder_path, "INBOX") == 0);
1046 /* Filter by folder path. */
1048 (include_inbox && si_is_inbox) ||
1049 g_str_has_prefix (folder_path, top);
1054 /* Filter by subscription flags.
1056 * Skip the folder if:
1057 * The user only wants to see subscribed folders
1058 * AND the folder is not subscribed
1059 * AND the caller only wants SUBSCRIBED folder info
1060 * AND the caller does NOT want a SUBSCRIPTION_LIST
1062 * Note that having both SUBSCRIBED and SUBSCRIPTION_LIST
1063 * flags set is contradictory. SUBSCRIPTION_LIST wins in
1067 !use_subscriptions ||
1068 (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) ||
1069 !(flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) ||
1070 (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST);
1075 fi = imapx_store_build_folder_info (
1076 imapx_store, folder_path, 0);
1077 fi->unread = si->unread;
1078 fi->total = si->total;
1079 if ((fi->flags & CAMEL_FOLDER_TYPE_MASK) != 0)
1081 (fi->flags & CAMEL_FOLDER_TYPE_MASK) |
1082 (si->flags & ~CAMEL_FOLDER_TYPE_MASK);
1084 fi->flags = si->flags;
1086 /* blah, this gets lost somewhere, i can't be bothered finding out why */
1089 (fi->flags & ~CAMEL_FOLDER_TYPE_MASK) |
1090 CAMEL_FOLDER_TYPE_INBOX;
1091 fi->flags |= CAMEL_FOLDER_SYSTEM;
1094 if (!(si->flags & CAMEL_FOLDER_NOSELECT))
1095 fill_fi ((CamelStore *) imapx_store, fi);
1098 fi->flags |= CAMEL_FOLDER_NOCHILDREN;
1100 g_ptr_array_add (folders, fi);
1103 camel_store_summary_array_free (imapx_store->summary, array);
1105 fi = camel_folder_info_build (folders, top, '/', TRUE);
1107 g_ptr_array_free (folders, TRUE);
1113 collect_folder_info_for_list (CamelIMAPXStore *imapx_store,
1114 CamelIMAPXMailbox *mailbox,
1115 GHashTable *folder_info_results)
1117 CamelIMAPXStoreInfo *si;
1118 CamelFolderInfo *fi;
1119 const gchar *folder_path;
1120 const gchar *mailbox_name;
1122 mailbox_name = camel_imapx_mailbox_get_name (mailbox);
1124 si = camel_imapx_store_summary_mailbox (
1125 imapx_store->summary, mailbox_name);
1126 g_return_if_fail (si != NULL);
1128 folder_path = camel_store_info_path (
1129 imapx_store->summary, (CamelStoreInfo *) si);
1130 fi = imapx_store_build_folder_info (imapx_store, folder_path, 0);
1132 /* Takes ownership of the CamelFolderInfo. */
1133 g_hash_table_insert (folder_info_results, g_strdup (mailbox_name), fi);
1137 fetch_folder_info_for_pattern (CamelIMAPXServer *server,
1138 CamelIMAPXNamespace *namespace,
1139 const gchar *pattern,
1140 CamelStoreGetFolderInfoFlags flags,
1141 GHashTable *folder_info_results,
1142 GCancellable *cancellable,
1145 CamelIMAPXStore *imapx_store;
1147 GError *local_error = NULL;
1150 g_object_ref (server);
1152 imapx_store = camel_imapx_server_ref_store (server);
1154 success = camel_imapx_server_list (server, pattern, flags, cancellable, &local_error);
1156 while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
1157 g_clear_error (&local_error);
1158 g_clear_object (&server);
1160 server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
1162 success = camel_imapx_server_list (server, pattern, flags, cancellable, &local_error);
1165 g_clear_object (&server);
1168 g_clear_object (&imapx_store);
1170 if (!g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED) &&
1171 camel_imapx_namespace_get_category (namespace) != CAMEL_IMAPX_NAMESPACE_PERSONAL) {
1172 /* Ignore errors for non-personal namespaces; one such error can be:
1173 "NO LIST failed: wildcards not permitted in username" */
1174 g_clear_error (&local_error);
1176 } else if (local_error) {
1177 g_propagate_error (error, local_error);
1183 list = camel_imapx_store_list_mailboxes (imapx_store, namespace, pattern);
1185 for (link = list; link != NULL; link = g_list_next (link)) {
1186 CamelIMAPXMailbox *mailbox;
1188 mailbox = CAMEL_IMAPX_MAILBOX (link->data);
1190 collect_folder_info_for_list (
1191 imapx_store, mailbox, folder_info_results);
1194 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1196 g_object_unref (imapx_store);
1202 fetch_folder_info_for_inbox (CamelIMAPXServer *server,
1203 CamelStoreGetFolderInfoFlags flags,
1204 GHashTable *folder_info_results,
1205 GCancellable *cancellable,
1208 CamelIMAPXStore *imapx_store;
1209 GError *local_error = NULL;
1212 g_object_ref (server);
1213 imapx_store = camel_imapx_server_ref_store (server);
1215 success = camel_imapx_server_list (server, "INBOX", flags, cancellable, &local_error);
1217 while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
1218 g_clear_error (&local_error);
1219 g_clear_object (&server);
1221 server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
1223 success = camel_imapx_server_list (server, "INBOX", flags, cancellable, &local_error);
1226 g_clear_object (&server);
1229 g_propagate_error (error, local_error);
1232 CamelIMAPXMailbox *mailbox;
1234 mailbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX");
1235 g_return_val_if_fail (mailbox != NULL, FALSE);
1237 collect_folder_info_for_list (
1238 imapx_store, mailbox, folder_info_results);
1241 g_object_unref (imapx_store);
1247 fetch_folder_info_for_namespace_category (CamelIMAPXStore *imapx_store,
1248 CamelIMAPXServer *server,
1249 CamelIMAPXNamespaceCategory category,
1250 CamelStoreGetFolderInfoFlags flags,
1251 GHashTable *folder_info_results,
1252 GCancellable *cancellable,
1255 CamelIMAPXNamespaceResponse *namespace_response;
1257 gboolean success = TRUE;
1259 namespace_response = camel_imapx_store_ref_namespaces (imapx_store);
1260 g_return_val_if_fail (namespace_response != NULL, FALSE);
1262 list = camel_imapx_namespace_response_list (namespace_response);
1264 for (link = list; link != NULL; link = g_list_next (link)) {
1265 CamelIMAPXNamespace *namespace;
1266 CamelIMAPXNamespaceCategory ns_category;
1267 const gchar *ns_prefix;
1270 namespace = CAMEL_IMAPX_NAMESPACE (link->data);
1271 ns_category = camel_imapx_namespace_get_category (namespace);
1272 ns_prefix = camel_imapx_namespace_get_prefix (namespace);
1274 if ((flags & (CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST | CAMEL_STORE_FOLDER_INFO_SUBSCRIBED)) == 0 && ns_category != category)
1277 pattern = g_strdup_printf ("%s*", ns_prefix);
1279 success = fetch_folder_info_for_pattern (
1280 server, namespace, pattern, flags,
1281 folder_info_results, cancellable, error);
1289 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1291 g_object_unref (namespace_response);
1297 fetch_folder_info_from_folder_path (CamelIMAPXStore *imapx_store,
1298 CamelIMAPXServer *server,
1299 const gchar *folder_path,
1300 CamelStoreGetFolderInfoFlags flags,
1301 GHashTable *folder_info_results,
1302 GCancellable *cancellable,
1305 CamelIMAPXNamespaceResponse *namespace_response;
1306 CamelIMAPXNamespace *namespace;
1307 gchar *mailbox_name;
1308 gchar *utf7_mailbox_name;
1311 gboolean success = FALSE;
1313 namespace_response = camel_imapx_store_ref_namespaces (imapx_store);
1314 g_return_val_if_fail (namespace_response != NULL, FALSE);
1316 /* Find a suitable IMAP namespace for the folder path. */
1317 namespace = camel_imapx_namespace_response_lookup_for_path (
1318 namespace_response, folder_path);
1319 if (namespace == NULL) {
1321 error, CAMEL_STORE_ERROR,
1322 CAMEL_STORE_ERROR_INVALID,
1323 _("No IMAP namespace for folder path '%s'"),
1328 /* Convert the folder path to a mailbox name. */
1329 separator = camel_imapx_namespace_get_separator (namespace);
1330 mailbox_name = g_strdelimit (g_strdup (folder_path), "/", separator);
1332 utf7_mailbox_name = camel_utf8_utf7 (mailbox_name);
1333 pattern = g_strdup_printf ("%s*", utf7_mailbox_name);
1335 success = fetch_folder_info_for_pattern (
1336 server, namespace, pattern, flags,
1337 folder_info_results, cancellable, error);
1340 g_free (utf7_mailbox_name);
1341 g_free (mailbox_name);
1344 g_clear_object (&namespace);
1345 g_clear_object (&namespace_response);
1351 sync_folders (CamelIMAPXStore *imapx_store,
1352 const gchar *root_folder_path,
1353 CamelStoreGetFolderInfoFlags flags,
1354 GCancellable *cancellable,
1357 CamelIMAPXServer *server;
1358 GHashTable *folder_info_results;
1363 server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
1367 /* mailbox name -> CamelFolderInfo */
1368 folder_info_results = g_hash_table_new_full (
1369 (GHashFunc) imapx_name_hash,
1370 (GEqualFunc) imapx_name_equal,
1371 (GDestroyNotify) g_free,
1372 (GDestroyNotify) camel_folder_info_free);
1374 /* This suppresses CamelStore signal emissions
1375 * in imapx_store_process_mailbox_attributes(). */
1376 g_atomic_int_inc (&imapx_store->priv->syncing_folders);
1378 if (root_folder_path != NULL && *root_folder_path != '\0') {
1379 success = fetch_folder_info_from_folder_path (
1380 imapx_store, server, root_folder_path, flags,
1381 folder_info_results, cancellable, error);
1383 gboolean have_folder_info_for_inbox;
1385 /* XXX We only fetch personal mailboxes at this time. */
1386 success = fetch_folder_info_for_namespace_category (
1387 imapx_store, server, CAMEL_IMAPX_NAMESPACE_PERSONAL, flags,
1388 folder_info_results, cancellable, error);
1390 have_folder_info_for_inbox =
1391 g_hash_table_contains (folder_info_results, "INBOX");
1393 /* XXX Slight hack, mainly for Courier servers. If INBOX
1394 * is not included in any defined personal namespaces,
1395 * then LIST it explicitly. */
1396 if (success && !have_folder_info_for_inbox)
1397 success = fetch_folder_info_for_inbox (
1398 server, flags, folder_info_results,
1399 cancellable, error);
1402 /* Don't need to test for zero, just decrement atomically. */
1403 g_atomic_int_dec_and_test (&imapx_store->priv->syncing_folders);
1408 array = camel_store_summary_array (imapx_store->summary);
1410 for (ii = 0; ii < array->len; ii++) {
1412 CamelFolderInfo *fi;
1413 const gchar *mailbox_name;
1414 const gchar *si_path;
1415 gboolean pattern_match;
1417 si = g_ptr_array_index (array, ii);
1418 si_path = camel_store_info_path (imapx_store->summary, si);
1420 mailbox_name = ((CamelIMAPXStoreInfo *) si)->mailbox_name;
1421 if (mailbox_name == NULL || *mailbox_name == '\0')
1425 (root_folder_path == NULL) ||
1426 (*root_folder_path == '\0') ||
1427 (g_str_has_prefix (si_path, root_folder_path));
1431 fi = g_hash_table_lookup (folder_info_results, mailbox_name);
1434 gchar *dup_folder_path = g_strdup (si_path);
1436 if (dup_folder_path != NULL) {
1437 /* Do not unsubscribe from it, it influences UI for non-subscribable folders */
1438 imapx_delete_folder_from_cache (
1439 imapx_store, dup_folder_path);
1440 g_free (dup_folder_path);
1442 camel_store_summary_remove (
1443 imapx_store->summary, si);
1448 camel_store_summary_array_free (imapx_store->summary, array);
1451 g_hash_table_destroy (folder_info_results);
1453 g_object_unref (server);
1459 imapx_refresh_finfo (CamelSession *session,
1460 GCancellable *cancellable,
1461 CamelIMAPXStore *store,
1464 CamelService *service;
1465 const gchar *display_name;
1467 service = CAMEL_SERVICE (store);
1468 display_name = camel_service_get_display_name (service);
1470 camel_operation_push_message (
1471 cancellable, _("Retrieving folder list for %s"),
1474 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store)))
1477 if (!camel_service_connect_sync (
1478 CAMEL_SERVICE (store), cancellable, error))
1481 sync_folders (store, NULL, 0, cancellable, error);
1483 camel_store_summary_save (store->summary);
1486 camel_operation_pop_message (cancellable);
1490 discover_inbox (CamelIMAPXStore *imapx_store,
1491 GCancellable *cancellable)
1493 CamelIMAPXServer *imapx_server;
1494 CamelIMAPXMailbox *mailbox = NULL;
1495 const gchar *attribute;
1497 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, NULL);
1499 if (imapx_server == NULL)
1502 mailbox = camel_imapx_store_ref_mailbox (imapx_store, "INBOX");
1504 if (mailbox == NULL)
1507 attribute = CAMEL_IMAPX_LIST_ATTR_SUBSCRIBED;
1508 if (!camel_imapx_mailbox_has_attribute (mailbox, attribute)) {
1509 GError *local_error = NULL;
1512 success = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
1514 while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
1515 g_clear_error (&local_error);
1516 g_clear_object (&imapx_server);
1518 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
1520 success = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
1523 g_clear_error (&local_error);
1527 g_clear_object (&mailbox);
1528 g_clear_object (&imapx_server);
1532 imapx_can_refresh_folder (CamelStore *store,
1533 CamelFolderInfo *info,
1536 CamelService *service;
1537 CamelSettings *settings;
1538 CamelStoreClass *store_class;
1540 gboolean check_subscribed;
1541 gboolean subscribed;
1543 GError *local_error = NULL;
1545 store_class = CAMEL_STORE_CLASS (camel_imapx_store_parent_class);
1547 service = CAMEL_SERVICE (store);
1549 settings = camel_service_ref_settings (service);
1551 check_all = camel_imapx_settings_get_check_all (
1552 CAMEL_IMAPX_SETTINGS (settings));
1554 check_subscribed = camel_imapx_settings_get_check_subscribed (
1555 CAMEL_IMAPX_SETTINGS (settings));
1557 g_object_unref (settings);
1559 subscribed = ((info->flags & CAMEL_FOLDER_SUBSCRIBED) != 0);
1561 res = store_class->can_refresh_folder (store, info, &local_error) ||
1562 check_all || (check_subscribed && subscribed);
1564 if (local_error != NULL)
1565 g_propagate_error (error, local_error);
1570 static CamelFolder *
1571 imapx_store_get_folder_sync (CamelStore *store,
1572 const gchar *folder_name,
1573 CamelStoreGetFolderFlags flags,
1574 GCancellable *cancellable,
1577 CamelFolder *folder;
1578 CamelSettings *settings;
1579 gboolean use_real_junk_path = FALSE;
1580 gboolean use_real_trash_path = FALSE;
1582 /* XXX This should be taken care of before we get this far. */
1583 if (*folder_name == '/')
1586 folder = get_folder_offline (store, folder_name, flags, error);
1588 /* Configure the folder flags according to IMAPX settings.
1590 * XXX Since this is only done when the folder is first created,
1591 * a restart is required to pick up changes to real Junk/Trash
1592 * folder settings. Need to think of a better way.
1594 * Perhaps have CamelStoreSettings grow junk and trash path
1595 * string properties, and eliminate the CAMEL_FOLDER_IS_JUNK
1596 * and CAMEL_FOLDER_IS_TRASH flags. Then add functions like
1597 * camel_folder_is_junk() and camel_folder_is_trash(), which
1598 * compare their own full name against CamelStoreSettings.
1600 * Something to think about...
1603 settings = camel_service_ref_settings (CAMEL_SERVICE (store));
1605 if (folder != NULL) {
1606 use_real_junk_path =
1607 camel_imapx_settings_get_use_real_junk_path (
1608 CAMEL_IMAPX_SETTINGS (settings));
1609 use_real_trash_path =
1610 camel_imapx_settings_get_use_real_trash_path (
1611 CAMEL_IMAPX_SETTINGS (settings));
1614 if (use_real_junk_path) {
1615 gchar *real_junk_path;
1618 camel_imapx_settings_dup_real_junk_path (
1619 CAMEL_IMAPX_SETTINGS (settings));
1621 /* So we can safely compare strings. */
1622 if (real_junk_path == NULL)
1623 real_junk_path = g_strdup ("");
1625 if (g_ascii_strcasecmp (real_junk_path, folder_name) == 0)
1626 folder->folder_flags |= CAMEL_FOLDER_IS_JUNK;
1628 g_free (real_junk_path);
1631 if (use_real_trash_path) {
1632 gchar *real_trash_path;
1635 camel_imapx_settings_dup_real_trash_path (
1636 CAMEL_IMAPX_SETTINGS (settings));
1638 /* So we can safely compare strings. */
1639 if (real_trash_path == NULL)
1640 real_trash_path = g_strdup ("");
1642 if (g_ascii_strcasecmp (real_trash_path, folder_name) == 0)
1643 folder->folder_flags |= CAMEL_FOLDER_IS_TRASH;
1645 g_free (real_trash_path);
1648 g_object_unref (settings);
1653 static CamelFolderInfo *
1654 imapx_store_get_folder_info_sync (CamelStore *store,
1656 CamelStoreGetFolderInfoFlags flags,
1657 GCancellable *cancellable,
1660 CamelIMAPXStore *imapx_store;
1661 CamelFolderInfo *fi = NULL;
1662 CamelService *service;
1663 CamelSettings *settings;
1664 gboolean initial_setup = FALSE;
1665 gboolean use_subscriptions;
1667 service = CAMEL_SERVICE (store);
1668 imapx_store = CAMEL_IMAPX_STORE (store);
1670 settings = camel_service_ref_settings (service);
1672 use_subscriptions = camel_imapx_settings_get_use_subscriptions (
1673 CAMEL_IMAPX_SETTINGS (settings));
1675 g_object_unref (settings);
1680 g_mutex_lock (&imapx_store->priv->get_finfo_lock);
1682 if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) {
1683 fi = get_folder_info_offline (store, top, flags, error);
1687 if (imapx_store->priv->last_refresh_time == 0) {
1688 imapx_store->priv->last_refresh_time = time (NULL);
1689 initial_setup = TRUE;
1692 /* XXX I don't know why the SUBSCRIBED flag matters here. */
1693 if (!initial_setup && flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) {
1694 time_t time_since_last_refresh;
1696 time_since_last_refresh =
1697 time (NULL) - imapx_store->priv->last_refresh_time;
1699 if (time_since_last_refresh > FINFO_REFRESH_INTERVAL) {
1700 CamelSession *session;
1702 imapx_store->priv->last_refresh_time = time (NULL);
1704 session = camel_service_ref_session (service);
1706 camel_session_submit_job (
1707 session, (CamelSessionCallback)
1708 imapx_refresh_finfo,
1709 g_object_ref (store),
1710 (GDestroyNotify) g_object_unref);
1712 g_object_unref (session);
1716 /* Avoid server interaction if the FAST flag is set. */
1717 if (!initial_setup && flags & CAMEL_STORE_FOLDER_INFO_FAST) {
1718 fi = get_folder_info_offline (store, top, flags, error);
1722 if (!sync_folders (imapx_store, top, flags, cancellable, error))
1725 camel_store_summary_save (imapx_store->summary);
1727 /* ensure the INBOX is subscribed if lsub was preferred*/
1728 if (initial_setup && use_subscriptions)
1729 discover_inbox (imapx_store, cancellable);
1731 fi = get_folder_info_offline (store, top, flags, error);
1734 g_mutex_unlock (&imapx_store->priv->get_finfo_lock);
1739 static CamelFolder *
1740 imapx_store_get_junk_folder_sync (CamelStore *store,
1741 GCancellable *cancellable,
1744 CamelFolder *folder = NULL;
1745 CamelStoreClass *store_class;
1746 CamelSettings *settings;
1748 settings = camel_service_ref_settings (CAMEL_SERVICE (store));
1749 if (camel_imapx_settings_get_use_real_junk_path (CAMEL_IMAPX_SETTINGS (settings))) {
1750 gchar *real_junk_path;
1752 real_junk_path = camel_imapx_settings_dup_real_junk_path (CAMEL_IMAPX_SETTINGS (settings));
1753 if (real_junk_path) {
1754 folder = camel_store_get_folder_sync (store, real_junk_path, 0, cancellable, NULL);
1755 g_free (real_junk_path);
1758 g_object_unref (settings);
1763 store_class = CAMEL_STORE_CLASS (camel_imapx_store_parent_class);
1764 folder = store_class->get_junk_folder_sync (store, cancellable, error);
1767 CamelObject *object = CAMEL_OBJECT (folder);
1768 CamelService *service;
1769 const gchar *user_cache_dir;
1772 service = CAMEL_SERVICE (store);
1773 user_cache_dir = camel_service_get_user_cache_dir (service);
1775 state = g_build_filename (
1776 user_cache_dir, "system", "Junk.cmeta", NULL);
1778 camel_object_set_state_filename (object, state);
1781 camel_object_state_read (object);
1787 static CamelFolder *
1788 imapx_store_get_trash_folder_sync (CamelStore *store,
1789 GCancellable *cancellable,
1792 CamelFolder *folder = NULL;
1793 CamelStoreClass *store_class;
1794 CamelSettings *settings;
1796 settings = camel_service_ref_settings (CAMEL_SERVICE (store));
1797 if (camel_imapx_settings_get_use_real_trash_path (CAMEL_IMAPX_SETTINGS (settings))) {
1798 gchar *real_trash_path;
1800 real_trash_path = camel_imapx_settings_dup_real_trash_path (CAMEL_IMAPX_SETTINGS (settings));
1801 if (real_trash_path) {
1802 folder = camel_store_get_folder_sync (store, real_trash_path, 0, cancellable, NULL);
1803 g_free (real_trash_path);
1806 g_object_unref (settings);
1811 store_class = CAMEL_STORE_CLASS (camel_imapx_store_parent_class);
1812 folder = store_class->get_trash_folder_sync (store, cancellable, error);
1815 CamelObject *object = CAMEL_OBJECT (folder);
1816 CamelService *service;
1817 const gchar *user_cache_dir;
1820 service = CAMEL_SERVICE (store);
1821 user_cache_dir = camel_service_get_user_cache_dir (service);
1823 state = g_build_filename (
1824 user_cache_dir, "system", "Trash.cmeta", NULL);
1826 camel_object_set_state_filename (object, state);
1829 camel_object_state_read (object);
1835 static CamelFolderInfo *
1836 imapx_store_create_folder_sync (CamelStore *store,
1837 const gchar *parent_name,
1838 const gchar *folder_name,
1839 GCancellable *cancellable,
1842 CamelIMAPXNamespaceResponse *namespace_response;
1843 CamelIMAPXNamespace *namespace;
1844 CamelIMAPXStore *imapx_store;
1845 CamelIMAPXServer *imapx_server;
1846 CamelFolder *folder;
1847 CamelIMAPXMailbox *parent_mailbox = NULL;
1848 CamelFolderInfo *fi = NULL;
1850 const gchar *namespace_prefix;
1851 const gchar *parent_mailbox_name;
1852 gchar *mailbox_name = NULL;
1855 GError *local_error = NULL;
1857 imapx_store = CAMEL_IMAPX_STORE (store);
1858 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
1860 if (imapx_server == NULL)
1863 if (parent_name == NULL || *parent_name == '\0')
1864 goto check_namespace;
1866 /* Obtain the separator from the parent CamelIMAPXMailbox. */
1868 folder = camel_store_get_folder_sync (
1869 store, parent_name, 0, cancellable, error);
1871 if (folder != NULL) {
1872 parent_mailbox = camel_imapx_folder_list_mailbox (
1873 CAMEL_IMAPX_FOLDER (folder), cancellable, error);
1874 g_object_unref (folder);
1877 if (parent_mailbox == NULL)
1880 separator = camel_imapx_mailbox_get_separator (parent_mailbox);
1881 parent_mailbox_name = camel_imapx_mailbox_get_name (parent_mailbox);
1883 mailbox_name = g_strdup_printf (
1884 "%s%c%s", parent_mailbox_name, separator, folder_name);
1886 g_object_unref (parent_mailbox);
1888 goto check_separator;
1892 /* Obtain the separator from the first personal namespace.
1894 * FIXME The CamelFolder API provides no way to specify a
1895 * namespace prefix when creating a top-level mailbox,
1896 * This needs fixed to properly support IMAP namespaces.
1899 namespace_response = camel_imapx_store_ref_namespaces (imapx_store);
1900 g_return_val_if_fail (namespace_response != NULL, NULL);
1902 list = camel_imapx_namespace_response_list (namespace_response);
1903 g_return_val_if_fail (list != NULL, NULL);
1905 /* The namespace list is in the order received in the NAMESPACE
1906 * response so the first element should be a personal namespace. */
1907 namespace = CAMEL_IMAPX_NAMESPACE (list->data);
1909 separator = camel_imapx_namespace_get_separator (namespace);
1910 namespace_prefix = camel_imapx_namespace_get_prefix (namespace);
1912 mailbox_name = g_strconcat (namespace_prefix, folder_name, NULL);
1914 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1915 g_object_unref (namespace_response);
1919 if (strchr (folder_name, separator) != NULL) {
1921 error, CAMEL_FOLDER_ERROR,
1922 CAMEL_FOLDER_ERROR_INVALID_PATH,
1923 _("The folder name \"%s\" is invalid "
1924 "because it contains the character \"%c\""),
1925 folder_name, separator);
1929 /* This also LISTs the mailbox after creating it, which
1930 * triggers the CamelIMAPXStore::mailbox-created signal
1931 * and all the local processing that goes along with it. */
1932 success = camel_imapx_server_create_mailbox (imapx_server, mailbox_name, cancellable, &local_error);
1934 while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
1935 g_clear_error (&local_error);
1936 g_clear_object (&imapx_server);
1938 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
1940 success = camel_imapx_server_create_mailbox (imapx_server, mailbox_name, cancellable, &local_error);
1944 g_propagate_error (error, local_error);
1947 fi = imapx_store_build_folder_info (
1948 imapx_store, folder_name,
1949 CAMEL_FOLDER_NOCHILDREN);
1953 g_free (mailbox_name);
1955 g_clear_object (&imapx_server);
1961 imapx_store_delete_folder_sync (CamelStore *store,
1962 const gchar *folder_name,
1963 GCancellable *cancellable,
1966 CamelFolder *folder;
1967 CamelIMAPXStore *imapx_store;
1968 CamelIMAPXServer *imapx_server;
1969 CamelIMAPXMailbox *mailbox = NULL;
1970 gboolean success = FALSE;
1971 GError *local_error = NULL;
1973 folder = camel_store_get_folder_sync (
1974 store, folder_name, 0, cancellable, error);
1979 imapx_store = CAMEL_IMAPX_STORE (store);
1980 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
1982 if (imapx_server == NULL)
1985 mailbox = camel_imapx_folder_list_mailbox (
1986 CAMEL_IMAPX_FOLDER (folder), cancellable, error);
1987 if (mailbox == NULL)
1990 success = camel_imapx_server_delete_mailbox (imapx_server, mailbox, cancellable, &local_error);
1992 while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
1993 g_clear_error (&local_error);
1994 g_clear_object (&imapx_server);
1996 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
1998 success = camel_imapx_server_delete_mailbox (imapx_server, mailbox, cancellable, &local_error);
2002 g_propagate_error (error, local_error);
2005 imapx_delete_folder_from_cache (imapx_store, folder_name);
2008 g_clear_object (&folder);
2009 g_clear_object (&mailbox);
2010 g_clear_object (&imapx_server);
2016 imapx_store_rename_folder_sync (CamelStore *store,
2019 GCancellable *cancellable,
2022 CamelFolder *folder;
2023 CamelService *service;
2024 CamelSettings *settings;
2025 CamelIMAPXStore *imapx_store;
2026 CamelIMAPXServer *imapx_server;
2027 CamelIMAPXMailbox *mailbox = NULL;
2028 CamelIMAPXMailbox *cloned_mailbox;
2029 gchar *new_mailbox_name = NULL;
2031 gboolean use_subscriptions;
2032 gboolean success = FALSE;
2033 GError *local_error = NULL;
2035 service = CAMEL_SERVICE (store);
2036 imapx_store = CAMEL_IMAPX_STORE (store);
2038 settings = camel_service_ref_settings (service);
2040 use_subscriptions = camel_imapx_settings_get_use_subscriptions (
2041 CAMEL_IMAPX_SETTINGS (settings));
2043 g_object_unref (settings);
2045 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
2047 if (imapx_server == NULL)
2050 folder = camel_store_get_folder_sync (
2051 store, old, 0, cancellable, error);
2053 if (folder != NULL) {
2054 mailbox = camel_imapx_folder_list_mailbox (
2055 CAMEL_IMAPX_FOLDER (folder), cancellable, error);
2056 g_object_unref (folder);
2059 if (mailbox == NULL)
2062 /* Assume the renamed mailbox will remain in the same namespace,
2063 * and therefore use the same separator character. XXX I'm not
2064 * sure if IMAP even allows inter-namespace mailbox renames. */
2065 separator = camel_imapx_mailbox_get_separator (mailbox);
2066 new_mailbox_name = camel_imapx_folder_path_to_mailbox (new, separator);
2068 if (use_subscriptions) {
2069 success = camel_imapx_server_unsubscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
2071 while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
2072 g_clear_error (&local_error);
2073 g_clear_object (&imapx_server);
2075 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
2077 success = camel_imapx_server_unsubscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
2080 g_clear_error (&local_error);
2083 success = camel_imapx_server_rename_mailbox (imapx_server, mailbox, new_mailbox_name, cancellable, &local_error);
2085 while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
2086 g_clear_error (&local_error);
2087 g_clear_object (&imapx_server);
2089 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
2091 success = camel_imapx_server_rename_mailbox (imapx_server, mailbox, new_mailbox_name, cancellable, &local_error);
2096 g_propagate_error (error, local_error);
2099 if (use_subscriptions) {
2102 success_2 = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
2104 while (!success_2 && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
2105 g_clear_error (&local_error);
2106 g_clear_object (&imapx_server);
2108 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
2110 success_2 = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
2113 g_clear_error (&local_error);
2118 /* Rename summary, and handle broken server. */
2119 imapx_store_rename_folder_info (imapx_store, old, new);
2120 imapx_store_rename_storage_path (imapx_store, old, new);
2122 /* Create a cloned CamelIMAPXMailbox with the new mailbox name. */
2123 cloned_mailbox = camel_imapx_mailbox_clone (mailbox, new_mailbox_name);
2125 camel_imapx_folder_set_mailbox (
2126 CAMEL_IMAPX_FOLDER (folder), cloned_mailbox);
2128 if (use_subscriptions) {
2129 success = camel_imapx_server_subscribe_mailbox (imapx_server, cloned_mailbox, cancellable, &local_error);
2131 while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
2132 g_clear_error (&local_error);
2133 g_clear_object (&imapx_server);
2135 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
2137 success = camel_imapx_server_subscribe_mailbox (imapx_server, cloned_mailbox, cancellable, &local_error);
2141 g_propagate_error (error, local_error);
2144 g_clear_object (&cloned_mailbox);
2147 g_free (new_mailbox_name);
2149 g_clear_object (&mailbox);
2150 g_clear_object (&imapx_server);
2156 imapx_migrate_to_user_cache_dir (CamelService *service)
2158 const gchar *user_data_dir, *user_cache_dir;
2160 g_return_if_fail (service != NULL);
2161 g_return_if_fail (CAMEL_IS_SERVICE (service));
2163 user_data_dir = camel_service_get_user_data_dir (service);
2164 user_cache_dir = camel_service_get_user_cache_dir (service);
2166 g_return_if_fail (user_data_dir != NULL);
2167 g_return_if_fail (user_cache_dir != NULL);
2169 /* migrate only if the source directory exists and the destination doesn't */
2170 if (g_file_test (user_data_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) &&
2171 !g_file_test (user_cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
2174 parent_dir = g_path_get_dirname (user_cache_dir);
2175 g_mkdir_with_parents (parent_dir, S_IRWXU);
2176 g_free (parent_dir);
2178 if (g_rename (user_data_dir, user_cache_dir) == -1)
2179 g_debug ("%s: Failed to migrate '%s' to '%s': %s", G_STRFUNC, user_data_dir, user_cache_dir, g_strerror (errno));
2184 imapx_store_initable_init (GInitable *initable,
2185 GCancellable *cancellable,
2188 CamelIMAPXStore *imapx_store;
2190 CamelService *service;
2191 const gchar *user_cache_dir;
2194 imapx_store = CAMEL_IMAPX_STORE (initable);
2195 store = CAMEL_STORE (initable);
2196 service = CAMEL_SERVICE (initable);
2198 store->flags |= CAMEL_STORE_USE_CACHE_DIR;
2199 imapx_migrate_to_user_cache_dir (service);
2201 /* Chain up to parent interface's init() method. */
2202 if (!parent_initable_interface->init (initable, cancellable, error))
2205 service = CAMEL_SERVICE (initable);
2206 user_cache_dir = camel_service_get_user_cache_dir (service);
2208 imapx_store->summary =
2209 g_object_new (CAMEL_TYPE_IMAPX_STORE_SUMMARY, NULL);
2211 summary = g_build_filename (user_cache_dir, ".ev-store-summary", NULL);
2212 camel_store_summary_set_filename (imapx_store->summary, summary);
2213 if (camel_store_summary_load (imapx_store->summary) == -1) {
2214 camel_store_summary_touch (imapx_store->summary);
2215 camel_store_summary_save (imapx_store->summary);
2223 static const gchar *
2224 imapx_store_get_service_name (CamelNetworkService *service,
2225 CamelNetworkSecurityMethod method)
2227 const gchar *service_name;
2230 case CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT:
2231 service_name = "imaps";
2235 service_name = "imap";
2239 return service_name;
2243 imapx_store_get_default_port (CamelNetworkService *service,
2244 CamelNetworkSecurityMethod method)
2246 guint16 default_port;
2249 case CAMEL_NETWORK_SECURITY_METHOD_SSL_ON_ALTERNATE_PORT:
2250 default_port = IMAPS_PORT;
2254 default_port = IMAP_PORT;
2258 return default_port;
2262 imapx_store_folder_is_subscribed (CamelSubscribable *subscribable,
2263 const gchar *folder_name)
2265 CamelIMAPXStore *imapx_store;
2267 gint is_subscribed = FALSE;
2269 imapx_store = CAMEL_IMAPX_STORE (subscribable);
2271 if (folder_name && *folder_name == '/')
2274 si = camel_store_summary_path (imapx_store->summary, folder_name);
2276 if (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)
2277 is_subscribed = TRUE;
2278 camel_store_summary_info_unref (imapx_store->summary, si);
2281 return is_subscribed;
2285 imapx_ensure_parents_subscribed (CamelIMAPXStore *imapx_store,
2286 const gchar *folder_name)
2288 GSList *parents = NULL, *iter;
2289 CamelSubscribable *subscribable;
2290 CamelFolderInfo *fi;
2291 gchar *parent, *sep;
2293 g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
2294 g_return_if_fail (folder_name != NULL);
2296 subscribable = CAMEL_SUBSCRIBABLE (imapx_store);
2298 if (folder_name && *folder_name == '/')
2301 parent = g_strdup (folder_name);
2302 while (sep = strrchr (parent, '/'), sep) {
2305 fi = camel_folder_info_new ();
2307 fi->display_name = strrchr (parent, '/');
2308 if (fi->display_name != NULL)
2309 fi->display_name = g_strdup (fi->display_name + 1);
2311 fi->display_name = g_strdup (parent);
2313 fi->full_name = g_strdup (parent);
2315 /* Since this is a "fake" folder node, it is not selectable. */
2316 fi->flags |= CAMEL_FOLDER_NOSELECT;
2318 parents = g_slist_prepend (parents, fi);
2321 for (iter = parents; iter; iter = g_slist_next (iter)) {
2324 camel_subscribable_folder_subscribed (subscribable, fi);
2325 camel_folder_info_free (fi);
2330 imapx_store_subscribe_folder_sync (CamelSubscribable *subscribable,
2331 const gchar *folder_name,
2332 GCancellable *cancellable,
2335 CamelFolder *folder;
2336 CamelIMAPXStore *imapx_store;
2337 CamelIMAPXServer *imapx_server;
2338 CamelIMAPXMailbox *mailbox = NULL;
2339 gboolean success = FALSE;
2340 GError *local_error = NULL;
2342 imapx_store = CAMEL_IMAPX_STORE (subscribable);
2343 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
2345 if (imapx_server == NULL)
2348 folder = camel_store_get_folder_sync (
2349 CAMEL_STORE (subscribable),
2350 folder_name, 0, cancellable, error);
2352 if (folder != NULL) {
2353 mailbox = camel_imapx_folder_list_mailbox (
2354 CAMEL_IMAPX_FOLDER (folder), cancellable, error);
2355 g_object_unref (folder);
2358 if (mailbox == NULL)
2361 success = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
2363 while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
2364 g_clear_error (&local_error);
2365 g_clear_object (&imapx_server);
2367 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
2369 success = camel_imapx_server_subscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
2373 g_propagate_error (error, local_error);
2376 CamelFolderInfo *fi;
2378 /* without this the folder is not visible if parents are not subscribed */
2379 imapx_ensure_parents_subscribed (imapx_store, folder_name);
2381 fi = imapx_store_build_folder_info (
2382 CAMEL_IMAPX_STORE (subscribable), folder_name, 0);
2383 camel_subscribable_folder_subscribed (subscribable, fi);
2384 camel_folder_info_free (fi);
2388 g_clear_object (&mailbox);
2389 g_clear_object (&imapx_server);
2395 imapx_store_unsubscribe_folder_sync (CamelSubscribable *subscribable,
2396 const gchar *folder_name,
2397 GCancellable *cancellable,
2400 CamelFolder *folder;
2401 CamelIMAPXStore *imapx_store;
2402 CamelIMAPXServer *imapx_server;
2403 CamelIMAPXMailbox *mailbox = NULL;
2404 gboolean success = FALSE;
2405 GError *local_error = NULL;
2407 imapx_store = CAMEL_IMAPX_STORE (subscribable);
2408 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, error);
2410 if (imapx_server == NULL)
2413 folder = camel_store_get_folder_sync (
2414 CAMEL_STORE (subscribable),
2415 folder_name, 0, cancellable, error);
2417 if (folder != NULL) {
2418 mailbox = camel_imapx_folder_list_mailbox (
2419 CAMEL_IMAPX_FOLDER (folder), cancellable, error);
2420 g_object_unref (folder);
2423 if (mailbox == NULL)
2426 success = camel_imapx_server_unsubscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
2428 while (!success && g_error_matches (local_error, CAMEL_IMAPX_SERVER_ERROR, CAMEL_IMAPX_SERVER_ERROR_TRY_RECONNECT)) {
2429 g_clear_error (&local_error);
2430 g_clear_object (&imapx_server);
2432 imapx_server = camel_imapx_store_ref_server (imapx_store, NULL, FALSE, cancellable, &local_error);
2434 success = camel_imapx_server_unsubscribe_mailbox (imapx_server, mailbox, cancellable, &local_error);
2438 g_propagate_error (error, local_error);
2441 CamelFolderInfo *fi;
2443 fi = imapx_store_build_folder_info (
2444 CAMEL_IMAPX_STORE (subscribable), folder_name, 0);
2445 camel_subscribable_folder_unsubscribed (subscribable, fi);
2446 camel_folder_info_free (fi);
2450 g_clear_object (&mailbox);
2451 g_clear_object (&imapx_server);
2457 imapx_store_mailbox_created (CamelIMAPXStore *imapx_store,
2458 CamelIMAPXMailbox *mailbox)
2462 "%s::mailbox-created (\"%s\")\n",
2463 G_OBJECT_TYPE_NAME (imapx_store),
2464 camel_imapx_mailbox_get_name (mailbox));
2466 imapx_store_add_mailbox_to_folder (imapx_store, mailbox);
2467 imapx_store_process_mailbox_attributes (imapx_store, mailbox, NULL);
2471 imapx_store_mailbox_renamed (CamelIMAPXStore *imapx_store,
2472 CamelIMAPXMailbox *mailbox,
2473 const gchar *oldname)
2477 "%s::mailbox-renamed (\"%s\" -> \"%s\")\n",
2478 G_OBJECT_TYPE_NAME (imapx_store), oldname,
2479 camel_imapx_mailbox_get_name (mailbox));
2481 imapx_store_process_mailbox_attributes (imapx_store, mailbox, oldname);
2482 imapx_store_process_mailbox_status (imapx_store, mailbox);
2486 imapx_store_mailbox_updated (CamelIMAPXStore *imapx_store,
2487 CamelIMAPXMailbox *mailbox)
2491 "%s::mailbox-updated (\"%s\")\n",
2492 G_OBJECT_TYPE_NAME (imapx_store),
2493 camel_imapx_mailbox_get_name (mailbox));
2495 imapx_store_process_mailbox_attributes (imapx_store, mailbox, NULL);
2496 imapx_store_process_mailbox_status (imapx_store, mailbox);
2500 camel_imapx_store_class_init (CamelIMAPXStoreClass *class)
2502 GObjectClass *object_class;
2503 CamelServiceClass *service_class;
2504 CamelStoreClass *store_class;
2506 g_type_class_add_private (class, sizeof (CamelIMAPXStorePrivate));
2508 object_class = G_OBJECT_CLASS (class);
2509 object_class->set_property = imapx_store_set_property;
2510 object_class->get_property = imapx_store_get_property;
2511 object_class->dispose = imapx_store_dispose;
2512 object_class->finalize = imapx_store_finalize;
2513 object_class->notify = imapx_store_notify;
2515 service_class = CAMEL_SERVICE_CLASS (class);
2516 service_class->settings_type = CAMEL_TYPE_IMAPX_SETTINGS;
2517 service_class->get_name = imapx_get_name;
2518 service_class->connect_sync = imapx_connect_sync;
2519 service_class->disconnect_sync = imapx_disconnect_sync;
2520 service_class->authenticate_sync = imapx_authenticate_sync;
2521 service_class->query_auth_types_sync = imapx_query_auth_types_sync;
2523 store_class = CAMEL_STORE_CLASS (class);
2524 store_class->hash_folder_name = imapx_name_hash;
2525 store_class->equal_folder_name = imapx_name_equal;
2526 store_class->can_refresh_folder = imapx_can_refresh_folder;
2527 store_class->get_folder_sync = imapx_store_get_folder_sync;
2528 store_class->get_folder_info_sync = imapx_store_get_folder_info_sync;
2529 store_class->get_junk_folder_sync = imapx_store_get_junk_folder_sync;
2530 store_class->get_trash_folder_sync = imapx_store_get_trash_folder_sync;
2531 store_class->create_folder_sync = imapx_store_create_folder_sync;
2532 store_class->delete_folder_sync = imapx_store_delete_folder_sync;
2533 store_class->rename_folder_sync = imapx_store_rename_folder_sync;
2535 class->mailbox_created = imapx_store_mailbox_created;
2536 class->mailbox_renamed = imapx_store_mailbox_renamed;
2537 class->mailbox_updated = imapx_store_mailbox_updated;
2539 /* Inherited from CamelNetworkService. */
2540 g_object_class_override_property (
2545 /* Inherited from CamelNetworkService. */
2546 g_object_class_override_property (
2548 PROP_HOST_REACHABLE,
2551 signals[MAILBOX_CREATED] = g_signal_new (
2553 G_OBJECT_CLASS_TYPE (class),
2555 G_STRUCT_OFFSET (CamelIMAPXStoreClass, mailbox_created),
2558 CAMEL_TYPE_IMAPX_MAILBOX);
2560 signals[MAILBOX_RENAMED] = g_signal_new (
2562 G_OBJECT_CLASS_TYPE (class),
2564 G_STRUCT_OFFSET (CamelIMAPXStoreClass, mailbox_renamed),
2567 CAMEL_TYPE_IMAPX_MAILBOX,
2570 signals[MAILBOX_UPDATED] = g_signal_new (
2572 G_OBJECT_CLASS_TYPE (class),
2574 G_STRUCT_OFFSET (CamelIMAPXStoreClass, mailbox_updated),
2577 CAMEL_TYPE_IMAPX_MAILBOX);
2581 camel_imapx_store_initable_init (GInitableIface *iface)
2583 parent_initable_interface = g_type_interface_peek_parent (iface);
2585 iface->init = imapx_store_initable_init;
2589 camel_network_service_init (CamelNetworkServiceInterface *iface)
2591 iface->get_service_name = imapx_store_get_service_name;
2592 iface->get_default_port = imapx_store_get_default_port;
2596 camel_subscribable_init (CamelSubscribableInterface *iface)
2598 iface->folder_is_subscribed = imapx_store_folder_is_subscribed;
2599 iface->subscribe_folder_sync = imapx_store_subscribe_folder_sync;
2600 iface->unsubscribe_folder_sync = imapx_store_unsubscribe_folder_sync;
2604 camel_imapx_store_init (CamelIMAPXStore *store)
2606 store->priv = CAMEL_IMAPX_STORE_GET_PRIVATE (store);
2608 store->priv->con_man = camel_imapx_conn_manager_new (CAMEL_STORE (store));
2610 g_mutex_init (&store->priv->get_finfo_lock);
2612 g_mutex_init (&store->priv->namespaces_lock);
2613 g_mutex_init (&store->priv->mailboxes_lock);
2614 /* Hash table key is owned by the CamelIMAPXMailbox. */
2615 store->priv->mailboxes = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
2617 /* Initialize to zero to ensure we always obtain fresh folder
2618 * info on startup. See imapx_store_get_folder_info_sync(). */
2619 store->priv->last_refresh_time = 0;
2621 g_mutex_init (&store->priv->server_lock);
2623 store->priv->quota_info = g_hash_table_new_full (
2624 (GHashFunc) g_str_hash,
2625 (GEqualFunc) g_str_equal,
2626 (GDestroyNotify) g_free,
2627 (GDestroyNotify) camel_folder_quota_info_free);
2628 g_mutex_init (&store->priv->quota_info_lock);
2630 g_mutex_init (&store->priv->settings_lock);
2632 imapx_utils_init ();
2635 store, "notify::settings",
2636 G_CALLBACK (imapx_store_update_store_flags), NULL);
2640 * camel_imapx_store_ref_server:
2641 * @store: a #CamelIMAPXStore
2642 * @folder_name: name of a folder, for which it'll be used; can be %NULL
2643 * @cancellable: a #GCancellable to use ofr possible new connection creation, or %NULL
2644 * @error: return location for a #GError, or %NULL
2646 * Returns the #CamelIMAPXServer for @store, if available.
2648 * As a convenience, if the @store is not currently connected to an IMAP
2649 * server, the function sets @error to %CAMEL_SERVER_ERROR_UNAVAILABLE and
2650 * returns %NULL. If an operation can possibly be executed while offline,
2651 * pass %NULL for @error.
2653 * The returned #CamelIMAPXServer is referenced for thread-safety and must
2654 * be unreferenced with g_object_unref() when finished with it.
2656 * Returns: a #CamelIMAPXServer, or %NULL
2661 camel_imapx_store_ref_server (CamelIMAPXStore *store,
2662 const gchar *folder_name,
2663 gboolean for_expensive_job,
2664 GCancellable *cancellable,
2667 CamelIMAPXServer *server = NULL;
2669 g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (store), NULL);
2671 server = camel_imapx_conn_manager_get_connection (
2672 store->priv->con_man, folder_name, for_expensive_job, cancellable, error);
2674 if (!server && error && !*error) {
2676 error, CAMEL_SERVICE_ERROR,
2677 CAMEL_SERVICE_ERROR_UNAVAILABLE,
2678 _("You must be working online "
2679 "to complete this operation"));
2685 /* The caller should hold the store->priv->server_lock already, when calling this */
2687 camel_imapx_store_set_connecting_server (CamelIMAPXStore *store,
2688 CamelIMAPXServer *server,
2689 gboolean is_concurrent_connection)
2691 g_return_if_fail (CAMEL_IS_IMAPX_STORE (store));
2694 g_return_if_fail (CAMEL_IS_IMAPX_SERVER (server));
2696 g_mutex_lock (&store->priv->server_lock);
2698 if (store->priv->connecting_server != server) {
2699 g_clear_object (&store->priv->connecting_server);
2701 store->priv->connecting_server = g_object_ref (server);
2704 store->priv->is_concurrent_connection = is_concurrent_connection;
2706 g_mutex_unlock (&store->priv->server_lock);
2710 camel_imapx_store_is_connecting_concurrent_connection (CamelIMAPXStore *imapx_store)
2714 g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), FALSE);
2716 g_mutex_lock (&imapx_store->priv->server_lock);
2717 res = imapx_store->priv->is_concurrent_connection;
2718 g_mutex_unlock (&imapx_store->priv->server_lock);
2724 camel_imapx_store_folder_op_done (CamelIMAPXStore *store,
2725 CamelIMAPXServer *server,
2726 const gchar *folder_name)
2728 g_return_if_fail (CAMEL_IS_IMAPX_STORE (store));
2729 g_return_if_fail (CAMEL_IS_IMAPX_SERVER (server));
2730 g_return_if_fail (folder_name != NULL);
2732 camel_imapx_conn_manager_update_con_info (
2733 store->priv->con_man, server, folder_name);
2737 * camel_imapx_store_ref_namespaces:
2738 * @imapx_store: a #CamelIMAPXStore
2740 * Returns the #CamelIMAPXNamespaceResponse for @is. This is obtained
2741 * during the connection phase if the IMAP server lists the "NAMESPACE"
2742 * keyword in its CAPABILITY response, or else is fabricated from the
2743 * first LIST response.
2745 * The returned #CamelIMAPXNamespaceResponse is reference for thread-safety
2746 * and must be unreferenced with g_object_unref() when finished with it.
2748 * Returns: a #CamelIMAPXNamespaceResponse
2752 CamelIMAPXNamespaceResponse *
2753 camel_imapx_store_ref_namespaces (CamelIMAPXStore *imapx_store)
2755 CamelIMAPXNamespaceResponse *namespaces = NULL;
2757 g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), NULL);
2759 g_mutex_lock (&imapx_store->priv->namespaces_lock);
2761 if (imapx_store->priv->namespaces != NULL)
2762 namespaces = g_object_ref (imapx_store->priv->namespaces);
2764 g_mutex_unlock (&imapx_store->priv->namespaces_lock);
2770 camel_imapx_store_set_namespaces (CamelIMAPXStore *imapx_store,
2771 CamelIMAPXNamespaceResponse *namespaces)
2773 g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
2775 g_return_if_fail (CAMEL_IS_IMAPX_NAMESPACE_RESPONSE (namespaces));
2778 g_object_ref (namespaces);
2780 g_mutex_lock (&imapx_store->priv->namespaces_lock);
2782 g_clear_object (&imapx_store->priv->namespaces);
2783 imapx_store->priv->namespaces = namespaces;
2785 g_mutex_unlock (&imapx_store->priv->namespaces_lock);
2789 imapx_store_add_mailbox_unlocked (CamelIMAPXStore *imapx_store,
2790 CamelIMAPXMailbox *mailbox)
2792 const gchar *mailbox_name;
2794 /* Acquire "mailboxes_lock" before calling. */
2796 mailbox_name = camel_imapx_mailbox_get_name (mailbox);
2797 g_return_if_fail (mailbox_name != NULL);
2799 /* Use g_hash_table_replace() here instead of g_hash_table_insert().
2800 * The hash table key is owned by the hash table value, so if we're
2801 * replacing an existing table item we want to replace both the key
2802 * and value to avoid data corruption. */
2803 g_hash_table_replace (
2804 imapx_store->priv->mailboxes,
2805 (gpointer) mailbox_name,
2806 g_object_ref (mailbox));
2810 imapx_store_remove_mailbox_unlocked (CamelIMAPXStore *imapx_store,
2811 CamelIMAPXMailbox *mailbox)
2813 const gchar *mailbox_name;
2815 /* Acquire "mailboxes_lock" before calling. */
2817 mailbox_name = camel_imapx_mailbox_get_name (mailbox);
2818 g_return_val_if_fail (mailbox_name != NULL, FALSE);
2820 return g_hash_table_remove (imapx_store->priv->mailboxes, mailbox_name);
2823 static CamelIMAPXMailbox *
2824 imapx_store_ref_mailbox_unlocked (CamelIMAPXStore *imapx_store,
2825 const gchar *mailbox_name)
2827 CamelIMAPXMailbox *mailbox;
2829 /* Acquire "mailboxes_lock" before calling. */
2831 g_return_val_if_fail (mailbox_name != NULL, NULL);
2833 /* The INBOX mailbox is case-insensitive. */
2834 if (g_ascii_strcasecmp (mailbox_name, "INBOX") == 0)
2835 mailbox_name = "INBOX";
2837 mailbox = g_hash_table_lookup (imapx_store->priv->mailboxes, mailbox_name);
2839 /* Remove non-existent mailboxes as we find them. */
2840 if (mailbox != NULL && !camel_imapx_mailbox_exists (mailbox)) {
2841 imapx_store_remove_mailbox_unlocked (imapx_store, mailbox);
2845 if (mailbox != NULL)
2846 g_object_ref (mailbox);
2852 imapx_store_list_mailboxes_unlocked (CamelIMAPXStore *imapx_store,
2853 CamelIMAPXNamespace *namespace,
2854 const gchar *pattern)
2856 GHashTableIter iter;
2860 /* Acquire "mailboxes_lock" before calling. */
2862 if (pattern == NULL)
2865 g_hash_table_iter_init (&iter, imapx_store->priv->mailboxes);
2867 while (g_hash_table_iter_next (&iter, NULL, &value)) {
2868 CamelIMAPXMailbox *mailbox;
2869 CamelIMAPXNamespace *mailbox_ns;
2871 mailbox = CAMEL_IMAPX_MAILBOX (value);
2872 mailbox_ns = camel_imapx_mailbox_get_namespace (mailbox);
2874 if (!camel_imapx_mailbox_exists (mailbox))
2877 if (!camel_imapx_namespace_equal (namespace, mailbox_ns))
2880 if (!camel_imapx_mailbox_matches (mailbox, pattern))
2883 list = g_list_prepend (list, g_object_ref (mailbox));
2886 /* Sort the list by mailbox name. */
2887 return g_list_sort (list, (GCompareFunc) camel_imapx_mailbox_compare);
2890 static CamelIMAPXMailbox *
2891 imapx_store_create_mailbox_unlocked (CamelIMAPXStore *imapx_store,
2892 CamelIMAPXListResponse *response)
2894 CamelIMAPXNamespaceResponse *namespace_response;
2895 CamelIMAPXNamespace *namespace;
2896 CamelIMAPXMailbox *mailbox = NULL;
2897 const gchar *mailbox_name;
2900 /* Acquire "mailboxes_lock" before calling. */
2902 namespace_response = camel_imapx_store_ref_namespaces (imapx_store);
2903 g_return_val_if_fail (namespace_response != NULL, FALSE);
2905 mailbox_name = camel_imapx_list_response_get_mailbox_name (response);
2906 separator = camel_imapx_list_response_get_separator (response);
2908 namespace = camel_imapx_namespace_response_lookup (
2909 namespace_response, mailbox_name, separator);
2911 if (namespace != NULL) {
2912 mailbox = camel_imapx_mailbox_new (response, namespace);
2913 imapx_store_add_mailbox_unlocked (imapx_store, mailbox);
2914 g_object_unref (namespace);
2916 /* XXX Slight hack, mainly for Courier servers. If INBOX does
2917 * not match any defined namespace, just create one for it
2918 * on the fly. The namespace response won't know about it. */
2919 } else if (camel_imapx_mailbox_is_inbox (mailbox_name)) {
2920 namespace = camel_imapx_namespace_new (
2921 CAMEL_IMAPX_NAMESPACE_PERSONAL, "", separator);
2922 mailbox = camel_imapx_mailbox_new (response, namespace);
2923 imapx_store_add_mailbox_unlocked (imapx_store, mailbox);
2924 g_object_unref (namespace);
2928 "%s: No matching namespace for \"%c\" %s",
2929 G_STRFUNC, separator, mailbox_name);
2932 g_object_unref (namespace_response);
2937 static CamelIMAPXMailbox *
2938 imapx_store_rename_mailbox_unlocked (CamelIMAPXStore *imapx_store,
2939 const gchar *old_mailbox_name,
2940 const gchar *new_mailbox_name)
2942 CamelIMAPXMailbox *old_mailbox;
2943 CamelIMAPXMailbox *new_mailbox;
2944 CamelIMAPXNamespace *namespace;
2945 gsize old_mailbox_name_length;
2950 /* Acquire "mailboxes_lock" before calling. */
2952 g_return_val_if_fail (old_mailbox_name != NULL, NULL);
2953 g_return_val_if_fail (new_mailbox_name != NULL, NULL);
2955 old_mailbox = imapx_store_ref_mailbox_unlocked (imapx_store, old_mailbox_name);
2956 if (old_mailbox == NULL)
2959 old_mailbox_name_length = strlen (old_mailbox_name);
2960 namespace = camel_imapx_mailbox_get_namespace (old_mailbox);
2961 separator = camel_imapx_mailbox_get_separator (old_mailbox);
2963 new_mailbox = camel_imapx_mailbox_clone (old_mailbox, new_mailbox_name);
2965 /* Add the new mailbox, remove the old mailbox.
2966 * Note we still have a reference on the old mailbox. */
2967 imapx_store_add_mailbox_unlocked (imapx_store, new_mailbox);
2968 imapx_store_remove_mailbox_unlocked (imapx_store, old_mailbox);
2970 /* Rename any child mailboxes. */
2972 pattern = g_strdup_printf ("%s%c*", old_mailbox_name, separator);
2973 list = imapx_store_list_mailboxes_unlocked (imapx_store, namespace, pattern);
2975 for (link = list; link != NULL; link = g_list_next (link)) {
2976 CamelIMAPXMailbox *old_child;
2977 CamelIMAPXMailbox *new_child;
2978 const gchar *old_child_name;
2979 gchar *new_child_name;
2981 old_child = CAMEL_IMAPX_MAILBOX (link->data);
2982 old_child_name = camel_imapx_mailbox_get_name (old_child);
2984 /* Sanity checks. */
2986 old_child_name != NULL &&
2987 strlen (old_child_name) > old_mailbox_name_length &&
2988 old_child_name[old_mailbox_name_length] == separator);
2990 new_child_name = g_strconcat (
2992 old_child_name + old_mailbox_name_length, NULL);
2993 new_child = camel_imapx_mailbox_clone (
2994 old_child, new_child_name);
2996 /* Add the new mailbox, remove the old mailbox.
2997 * Note we still have a reference on the old mailbox. */
2998 imapx_store_add_mailbox_unlocked (imapx_store, new_child);
2999 imapx_store_remove_mailbox_unlocked (imapx_store, old_child);
3001 g_object_unref (new_child);
3002 g_free (new_child_name);
3005 g_list_free_full (list, (GDestroyNotify) g_object_unref);
3008 g_object_unref (old_mailbox);
3014 * camel_imapx_store_ref_mailbox:
3015 * @imapx_store: a #CamelIMAPXStore
3016 * @mailbox_name: a mailbox name
3018 * Looks up a #CamelMailbox by its name. If no match is found, the function
3021 * The returned #CamelIMAPXMailbox is referenced for thread-safety and
3022 * should be unreferenced with g_object_unref() when finished with it.
3024 * Returns: a #CamelIMAPXMailbox, or %NULL
3029 camel_imapx_store_ref_mailbox (CamelIMAPXStore *imapx_store,
3030 const gchar *mailbox_name)
3032 CamelIMAPXMailbox *mailbox;
3034 g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), NULL);
3035 g_return_val_if_fail (mailbox_name != NULL, NULL);
3037 g_mutex_lock (&imapx_store->priv->mailboxes_lock);
3039 mailbox = imapx_store_ref_mailbox_unlocked (imapx_store, mailbox_name);
3041 g_mutex_unlock (&imapx_store->priv->mailboxes_lock);
3047 * camel_imapx_store_list_mailboxes:
3048 * @imapx_store: a #CamelIMAPXStore
3049 * @namespace_: a #CamelIMAPXNamespace
3050 * @pattern: mailbox name with possible wildcards, or %NULL
3052 * Returns a list of #CamelIMAPXMailbox instances which match @namespace and
3053 * @pattern. The @pattern may contain wildcard characters '*' and '%', which
3054 * are interpreted similar to the IMAP LIST command. A %NULL @pattern lists
3055 * all mailboxes in @namespace; equivalent to passing "*".
3057 * The mailboxes returned in the list are referenced for thread-safety.
3058 * They must each be unreferenced with g_object_unref() when finished with
3059 * them. Free the returned list itself with g_list_free().
3061 * An easy way to free the list properly in one step is as follows:
3064 * g_list_free_full (list, g_object_unref);
3067 * Returns: a list of #CamelIMAPXMailbox instances
3072 camel_imapx_store_list_mailboxes (CamelIMAPXStore *imapx_store,
3073 CamelIMAPXNamespace *namespace,
3074 const gchar *pattern)
3078 g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), NULL);
3079 g_return_val_if_fail (CAMEL_IS_IMAPX_NAMESPACE (namespace), NULL);
3081 g_mutex_lock (&imapx_store->priv->mailboxes_lock);
3083 list = imapx_store_list_mailboxes_unlocked (imapx_store, namespace, pattern);
3085 g_mutex_unlock (&imapx_store->priv->mailboxes_lock);
3091 camel_imapx_store_emit_mailbox_updated (CamelIMAPXStore *imapx_store,
3092 CamelIMAPXMailbox *mailbox)
3094 g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
3095 g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (mailbox));
3097 g_signal_emit (imapx_store, signals[MAILBOX_UPDATED], 0, mailbox);
3101 camel_imapx_store_handle_mailbox_rename (CamelIMAPXStore *imapx_store,
3102 CamelIMAPXMailbox *old_mailbox,
3103 const gchar *new_mailbox_name)
3105 CamelIMAPXMailbox *new_mailbox;
3106 const gchar *old_mailbox_name;
3108 g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
3109 g_return_if_fail (CAMEL_IS_IMAPX_MAILBOX (old_mailbox));
3110 g_return_if_fail (new_mailbox_name != NULL);
3112 old_mailbox_name = camel_imapx_mailbox_get_name (old_mailbox);
3114 g_mutex_lock (&imapx_store->priv->mailboxes_lock);
3115 new_mailbox = imapx_store_rename_mailbox_unlocked (
3116 imapx_store, old_mailbox_name, new_mailbox_name);
3117 g_mutex_unlock (&imapx_store->priv->mailboxes_lock);
3119 g_warn_if_fail (new_mailbox != NULL);
3122 imapx_store, signals[MAILBOX_RENAMED], 0,
3123 new_mailbox, old_mailbox_name);
3125 g_clear_object (&new_mailbox);
3129 camel_imapx_store_handle_list_response (CamelIMAPXStore *imapx_store,
3130 CamelIMAPXServer *imapx_server,
3131 CamelIMAPXListResponse *response)
3133 CamelIMAPXMailbox *mailbox = NULL;
3134 const gchar *mailbox_name;
3135 const gchar *old_mailbox_name;
3136 gboolean emit_mailbox_created = FALSE;
3137 gboolean emit_mailbox_renamed = FALSE;
3138 gboolean emit_mailbox_updated = FALSE;
3140 g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
3141 g_return_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server));
3142 g_return_if_fail (CAMEL_IS_IMAPX_LIST_RESPONSE (response));
3144 mailbox_name = camel_imapx_list_response_get_mailbox_name (response);
3145 old_mailbox_name = camel_imapx_list_response_get_oldname (response);
3147 /* Fabricate a CamelIMAPXNamespaceResponse if the server lacks the
3148 * NAMESPACE capability and this is the first LIST / LSUB response. */
3149 if (CAMEL_IMAPX_LACK_CAPABILITY (imapx_server->cinfo, NAMESPACE)) {
3150 g_mutex_lock (&imapx_store->priv->namespaces_lock);
3151 if (imapx_store->priv->namespaces == NULL) {
3152 imapx_store->priv->namespaces = camel_imapx_namespace_response_faux_new (response);
3154 g_mutex_unlock (&imapx_store->priv->namespaces_lock);
3157 /* Create, rename, or update a corresponding CamelIMAPXMailbox. */
3158 g_mutex_lock (&imapx_store->priv->mailboxes_lock);
3159 if (old_mailbox_name != NULL) {
3160 mailbox = imapx_store_rename_mailbox_unlocked (
3161 imapx_store, old_mailbox_name, mailbox_name);
3162 emit_mailbox_renamed = (mailbox != NULL);
3164 if (mailbox == NULL) {
3165 mailbox = imapx_store_ref_mailbox_unlocked (imapx_store, mailbox_name);
3166 emit_mailbox_updated = (mailbox != NULL);
3168 if (mailbox == NULL) {
3169 mailbox = imapx_store_create_mailbox_unlocked (imapx_store, response);
3170 emit_mailbox_created = (mailbox != NULL);
3172 camel_imapx_mailbox_handle_list_response (mailbox, response);
3174 g_mutex_unlock (&imapx_store->priv->mailboxes_lock);
3176 if (emit_mailbox_created)
3177 g_signal_emit (imapx_store, signals[MAILBOX_CREATED], 0, mailbox);
3179 if (emit_mailbox_renamed)
3181 imapx_store, signals[MAILBOX_RENAMED], 0,
3182 mailbox, old_mailbox_name);
3184 if (emit_mailbox_updated)
3185 g_signal_emit (imapx_store, signals[MAILBOX_UPDATED], 0, mailbox);
3187 g_clear_object (&mailbox);
3191 camel_imapx_store_handle_lsub_response (CamelIMAPXStore *imapx_store,
3192 CamelIMAPXServer *imapx_server,
3193 CamelIMAPXListResponse *response)
3195 CamelIMAPXMailbox *mailbox;
3196 const gchar *mailbox_name;
3197 gboolean emit_mailbox_updated = FALSE;
3199 g_return_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store));
3200 g_return_if_fail (CAMEL_IS_IMAPX_SERVER (imapx_server));
3201 g_return_if_fail (CAMEL_IS_IMAPX_LIST_RESPONSE (response));
3203 mailbox_name = camel_imapx_list_response_get_mailbox_name (response);
3205 /* Fabricate a CamelIMAPXNamespaceResponse if the server lacks the
3206 * NAMESPACE capability and this is the first LIST / LSUB response. */
3207 if (CAMEL_IMAPX_LACK_CAPABILITY (imapx_server->cinfo, NAMESPACE)) {
3208 g_mutex_lock (&imapx_store->priv->namespaces_lock);
3209 if (imapx_store->priv->namespaces == NULL) {
3210 imapx_store->priv->namespaces = camel_imapx_namespace_response_faux_new (response);
3212 g_mutex_unlock (&imapx_store->priv->namespaces_lock);
3215 /* Update a corresponding CamelIMAPXMailbox.
3217 * Note, don't create the CamelIMAPXMailbox like we do for a LIST
3218 * response. We always issue LIST before LSUB on a mailbox name,
3219 * so if we don't already have a CamelIMAPXMailbox instance then
3220 * this is a subscription on a non-existent mailbox. Skip it. */
3221 g_mutex_lock (&imapx_store->priv->mailboxes_lock);
3222 mailbox = imapx_store_ref_mailbox_unlocked (imapx_store, mailbox_name);
3223 if (mailbox != NULL) {
3224 camel_imapx_mailbox_handle_lsub_response (mailbox, response);
3225 emit_mailbox_updated = TRUE;
3227 g_mutex_unlock (&imapx_store->priv->mailboxes_lock);
3229 if (emit_mailbox_updated)
3230 g_signal_emit (imapx_store, signals[MAILBOX_UPDATED], 0, mailbox);
3232 g_clear_object (&mailbox);
3235 CamelFolderQuotaInfo *
3236 camel_imapx_store_dup_quota_info (CamelIMAPXStore *store,
3237 const gchar *quota_root_name)
3239 CamelFolderQuotaInfo *info;
3241 g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (store), NULL);
3242 g_return_val_if_fail (quota_root_name != NULL, NULL);
3244 g_mutex_lock (&store->priv->quota_info_lock);
3246 info = g_hash_table_lookup (
3247 store->priv->quota_info, quota_root_name);
3249 /* camel_folder_quota_info_clone() handles NULL gracefully. */
3250 info = camel_folder_quota_info_clone (info);
3252 g_mutex_unlock (&store->priv->quota_info_lock);
3258 camel_imapx_store_set_quota_info (CamelIMAPXStore *store,
3259 const gchar *quota_root_name,
3260 const CamelFolderQuotaInfo *info)
3262 g_return_if_fail (CAMEL_IS_IMAPX_STORE (store));
3263 g_return_if_fail (quota_root_name != NULL);
3265 g_mutex_lock (&store->priv->quota_info_lock);
3267 /* camel_folder_quota_info_clone() handles NULL gracefully. */
3268 g_hash_table_insert (
3269 store->priv->quota_info,
3270 g_strdup (quota_root_name),
3271 camel_folder_quota_info_clone (info));
3273 g_mutex_unlock (&store->priv->quota_info_lock);
3276 /* Tries to find matching job among all active connections.
3277 See camel_imapx_server_ref_job() for more information on parameters
3281 camel_imapx_store_ref_job (CamelIMAPXStore *imapx_store,
3282 CamelIMAPXMailbox *mailbox,
3286 GList *servers, *siter;
3287 CamelIMAPXJob *job = NULL;
3289 g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (imapx_store), NULL);
3291 servers = camel_imapx_conn_manager_get_connections (imapx_store->priv->con_man);
3293 for (siter = servers; siter; siter = g_list_next (siter)) {
3294 CamelIMAPXServer *imapx_server = siter->data;
3296 job = camel_imapx_server_ref_job (imapx_server, mailbox, job_type, uid);
3301 g_list_free_full (servers, g_object_unref);