/* * camel-subscribable.c * * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . * */ #include "camel-subscribable.h" #include #include #include "camel-async-closure.h" #include "camel-debug.h" #include "camel-session.h" #include "camel-vtrash-folder.h" typedef struct _AsyncContext AsyncContext; typedef struct _SignalClosure SignalClosure; struct _AsyncContext { gchar *folder_name; }; struct _SignalClosure { GWeakRef subscribable; CamelFolderInfo *folder_info; }; enum { FOLDER_SUBSCRIBED, FOLDER_UNSUBSCRIBED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; G_DEFINE_INTERFACE (CamelSubscribable, camel_subscribable, CAMEL_TYPE_STORE) static void async_context_free (AsyncContext *async_context) { g_free (async_context->folder_name); g_slice_free (AsyncContext, async_context); } static void signal_closure_free (SignalClosure *signal_closure) { g_weak_ref_clear (&signal_closure->subscribable); if (signal_closure->folder_info != NULL) camel_folder_info_free (signal_closure->folder_info); g_slice_free (SignalClosure, signal_closure); } static gboolean subscribable_emit_folder_subscribed_cb (gpointer user_data) { SignalClosure *signal_closure = user_data; CamelSubscribable *subscribable; subscribable = g_weak_ref_get (&signal_closure->subscribable); if (subscribable != NULL) { g_signal_emit ( subscribable, signals[FOLDER_SUBSCRIBED], 0, signal_closure->folder_info); g_object_unref (subscribable); } return FALSE; } static gboolean subscribable_emit_folder_unsubscribed_cb (gpointer user_data) { SignalClosure *signal_closure = user_data; CamelSubscribable *subscribable; subscribable = g_weak_ref_get (&signal_closure->subscribable); if (subscribable != NULL) { g_signal_emit ( subscribable, signals[FOLDER_UNSUBSCRIBED], 0, signal_closure->folder_info); g_object_unref (subscribable); } return FALSE; } static void subscribable_delete_cached_folder (CamelStore *store, const gchar *folder_name) { CamelFolder *folder; CamelVeeFolder *vfolder; /* XXX Copied from camel-store.c. Should this be public? */ if (store->folders == NULL) return; folder = camel_object_bag_get (store->folders, folder_name); if (folder == NULL) return; if (store->flags & CAMEL_STORE_VTRASH) { folder_name = CAMEL_VTRASH_NAME; vfolder = camel_object_bag_get (store->folders, folder_name); if (vfolder != NULL) { camel_vee_folder_remove_folder (vfolder, folder, NULL); g_object_unref (vfolder); } } if (store->flags & CAMEL_STORE_VJUNK) { folder_name = CAMEL_VJUNK_NAME; vfolder = camel_object_bag_get (store->folders, folder_name); if (vfolder != NULL) { camel_vee_folder_remove_folder (vfolder, folder, NULL); g_object_unref (vfolder); } } camel_folder_delete (folder); camel_object_bag_remove (store->folders, folder); g_object_unref (folder); } static void camel_subscribable_default_init (CamelSubscribableInterface *iface) { signals[FOLDER_SUBSCRIBED] = g_signal_new ( "folder-subscribed", G_OBJECT_CLASS_TYPE (iface), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET ( CamelSubscribableInterface, folder_subscribed), NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_POINTER); signals[FOLDER_UNSUBSCRIBED] = g_signal_new ( "folder-unsubscribed", G_OBJECT_CLASS_TYPE (iface), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET ( CamelSubscribableInterface, folder_unsubscribed), NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_POINTER); } /** * camel_subscribable_folder_is_subscribed: * @subscribable: a #CamelSubscribable * @folder_name: full path of the folder * * Find out if a folder has been subscribed to. * * Returns: %TRUE if the folder has been subscribed to or %FALSE otherwise * * Since: 3.2 **/ gboolean camel_subscribable_folder_is_subscribed (CamelSubscribable *subscribable, const gchar *folder_name) { CamelSubscribableInterface *iface; g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE); g_return_val_if_fail (folder_name != NULL, FALSE); iface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable); g_return_val_if_fail (iface->folder_is_subscribed != NULL, FALSE); return iface->folder_is_subscribed (subscribable, folder_name); } /** * camel_subscribable_subscribe_folder_sync: * @subscribable: a #CamelSubscribable * @folder_name: full path of the folder * @cancellable: optional #GCancellable object, or %NULL * @error: return location for a #GError, or %NULL * * Subscribes to the folder described by @folder_name. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.2 **/ gboolean camel_subscribable_subscribe_folder_sync (CamelSubscribable *subscribable, const gchar *folder_name, GCancellable *cancellable, GError **error) { CamelAsyncClosure *closure; GAsyncResult *result; gboolean success; g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE); g_return_val_if_fail (folder_name != NULL, FALSE); closure = camel_async_closure_new (); camel_subscribable_subscribe_folder ( subscribable, folder_name, G_PRIORITY_DEFAULT, cancellable, camel_async_closure_callback, closure); result = camel_async_closure_wait (closure); success = camel_subscribable_subscribe_folder_finish ( subscribable, result, error); camel_async_closure_free (closure); return success; } /* Helper for camel_subscribable_subscribe_folder() */ static void subscribable_subscribe_folder_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { CamelSubscribable *subscribable; CamelSubscribableInterface *iface; const gchar *folder_name; const gchar *message; gboolean success; AsyncContext *async_context; GError *local_error = NULL; subscribable = CAMEL_SUBSCRIBABLE (source_object); async_context = (AsyncContext *) task_data; folder_name = async_context->folder_name; iface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable); g_return_if_fail (iface->subscribe_folder_sync != NULL); /* Need to establish a connection before subscribing. */ camel_service_connect_sync ( CAMEL_SERVICE (subscribable), cancellable, &local_error); if (local_error != NULL) { g_task_return_error (task, local_error); return; } message = _("Subscribing to folder '%s'"); camel_operation_push_message (cancellable, message, folder_name); success = iface->subscribe_folder_sync ( subscribable, folder_name, cancellable, &local_error); CAMEL_CHECK_LOCAL_GERROR ( subscribable, subscribe_folder_sync, success, local_error); camel_operation_pop_message (cancellable); if (local_error != NULL) { g_task_return_error (task, local_error); } else { g_task_return_boolean (task, success); } } /** * camel_subscribable_subscribe_folder: * @subscribable: a #CamelSubscribable * @folder_name: full path of the folder * @io_priority: the I/O priority of the request * @cancellable: optional #GCancellable object, or %NULL * @callback: a #GAsyncReadyCallback to call when the request is satisfied * @user_data: data to pass to the callback function * * Asynchronously subscribes to the folder described by @folder_name. * * When the operation is finished, @callback will be called. You can then * call camel_subscribable_subscribe_folder_finish() to get the result of * the operation. * * Since: 3.2 **/ void camel_subscribable_subscribe_folder (CamelSubscribable *subscribable, const gchar *folder_name, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; CamelService *service; AsyncContext *async_context; g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable)); g_return_if_fail (folder_name != NULL); service = CAMEL_SERVICE (subscribable); async_context = g_slice_new0 (AsyncContext); async_context->folder_name = g_strdup (folder_name); task = g_task_new (subscribable, cancellable, callback, user_data); g_task_set_source_tag (task, camel_subscribable_subscribe_folder); g_task_set_priority (task, io_priority); g_task_set_task_data ( task, async_context, (GDestroyNotify) async_context_free); camel_service_queue_task ( service, task, subscribable_subscribe_folder_thread); g_object_unref (task); } /** * camel_subscribable_subscribe_folder_finish: * @subscribable: a #CamelSubscribable * @result: a #GAsyncResult * @error: return location for a #GError, or %NULL * * Finishes the operation started with camel_subscribable_subscribe_folder(). * * Returns: %TRUE on success, %FALSE on error * * Since: 3.2 **/ gboolean camel_subscribable_subscribe_folder_finish (CamelSubscribable *subscribable, GAsyncResult *result, GError **error) { g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE); g_return_val_if_fail (g_task_is_valid (result, subscribable), FALSE); g_return_val_if_fail ( g_async_result_is_tagged ( result, camel_subscribable_subscribe_folder), FALSE); return g_task_propagate_boolean (G_TASK (result), error); } /** * camel_subscribable_unsubscribe_folder_sync: * @subscribable: a #CamelSubscribable * @folder_name: full path of the folder * @cancellable: optional #GCancellable object, or %NULL * @error: return location for a #GError, or %NULL * * Unsubscribes from the folder described by @folder_name. * * Returns: %TRUE on success, %FALSE on error * * Since: 3.2 **/ gboolean camel_subscribable_unsubscribe_folder_sync (CamelSubscribable *subscribable, const gchar *folder_name, GCancellable *cancellable, GError **error) { CamelAsyncClosure *closure; GAsyncResult *result; gboolean success; g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE); g_return_val_if_fail (folder_name != NULL, FALSE); closure = camel_async_closure_new (); camel_subscribable_unsubscribe_folder ( subscribable, folder_name, G_PRIORITY_DEFAULT, cancellable, camel_async_closure_callback, closure); result = camel_async_closure_wait (closure); success = camel_subscribable_unsubscribe_folder_finish ( subscribable, result, error); camel_async_closure_free (closure); return success; } /* Helper for camel_subscribable_unsubscribe_folder() */ static void subscribable_unsubscribe_folder_thread (GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) { CamelSubscribable *subscribable; CamelSubscribableInterface *iface; const gchar *folder_name; const gchar *message; gboolean success; AsyncContext *async_context; GError *local_error = NULL; subscribable = CAMEL_SUBSCRIBABLE (source_object); async_context = (AsyncContext *) task_data; folder_name = async_context->folder_name; iface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable); g_return_if_fail (iface->unsubscribe_folder_sync != NULL); /* Need to establish a connection before unsubscribing. */ camel_service_connect_sync ( CAMEL_SERVICE (subscribable), cancellable, &local_error); if (local_error != NULL) { g_task_return_error (task, local_error); return; } message = _("Unsubscribing from folder '%s'"); camel_operation_push_message (cancellable, message, folder_name); success = iface->unsubscribe_folder_sync ( subscribable, folder_name, cancellable, &local_error); CAMEL_CHECK_LOCAL_GERROR ( subscribable, unsubscribe_folder_sync, success, local_error); if (success) subscribable_delete_cached_folder ( CAMEL_STORE (subscribable), folder_name); camel_operation_pop_message (cancellable); if (local_error != NULL) { g_task_return_error (task, local_error); } else { g_task_return_boolean (task, success); } } /** * camel_subscribable_unsubscribe_folder: * @subscribable: a #CamelSubscribable * @folder_name: full path of the folder * @io_priority: the I/O priority of the request * @cancellable: optional #GCancellable object, or %NULL * @callback: a #GAsyncReadyCallback to call when the request is satisfied * @user_data: data to pass to the callback function * * Asynchronously unsubscribes from the folder described by @folder_name. * * When the operation is finished, @callback will be called. You can then * call camel_subscribable_unsubscribe_folder_finish() to get the result of * the operation. * * Since: 3.2 **/ void camel_subscribable_unsubscribe_folder (CamelSubscribable *subscribable, const gchar *folder_name, gint io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; CamelService *service; AsyncContext *async_context; g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable)); g_return_if_fail (folder_name != NULL); service = CAMEL_SERVICE (subscribable); async_context = g_slice_new0 (AsyncContext); async_context->folder_name = g_strdup (folder_name); task = g_task_new (subscribable, cancellable, callback, user_data); g_task_set_source_tag (task, camel_subscribable_unsubscribe_folder); g_task_set_priority (task, io_priority); g_task_set_task_data ( task, async_context, (GDestroyNotify) async_context_free); camel_service_queue_task ( service, task, subscribable_unsubscribe_folder_thread); g_object_unref (task); } /** * camel_subscribable_unsubscribe_folder_finish: * @subscribable: a #CamelSubscribable * @result: a #GAsyncResult * @error: return location for a #GError, or %NULL * * Finishes the operation started with camel_subscribable_unsubscribe_folder(). * * Returns: %TRUE on success, %FALSE on error * * Since: 3.2 **/ gboolean camel_subscribable_unsubscribe_folder_finish (CamelSubscribable *subscribable, GAsyncResult *result, GError **error) { g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE); g_return_val_if_fail (g_task_is_valid (result, subscribable), FALSE); g_return_val_if_fail ( g_async_result_is_tagged ( result, camel_subscribable_unsubscribe_folder), FALSE); return g_task_propagate_boolean (G_TASK (result), error); } /** * camel_subscribable_folder_subscribed: * @subscribable: a #CamelSubscribable * @folder_info: information about the subscribed folder * * Emits the #CamelSubscribable::folder-subscribed signal from an idle source * on the main loop. The idle source's priority is #G_PRIORITY_HIGH_IDLE. * * This function is only intended for Camel providers. * * Since: 3.2 **/ void camel_subscribable_folder_subscribed (CamelSubscribable *subscribable, CamelFolderInfo *folder_info) { CamelService *service; CamelSession *session; SignalClosure *signal_closure; g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable)); g_return_if_fail (folder_info != NULL); service = CAMEL_SERVICE (subscribable); session = camel_service_ref_session (service); signal_closure = g_slice_new0 (SignalClosure); g_weak_ref_init (&signal_closure->subscribable, subscribable); signal_closure->folder_info = camel_folder_info_clone (folder_info); /* Prioritize ahead of GTK+ redraws. */ camel_session_idle_add ( session, G_PRIORITY_HIGH_IDLE, subscribable_emit_folder_subscribed_cb, signal_closure, (GDestroyNotify) signal_closure_free); g_object_unref (session); } /** * camel_subscribable_folder_unsubscribed: * @subscribable: a #CamelSubscribable * @folder_info: information about the unsubscribed folder * * Emits the #CamelSubscribable::folder-unsubscribed signal from an idle source * on the main loop. The idle source's priority is #G_PRIORITY_HIGH_IDLE. * * This function is only intended for Camel providers. * * Since: 3.2 **/ void camel_subscribable_folder_unsubscribed (CamelSubscribable *subscribable, CamelFolderInfo *folder_info) { CamelService *service; CamelSession *session; SignalClosure *signal_closure; g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable)); g_return_if_fail (folder_info != NULL); service = CAMEL_SERVICE (subscribable); session = camel_service_ref_session (service); signal_closure = g_slice_new0 (SignalClosure); g_weak_ref_init (&signal_closure->subscribable, subscribable); signal_closure->folder_info = camel_folder_info_clone (folder_info); /* Prioritize ahead of GTK+ redraws. */ camel_session_idle_add ( session, G_PRIORITY_HIGH_IDLE, subscribable_emit_folder_unsubscribed_cb, signal_closure, (GDestroyNotify) signal_closure_free); g_object_unref (session); }