[evolution-data-server] CamelStore: Split subscription API into a separate interface.



commit 4f20d5b76c78f89dc44637540e7ba7fc2a723fc2
Author: Matthew Barnes <mbarnes redhat com>
Date:   Mon Aug 15 16:40:55 2011 -0400

    CamelStore: Split subscription API into a separate interface.
    
    The CAMEL_STORE_SUBSCRIPTIONS flag basically means the CamelStore
    instance supports the folder subscription portion of its API.  This
    would be better handled by splitting the subscription portion of its
    API into a separate GTypeInterface for providers to implement.  Then
    we don't need the subscriptions flag.
    
    The CamelSubscribable interface does exactly that.  To check if a
    CamelStore supports folder subscriptions, just do:
    
       if (CAMEL_IS_SUBSCRIBABLE (store)) ...
    
    Implement CamelSubscribable in the IMAP, IMAPX and NNTP providers.

 camel/Makefile.am                                 |    2 +
 camel/camel-disco-store.c                         |    3 +-
 camel/camel-enums.h                               |   13 +-
 camel/camel-store.c                               |  518 +-----------------
 camel/camel-store.h                               |   74 ---
 camel/camel-subscribable.c                        |  639 +++++++++++++++++++++
 camel/camel-subscribable.h                        |  151 +++++
 camel/camel.h                                     |    1 +
 camel/providers/imap/camel-imap-store.c           |  319 ++++++-----
 camel/providers/imapx/camel-imapx-store.c         |  106 ++--
 camel/providers/nntp/camel-nntp-store.c           |  265 +++++----
 docs/reference/camel/camel-docs.sgml              |    1 +
 docs/reference/camel/camel-sections.txt           |   35 +-
 docs/reference/camel/camel.types                  |    1 +
 docs/reference/camel/tmpl/camel-store.sgml        |  126 ----
 docs/reference/camel/tmpl/camel-subscribable.sgml |  144 +++++
 docs/reference/camel/tmpl/camel-unused.sgml       |  115 ++++
 17 files changed, 1452 insertions(+), 1061 deletions(-)
---
diff --git a/camel/Makefile.am b/camel/Makefile.am
index 63d12df..6f9c606 100644
--- a/camel/Makefile.am
+++ b/camel/Makefile.am
@@ -90,6 +90,7 @@ libcamel_provider_1_2_la_SOURCES = 		\
 	camel-store.c				\
 	camel-store-settings.c			\
 	camel-store-summary.c			\
+	camel-subscribable.c			\
 	camel-tcp-stream-raw.c			\
 	camel-tcp-stream-ssl.c			\
 	camel-tcp-stream.c			\
@@ -142,6 +143,7 @@ libcamel_providerinclude_HEADERS =		\
 	camel-store.h				\
 	camel-store-settings.h			\
 	camel-store-summary.h			\
+	camel-subscribable.h			\
 	camel-tcp-stream-raw.h			\
 	camel-tcp-stream-ssl.h			\
 	camel-tcp-stream.h			\
diff --git a/camel/camel-disco-store.c b/camel/camel-disco-store.c
index 329af8d..e2c3c65 100644
--- a/camel/camel-disco-store.c
+++ b/camel/camel-disco-store.c
@@ -34,6 +34,7 @@
 #include "camel-disco-store.h"
 #include "camel-offline-settings.h"
 #include "camel-session.h"
+#include "camel-subscribable.h"
 
 #define d(x)
 
@@ -223,7 +224,7 @@ disco_store_get_folder_info_sync (CamelStore *store,
 
 	case CAMEL_DISCO_STORE_OFFLINE:
 		/* Can't edit subscriptions while offline */
-		if ((store->flags & CAMEL_STORE_SUBSCRIPTIONS) &&
+		if (CAMEL_IS_SUBSCRIBABLE (store) &&
 		    !(flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED)) {
 			camel_disco_store_check_online (disco_store, error);
 			return NULL;
diff --git a/camel/camel-enums.h b/camel/camel-enums.h
index e347ca0..1ac294f 100644
--- a/camel/camel-enums.h
+++ b/camel/camel-enums.h
@@ -290,13 +290,12 @@ typedef enum {
 } CamelSortType;
 
 typedef enum { /*< flags >*/
-	CAMEL_STORE_SUBSCRIPTIONS    = 1 << 0,
-	CAMEL_STORE_VTRASH           = 1 << 1,
-	CAMEL_STORE_VJUNK            = 1 << 2,
-	CAMEL_STORE_PROXY            = 1 << 3,
-	CAMEL_STORE_IS_MIGRATING     = 1 << 4,
-	CAMEL_STORE_ASYNC            = 1 << 5,
-	CAMEL_STORE_REAL_JUNK_FOLDER = 1 << 6
+	CAMEL_STORE_VTRASH           = 1 << 0,
+	CAMEL_STORE_VJUNK            = 1 << 1,
+	CAMEL_STORE_PROXY            = 1 << 2,
+	CAMEL_STORE_IS_MIGRATING     = 1 << 3,
+	CAMEL_STORE_ASYNC            = 1 << 4,
+	CAMEL_STORE_REAL_JUNK_FOLDER = 1 << 5
 } CamelStoreFlags;
 
 /**
diff --git a/camel/camel-store.c b/camel/camel-store.c
index a58b953..2a5a732 100644
--- a/camel/camel-store.c
+++ b/camel/camel-store.c
@@ -41,6 +41,7 @@
 #include "camel-session.h"
 #include "camel-store.h"
 #include "camel-store-settings.h"
+#include "camel-subscribable.h"
 #include "camel-vtrash-folder.h"
 
 #define d(x)
@@ -81,8 +82,6 @@ enum {
 	FOLDER_DELETED,
 	FOLDER_OPENED,
 	FOLDER_RENAMED,
-	FOLDER_SUBSCRIBED,
-	FOLDER_UNSUBSCRIBED,
 	LAST_SIGNAL
 };
 
@@ -173,28 +172,6 @@ store_emit_folder_renamed_cb (SignalData *data)
 	return FALSE;
 }
 
-static gboolean
-store_emit_folder_subscribed_cb (SignalData *data)
-{
-	g_signal_emit (
-		data->store,
-		signals[FOLDER_SUBSCRIBED], 0,
-		data->folder_info);
-
-	return FALSE;
-}
-
-static gboolean
-store_emit_folder_unsubscribed_cb (SignalData *data)
-{
-	g_signal_emit (
-		data->store,
-		signals[FOLDER_UNSUBSCRIBED], 0,
-		data->folder_info);
-
-	return FALSE;
-}
-
 /**
  * ignore_no_such_table_exception:
  * Clears the exception 'ex' when it's the 'no such table' exception.
@@ -982,136 +959,6 @@ store_rename_folder_finish (CamelStore *store,
 }
 
 static void
-store_subscribe_folder_thread (GSimpleAsyncResult *simple,
-                               GObject *object,
-                               GCancellable *cancellable)
-{
-	AsyncContext *async_context;
-	GError *error = NULL;
-
-	async_context = g_simple_async_result_get_op_res_gpointer (simple);
-
-	camel_store_subscribe_folder_sync (
-		CAMEL_STORE (object), async_context->folder_name_1,
-		cancellable, &error);
-
-	if (error != NULL) {
-		g_simple_async_result_set_from_error (simple, error);
-		g_error_free (error);
-	}
-}
-
-static void
-store_subscribe_folder (CamelStore *store,
-                        const gchar *folder_name,
-                        gint io_priority,
-                        GCancellable *cancellable,
-                        GAsyncReadyCallback callback,
-                        gpointer user_data)
-{
-	GSimpleAsyncResult *simple;
-	AsyncContext *async_context;
-
-	async_context = g_slice_new0 (AsyncContext);
-	async_context->folder_name_1 = g_strdup (folder_name);
-
-	simple = g_simple_async_result_new (
-		G_OBJECT (store), callback,
-		user_data, store_subscribe_folder);
-
-	g_simple_async_result_set_op_res_gpointer (
-		simple, async_context, (GDestroyNotify) async_context_free);
-
-	g_simple_async_result_run_in_thread (
-		simple, store_subscribe_folder_thread,
-		io_priority, cancellable);
-
-	g_object_unref (simple);
-}
-
-static gboolean
-store_subscribe_folder_finish (CamelStore *store,
-                               GAsyncResult *result,
-                               GError **error)
-{
-	GSimpleAsyncResult *simple;
-
-	g_return_val_if_fail (
-		g_simple_async_result_is_valid (
-		result, G_OBJECT (store), store_subscribe_folder), FALSE);
-
-	simple = G_SIMPLE_ASYNC_RESULT (result);
-
-	/* Assume success unless a GError is set. */
-	return !g_simple_async_result_propagate_error (simple, error);
-}
-
-static void
-store_unsubscribe_folder_thread (GSimpleAsyncResult *simple,
-                                 GObject *object,
-                                 GCancellable *cancellable)
-{
-	AsyncContext *async_context;
-	GError *error = NULL;
-
-	async_context = g_simple_async_result_get_op_res_gpointer (simple);
-
-	camel_store_unsubscribe_folder_sync (
-		CAMEL_STORE (object), async_context->folder_name_1,
-		cancellable, &error);
-
-	if (error != NULL) {
-		g_simple_async_result_set_from_error (simple, error);
-		g_error_free (error);
-	}
-}
-
-static void
-store_unsubscribe_folder (CamelStore *store,
-                          const gchar *folder_name,
-                          gint io_priority,
-                          GCancellable *cancellable,
-                          GAsyncReadyCallback callback,
-                          gpointer user_data)
-{
-	GSimpleAsyncResult *simple;
-	AsyncContext *async_context;
-
-	async_context = g_slice_new0 (AsyncContext);
-	async_context->folder_name_1 = g_strdup (folder_name);
-
-	simple = g_simple_async_result_new (
-		G_OBJECT (store), callback,
-		user_data, store_unsubscribe_folder);
-
-	g_simple_async_result_set_op_res_gpointer (
-		simple, async_context, (GDestroyNotify) async_context_free);
-
-	g_simple_async_result_run_in_thread (
-		simple, store_unsubscribe_folder_thread,
-		io_priority, cancellable);
-
-	g_object_unref (simple);
-}
-
-static gboolean
-store_unsubscribe_folder_finish (CamelStore *store,
-                                 GAsyncResult *result,
-                                 GError **error)
-{
-	GSimpleAsyncResult *simple;
-
-	g_return_val_if_fail (
-		g_simple_async_result_is_valid (
-		result, G_OBJECT (store), store_unsubscribe_folder), FALSE);
-
-	simple = G_SIMPLE_ASYNC_RESULT (result);
-
-	/* Assume success unless a GError is set. */
-	return !g_simple_async_result_propagate_error (simple, error);
-}
-
-static void
 store_synchronize_thread (GSimpleAsyncResult *simple,
                           GObject *object,
                           GCancellable *cancellable)
@@ -1309,10 +1156,6 @@ camel_store_class_init (CamelStoreClass *class)
 	class->delete_folder_finish = store_delete_folder_finish;
 	class->rename_folder = store_rename_folder;
 	class->rename_folder_finish = store_rename_folder_finish;
-	class->subscribe_folder = store_subscribe_folder;
-	class->subscribe_folder_finish = store_subscribe_folder_finish;
-	class->unsubscribe_folder = store_unsubscribe_folder;
-	class->unsubscribe_folder_finish = store_unsubscribe_folder_finish;
 	class->synchronize = store_synchronize;
 	class->synchronize_finish = store_synchronize_finish;
 	class->noop = store_noop;
@@ -1358,26 +1201,6 @@ camel_store_class_init (CamelStoreClass *class)
 		G_TYPE_NONE, 2,
 		G_TYPE_STRING,
 		G_TYPE_POINTER);
-
-	signals[FOLDER_SUBSCRIBED] = g_signal_new (
-		"folder-subscribed",
-		G_OBJECT_CLASS_TYPE (class),
-		G_SIGNAL_RUN_FIRST,
-		G_STRUCT_OFFSET (CamelStoreClass, folder_subscribed),
-		NULL, NULL,
-		g_cclosure_marshal_VOID__POINTER,
-		G_TYPE_NONE, 1,
-		G_TYPE_POINTER);
-
-	signals[FOLDER_UNSUBSCRIBED] = g_signal_new (
-		"folder-unsubscribed",
-		G_OBJECT_CLASS_TYPE (class),
-		G_SIGNAL_RUN_FIRST,
-		G_STRUCT_OFFSET (CamelStoreClass, folder_unsubscribed),
-		NULL, NULL,
-		g_cclosure_marshal_VOID__POINTER,
-		G_TYPE_NONE, 1,
-		G_TYPE_POINTER);
 }
 
 static void
@@ -1541,68 +1364,6 @@ camel_store_folder_renamed (CamelStore *store,
 		data, (GDestroyNotify) signal_data_free);
 }
 
-/**
- * camel_store_folder_subscribed:
- * @store: a #CamelStore
- * @folder_info: information about the subscribed folder
- *
- * Emits the #CamelStore::folder-subscribed signal from an idle source on
- * the main loop.  The idle source's priority is #G_PRIORITY_DEFAULT_IDLE.
- *
- * This function is only intended for Camel providers.
- *
- * Since: 2.32
- **/
-void
-camel_store_folder_subscribed (CamelStore *store,
-                               CamelFolderInfo *folder_info)
-{
-	SignalData *data;
-
-	g_return_if_fail (CAMEL_IS_STORE (store));
-	g_return_if_fail (folder_info != NULL);
-
-	data = g_slice_new0 (SignalData);
-	data->store = g_object_ref (store);
-	data->folder_info = camel_folder_info_clone (folder_info);
-
-	g_idle_add_full (
-		G_PRIORITY_DEFAULT_IDLE,
-		(GSourceFunc) store_emit_folder_subscribed_cb,
-		data, (GDestroyNotify) signal_data_free);
-}
-
-/**
- * camel_store_folder_unsubscribed:
- * @store: a #CamelStore
- * @folder_info: information about the unsubscribed folder
- *
- * Emits the #CamelStore::folder-unsubscribed signal from an idle source on
- * the main loop.  The idle source's priority is #G_PRIORITY_DEFAULT_IDLE.
- *
- * This function is only intended for Camel providers.
- *
- * Since: 2.32
- **/
-void
-camel_store_folder_unsubscribed (CamelStore *store,
-                                 CamelFolderInfo *folder_info)
-{
-	SignalData *data;
-
-	g_return_if_fail (CAMEL_IS_STORE (store));
-	g_return_if_fail (folder_info != NULL);
-
-	data = g_slice_new0 (SignalData);
-	data->store = g_object_ref (store);
-	data->folder_info = camel_folder_info_clone (folder_info);
-
-	g_idle_add_full (
-		G_PRIORITY_DEFAULT_IDLE,
-		(GSourceFunc) store_emit_folder_unsubscribed_cb,
-		data, (GDestroyNotify) signal_data_free);
-}
-
 static void
 add_special_info (CamelStore *store,
                   CamelFolderInfo *info,
@@ -1924,54 +1685,6 @@ camel_folder_info_clone (CamelFolderInfo *fi)
 }
 
 /**
- * camel_store_supports_subscriptions:
- * @store: a #CamelStore
- *
- * Get whether or not @store supports subscriptions to folders.
- *
- * Returns: %TRUE if folder subscriptions are supported or %FALSE otherwise
- **/
-gboolean
-camel_store_supports_subscriptions (CamelStore *store)
-{
-	g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
-
-	return (store->flags & CAMEL_STORE_SUBSCRIPTIONS);
-}
-
-/**
- * camel_store_folder_is_subscribed:
- * @store: a #CamelStore
- * @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
- **/
-gboolean
-camel_store_folder_is_subscribed (CamelStore *store,
-                                  const gchar *folder_name)
-{
-	CamelStoreClass *class;
-	gboolean is_subscribed;
-
-	g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
-	g_return_val_if_fail (folder_name != NULL, FALSE);
-	g_return_val_if_fail (store->flags & CAMEL_STORE_SUBSCRIPTIONS, FALSE);
-
-	class = CAMEL_STORE_GET_CLASS (store);
-	g_return_val_if_fail (class->folder_is_subscribed != NULL, FALSE);
-
-	camel_store_lock (store, CAMEL_STORE_FOLDER_LOCK);
-
-	is_subscribed = class->folder_is_subscribed (store, folder_name);
-
-	camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
-
-	return is_subscribed;
-}
-
-/**
  * camel_store_can_refresh_folder
  * @store: a #CamelStore
  * @info: a #CamelFolderInfo
@@ -3142,7 +2855,7 @@ camel_store_rename_folder_sync (CamelStore *store,
 			}
 
 			/* Emit renamed signal */
-			if (store->flags & CAMEL_STORE_SUBSCRIPTIONS)
+			if (CAMEL_IS_SUBSCRIBABLE (store))
 				flags |= CAMEL_STORE_FOLDER_INFO_SUBSCRIBED;
 
 			folder_info = class->get_folder_info_sync (
@@ -3240,233 +2953,6 @@ camel_store_rename_folder_finish (CamelStore *store,
 }
 
 /**
- * camel_store_subscribe_folder_sync:
- * @store: a #CamelStore
- * @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.0
- **/
-gboolean
-camel_store_subscribe_folder_sync (CamelStore *store,
-                                   const gchar *folder_name,
-                                   GCancellable *cancellable,
-                                   GError **error)
-{
-	CamelStoreClass *class;
-	gboolean success;
-
-	g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
-	g_return_val_if_fail (folder_name != NULL, FALSE);
-	g_return_val_if_fail (store->flags & CAMEL_STORE_SUBSCRIPTIONS, FALSE);
-
-	class = CAMEL_STORE_GET_CLASS (store);
-	g_return_val_if_fail (class->subscribe_folder_sync != NULL, FALSE);
-
-	camel_store_lock (store, CAMEL_STORE_FOLDER_LOCK);
-
-	/* Check for cancellation after locking. */
-	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
-		camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
-		return FALSE;
-	}
-
-	success = class->subscribe_folder_sync (
-		store, folder_name, cancellable, error);
-	CAMEL_CHECK_GERROR (store, subscribe_folder_sync, success, error);
-
-	camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
-
-	return success;
-}
-
-/**
- * camel_store_subscribe_folder:
- * @store: a #CamelStore
- * @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_store_subscribe_folder_finish() to get the result of
- * the operation.
- *
- * Since: 3.0
- **/
-void
-camel_store_subscribe_folder (CamelStore *store,
-                              const gchar *folder_name,
-                              gint io_priority,
-                              GCancellable *cancellable,
-                              GAsyncReadyCallback callback,
-                              gpointer user_data)
-{
-	CamelStoreClass *class;
-
-	g_return_if_fail (CAMEL_IS_STORE (store));
-	g_return_if_fail (folder_name != NULL);
-
-	class = CAMEL_STORE_GET_CLASS (store);
-	g_return_if_fail (class->subscribe_folder != NULL);
-
-	class->subscribe_folder (
-		store, folder_name, io_priority,
-		cancellable, callback, user_data);
-}
-
-/**
- * camel_store_subscribe_folder_finish:
- * @store: a #CamelStore
- * @result: a #GAsyncResult
- * @error: return location for a #GError, or %NULL
- *
- * Finishes the operation started with camel_store_subscribe_folder().
- *
- * Returns: %TRUE on success, %FALSE on error
- *
- * Since: 3.0
- **/
-gboolean
-camel_store_subscribe_folder_finish (CamelStore *store,
-                                     GAsyncResult *result,
-                                     GError **error)
-{
-	CamelStoreClass *class;
-
-	g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
-	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
-
-	class = CAMEL_STORE_GET_CLASS (store);
-	g_return_val_if_fail (class->subscribe_folder_finish != NULL, FALSE);
-
-	return class->subscribe_folder_finish (store, result, error);
-}
-
-/**
- * camel_store_unsubscribe_folder_sync:
- * @store: a #CamelStore
- * @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.0
- **/
-gboolean
-camel_store_unsubscribe_folder_sync (CamelStore *store,
-                                     const gchar *folder_name,
-                                     GCancellable *cancellable,
-                                     GError **error)
-{
-	CamelStoreClass *class;
-	gboolean success;
-
-	g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
-	g_return_val_if_fail (folder_name != NULL, FALSE);
-	g_return_val_if_fail (store->flags & CAMEL_STORE_SUBSCRIPTIONS, FALSE);
-
-	class = CAMEL_STORE_GET_CLASS (store);
-	g_return_val_if_fail (class->unsubscribe_folder_sync != NULL, FALSE);
-
-	camel_store_lock (store, CAMEL_STORE_FOLDER_LOCK);
-
-	/* Check for cancellation after locking. */
-	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
-		camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
-		return FALSE;
-	}
-
-	success = class->unsubscribe_folder_sync (
-		store, folder_name, cancellable, error);
-	CAMEL_CHECK_GERROR (store, unsubscribe_folder_sync, success, error);
-
-	if (success)
-		cs_delete_cached_folder (store, folder_name);
-
-	camel_store_unlock (store, CAMEL_STORE_FOLDER_LOCK);
-
-	return success;
-}
-
-/**
- * camel_store_unsubscribe_folder:
- * @store: a #CamelStore
- * @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_store_unsubscribe_folder_finish() to get the result of the
- * operation.
- *
- * Since: 3.0
- **/
-void
-camel_store_unsubscribe_folder (CamelStore *store,
-                                const gchar *folder_name,
-                                gint io_priority,
-                                GCancellable *cancellable,
-                                GAsyncReadyCallback callback,
-                                gpointer user_data)
-{
-	CamelStoreClass *class;
-
-	g_return_if_fail (CAMEL_IS_STORE (store));
-	g_return_if_fail (folder_name != NULL);
-
-	class = CAMEL_STORE_GET_CLASS (store);
-	g_return_if_fail (class->unsubscribe_folder != NULL);
-
-	class->unsubscribe_folder (
-		store, folder_name, io_priority,
-		cancellable, callback, user_data);
-}
-
-/**
- * camel_store_unsubscribe_folder_finish:
- * @store: a #CamelStore
- * @result: a #GAsyncResult
- * @error: return location for a #GError, or %NULL
- *
- * Finishes the operation started with camel_store_unsubscribe_folder().
- *
- * Returns: %TRUE on success, %FALSE on error
- *
- * Since: 3.0
- **/
-gboolean
-camel_store_unsubscribe_folder_finish (CamelStore *store,
-                                       GAsyncResult *result,
-                                       GError **error)
-{
-	CamelStoreClass *class;
-
-	g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
-	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
-
-	class = CAMEL_STORE_GET_CLASS (store);
-	g_return_val_if_fail (class->unsubscribe_folder_finish != NULL, FALSE);
-
-	return class->unsubscribe_folder_finish (store, result, error);
-}
-
-/**
  * camel_store_synchronize_sync:
  * @store: a #CamelStore
  * @expunge: whether to expunge after synchronizing
diff --git a/camel/camel-store.h b/camel/camel-store.h
index 1e86c8d..6ef1749 100644
--- a/camel/camel-store.h
+++ b/camel/camel-store.h
@@ -144,8 +144,6 @@ struct _CamelStoreClass {
 	gboolean	(*can_refresh_folder)	(CamelStore *store,
 						 CamelFolderInfo *info,
 						 GError **error);
-	gboolean	(*folder_is_subscribed)	(CamelStore *store,
-						 const gchar *folder_name);
 	void		(*free_folder_info)	(CamelStore *store,
 						 CamelFolderInfo *fi);
 
@@ -187,16 +185,6 @@ struct _CamelStoreClass {
 						 const gchar *new_name,
 						 GCancellable *cancellable,
 						 GError **error);
-	gboolean	(*subscribe_folder_sync)
-						(CamelStore *store,
-						 const gchar *folder_name,
-						 GCancellable *cancellable,
-						 GError **error);
-	gboolean	(*unsubscribe_folder_sync)
-						(CamelStore *store,
-						 const gchar *folder_name,
-						 GCancellable *cancellable,
-						 GError **error);
 	gboolean	(*synchronize_sync)	(CamelStore *store,
 						 gboolean expunge,
 						 GCancellable *cancellable,
@@ -285,26 +273,6 @@ struct _CamelStoreClass {
 	gboolean	(*rename_folder_finish)	(CamelStore *store,
 						 GAsyncResult *result,
 						 GError **error);
-	void		(*subscribe_folder)	(CamelStore *store,
-						 const gchar *folder_name,
-						 gint io_priority,
-						 GCancellable *cancellable,
-						 GAsyncReadyCallback callback,
-						 gpointer user_data);
-	gboolean	(*subscribe_folder_finish)
-						(CamelStore *store,
-						 GAsyncResult *result,
-						 GError **error);
-	void		(*unsubscribe_folder)	(CamelStore *store,
-						 const gchar *folder_name,
-						 gint io_priority,
-						 GCancellable *cancellable,
-						 GAsyncReadyCallback callback,
-						 gpointer user_data);
-	gboolean	(*unsubscribe_folder_finish)
-						(CamelStore *store,
-						 GAsyncResult *result,
-						 GError **error);
 	void		(*synchronize)		(CamelStore *store,
 						 gboolean expunge,
 						 gint io_priority,
@@ -333,10 +301,6 @@ struct _CamelStoreClass {
 	void		(*folder_renamed)	(CamelStore *store,
 						 const gchar *old_name,
 						 CamelFolderInfo *folder_info);
-	void		(*folder_subscribed)	(CamelStore *store,
-						 CamelFolderInfo *folder_info);
-	void		(*folder_unsubscribed)	(CamelStore *store,
-						 CamelFolderInfo *folder_info);
 };
 
 GType		camel_store_get_type		(void);
@@ -350,10 +314,6 @@ void		camel_store_folder_opened	(CamelStore *store,
 void		camel_store_folder_renamed	(CamelStore *store,
 						 const gchar *old_name,
 						 CamelFolderInfo *folder_info);
-void		camel_store_folder_subscribed	(CamelStore *store,
-						 CamelFolderInfo *folder_info);
-void		camel_store_folder_unsubscribed	(CamelStore *store,
-						 CamelFolderInfo *folder_info);
 void		camel_store_free_folder_info	(CamelStore *store,
 						 CamelFolderInfo *fi);
 void		camel_store_free_folder_info_full
@@ -373,10 +333,6 @@ CamelFolderInfo *
 #endif /* CAMEL_DISABLE_DEPRECATED */
 CamelFolderInfo *
 		camel_folder_info_clone		(CamelFolderInfo *fi);
-gboolean	camel_store_supports_subscriptions
-						(CamelStore *store);
-gboolean	camel_store_folder_is_subscribed (CamelStore *store,
-						 const gchar *folder_name);
 gboolean	camel_store_can_refresh_folder	(CamelStore *store,
 						 CamelFolderInfo *info,
 						 GError **error);
@@ -506,36 +462,6 @@ gboolean	camel_store_rename_folder_finish
 						(CamelStore *store,
 						 GAsyncResult *result,
 						 GError **error);
-gboolean	camel_store_subscribe_folder_sync
-						(CamelStore *store,
-						 const gchar *folder_name,
-						 GCancellable *cancellable,
-						 GError **error);
-void		camel_store_subscribe_folder	(CamelStore *store,
-						 const gchar *folder_name,
-						 gint io_priority,
-						 GCancellable *cancellable,
-						 GAsyncReadyCallback callback,
-						 gpointer user_data);
-gboolean	camel_store_subscribe_folder_finish
-						(CamelStore *store,
-						 GAsyncResult *result,
-						 GError **error);
-gboolean	camel_store_unsubscribe_folder_sync
-						(CamelStore *store,
-						 const gchar *folder_name,
-						 GCancellable *cancellable,
-						 GError **error);
-void		camel_store_unsubscribe_folder	(CamelStore *store,
-						 const gchar *folder_name,
-						 gint io_priority,
-						 GCancellable *cancellable,
-						 GAsyncReadyCallback callback,
-						 gpointer user_data);
-gboolean	camel_store_unsubscribe_folder_finish
-						(CamelStore *store,
-						 GAsyncResult *result,
-						 GError **error);
 gboolean	camel_store_synchronize_sync	(CamelStore *store,
 						 gboolean expunge,
 						 GCancellable *cancellable,
diff --git a/camel/camel-subscribable.c b/camel/camel-subscribable.c
new file mode 100644
index 0000000..7da1007
--- /dev/null
+++ b/camel/camel-subscribable.c
@@ -0,0 +1,639 @@
+/*
+ * camel-subscribable.c
+ *
+ * This program 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include "camel-subscribable.h"
+
+#include "camel-debug.h"
+#include "camel-vtrash-folder.h"
+
+typedef struct _AsyncContext AsyncContext;
+typedef struct _SignalData SignalData;
+
+struct _AsyncContext {
+	gchar *folder_name;
+};
+
+struct _SignalData {
+	CamelSubscribable *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_data_free (SignalData *signal_data)
+{
+	if (signal_data->subscribable != NULL)
+		g_object_unref (signal_data->subscribable);
+
+	if (signal_data->folder_info != NULL)
+		camel_folder_info_free (signal_data->folder_info);
+
+	g_slice_free (SignalData, signal_data);
+}
+
+static gboolean
+subscribable_emit_folder_subscribed_cb (SignalData *signal_data)
+{
+	g_signal_emit (
+		signal_data->subscribable,
+		signals[FOLDER_SUBSCRIBED], 0,
+		signal_data->folder_info);
+
+	return FALSE;
+}
+
+static gboolean
+subscribable_emit_folder_unsubscribed_cb (SignalData *signal_data)
+{
+	g_signal_emit (
+		signal_data->subscribable,
+		signals[FOLDER_UNSUBSCRIBED], 0,
+		signal_data->folder_info);
+
+	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);
+			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);
+			g_object_unref (vfolder);
+		}
+	}
+
+	camel_folder_delete (folder);
+
+	camel_object_bag_remove (store->folders, folder);
+	g_object_unref (folder);
+}
+
+static void
+subscribable_subscribe_folder_thread (GSimpleAsyncResult *simple,
+                                      GObject *object,
+                                      GCancellable *cancellable)
+{
+	AsyncContext *async_context;
+	GError *error = NULL;
+
+	async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	camel_subscribable_subscribe_folder_sync (
+		CAMEL_SUBSCRIBABLE (object),
+		async_context->folder_name,
+		cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+}
+
+static void
+subscribable_subscribe_folder (CamelSubscribable *subscribable,
+                               const gchar *folder_name,
+                               gint io_priority,
+                               GCancellable *cancellable,
+                               GAsyncReadyCallback callback,
+                               gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+
+	async_context = g_slice_new0 (AsyncContext);
+	async_context->folder_name = g_strdup (folder_name);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (subscribable), callback,
+		user_data, subscribable_subscribe_folder);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, async_context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, subscribable_subscribe_folder_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+static gboolean
+subscribable_subscribe_folder_finish (CamelSubscribable *subscribable,
+                                      GAsyncResult *result,
+                                      GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (subscribable),
+		subscribable_subscribe_folder), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+subscribable_unsubscribe_folder_thread (GSimpleAsyncResult *simple,
+                                        GObject *object,
+                                        GCancellable *cancellable)
+{
+	AsyncContext *async_context;
+	GError *error = NULL;
+
+	async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+	camel_subscribable_unsubscribe_folder_sync (
+		CAMEL_SUBSCRIBABLE (object),
+		async_context->folder_name,
+		cancellable, &error);
+
+	if (error != NULL) {
+		g_simple_async_result_set_from_error (simple, error);
+		g_error_free (error);
+	}
+}
+
+static void
+subscribable_unsubscribe_folder (CamelSubscribable *subscribable,
+                                 const gchar *folder_name,
+                                 gint io_priority,
+                                 GCancellable *cancellable,
+                                 GAsyncReadyCallback callback,
+                                 gpointer user_data)
+{
+	GSimpleAsyncResult *simple;
+	AsyncContext *async_context;
+
+	async_context = g_slice_new0 (AsyncContext);
+	async_context->folder_name = g_strdup (folder_name);
+
+	simple = g_simple_async_result_new (
+		G_OBJECT (subscribable), callback,
+		user_data, subscribable_unsubscribe_folder);
+
+	g_simple_async_result_set_op_res_gpointer (
+		simple, async_context, (GDestroyNotify) async_context_free);
+
+	g_simple_async_result_run_in_thread (
+		simple, subscribable_unsubscribe_folder_thread,
+		io_priority, cancellable);
+
+	g_object_unref (simple);
+}
+
+static gboolean
+subscribable_unsubscribe_folder_finish (CamelSubscribable *subscribable,
+                                        GAsyncResult *result,
+                                        GError **error)
+{
+	GSimpleAsyncResult *simple;
+
+	g_return_val_if_fail (
+		g_simple_async_result_is_valid (
+		result, G_OBJECT (subscribable),
+		subscribable_unsubscribe_folder), FALSE);
+
+	simple = G_SIMPLE_ASYNC_RESULT (result);
+
+	/* Assume success unless a GError is set. */
+	return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+camel_subscribable_default_init (CamelSubscribableInterface *interface)
+{
+	interface->subscribe_folder = subscribable_subscribe_folder;
+	interface->subscribe_folder_finish = subscribable_subscribe_folder_finish;
+	interface->unsubscribe_folder = subscribable_unsubscribe_folder;
+	interface->unsubscribe_folder_finish = subscribable_unsubscribe_folder_finish;
+
+	signals[FOLDER_SUBSCRIBED] = g_signal_new (
+		"folder-subscribed",
+		G_OBJECT_CLASS_TYPE (interface),
+		G_SIGNAL_RUN_FIRST,
+		G_STRUCT_OFFSET (
+			CamelSubscribableInterface,
+			folder_subscribed),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__POINTER,
+		G_TYPE_NONE, 1,
+		G_TYPE_POINTER);
+
+	signals[FOLDER_UNSUBSCRIBED] = g_signal_new (
+		"folder-unsubscribed",
+		G_OBJECT_CLASS_TYPE (interface),
+		G_SIGNAL_RUN_FIRST,
+		G_STRUCT_OFFSET (
+			CamelSubscribableInterface,
+			folder_unsubscribed),
+		NULL, NULL,
+		g_cclosure_marshal_VOID__POINTER,
+		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 *interface;
+	gboolean is_subscribed;
+
+	g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE);
+	g_return_val_if_fail (folder_name != NULL, FALSE);
+
+	interface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable);
+	g_return_val_if_fail (interface->folder_is_subscribed != NULL, FALSE);
+
+	camel_store_lock (
+		CAMEL_STORE (subscribable),
+		CAMEL_STORE_FOLDER_LOCK);
+
+	is_subscribed = interface->folder_is_subscribed (
+		subscribable, folder_name);
+
+	camel_store_unlock (
+		CAMEL_STORE (subscribable),
+		CAMEL_STORE_FOLDER_LOCK);
+
+	return is_subscribed;
+}
+
+/**
+ * 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)
+{
+	CamelSubscribableInterface *interface;
+	gboolean success;
+
+	g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE);
+	g_return_val_if_fail (folder_name != NULL, FALSE);
+
+	interface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable);
+	g_return_val_if_fail (interface->subscribe_folder_sync != NULL, FALSE);
+
+	camel_store_lock (
+		CAMEL_STORE (subscribable),
+		CAMEL_STORE_FOLDER_LOCK);
+
+	/* Check for cancellation after locking. */
+	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
+		camel_store_unlock (
+			CAMEL_STORE (subscribable),
+			CAMEL_STORE_FOLDER_LOCK);
+		return FALSE;
+	}
+
+	success = interface->subscribe_folder_sync (
+		subscribable, folder_name, cancellable, error);
+	CAMEL_CHECK_GERROR (
+		subscribable, subscribe_folder_sync, success, error);
+
+	camel_store_unlock (
+		CAMEL_STORE (subscribable),
+		CAMEL_STORE_FOLDER_LOCK);
+
+	return 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)
+{
+	CamelSubscribableInterface *interface;
+
+	g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable));
+	g_return_if_fail (folder_name != NULL);
+
+	interface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable);
+	g_return_if_fail (interface->subscribe_folder != NULL);
+
+	interface->subscribe_folder (
+		subscribable, folder_name, io_priority,
+		cancellable, callback, user_data);
+}
+
+/**
+ * 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)
+{
+	CamelSubscribableInterface *interface;
+
+	g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE);
+	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+	interface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable);
+	g_return_val_if_fail (
+		interface->subscribe_folder_finish != NULL, FALSE);
+
+	return interface->subscribe_folder_finish (
+		subscribable, 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)
+{
+	CamelSubscribableInterface *interface;
+	gboolean success;
+
+	g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE);
+	g_return_val_if_fail (folder_name != NULL, FALSE);
+
+	interface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable);
+	g_return_val_if_fail (
+		interface->unsubscribe_folder_sync != NULL, FALSE);
+
+	camel_store_lock (
+		CAMEL_STORE (subscribable),
+		CAMEL_STORE_FOLDER_LOCK);
+
+	/* Check for cancellation after locking. */
+	if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
+		camel_store_unlock (
+			CAMEL_STORE (subscribable),
+			CAMEL_STORE_FOLDER_LOCK);
+		return FALSE;
+	}
+
+	success = interface->unsubscribe_folder_sync (
+		subscribable, folder_name, cancellable, error);
+	CAMEL_CHECK_GERROR (
+		subscribable, unsubscribe_folder_sync, success, error);
+
+	if (success)
+		subscribable_delete_cached_folder (
+			CAMEL_STORE (subscribable), folder_name);
+
+	camel_store_unlock (
+		CAMEL_STORE (subscribable),
+		CAMEL_STORE_FOLDER_LOCK);
+
+	return 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)
+{
+	CamelSubscribableInterface *interface;
+
+	g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable));
+	g_return_if_fail (folder_name != NULL);
+
+	interface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable);
+	g_return_if_fail (interface->unsubscribe_folder != NULL);
+
+	interface->unsubscribe_folder (
+		subscribable, folder_name, io_priority,
+		cancellable, callback, user_data);
+}
+
+/**
+ * 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)
+{
+	CamelSubscribableInterface *interface;
+
+	g_return_val_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable), FALSE);
+	g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+	interface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable);
+	g_return_val_if_fail (
+		interface->unsubscribe_folder_finish != NULL, FALSE);
+
+	return interface->unsubscribe_folder_finish (
+		subscribable, 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_DEFAULT_IDLE.
+ *
+ * This function is only intended for Camel providers.
+ *
+ * Since: 3.2
+ **/
+void
+camel_subscribable_folder_subscribed (CamelSubscribable *subscribable,
+                                      CamelFolderInfo *folder_info)
+{
+	SignalData *signal_data;
+
+	g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable));
+	g_return_if_fail (folder_info != NULL);
+
+	signal_data = g_slice_new0 (SignalData);
+	signal_data->subscribable = g_object_ref (subscribable);
+	signal_data->folder_info = camel_folder_info_clone (folder_info);
+
+	g_idle_add_full (
+		G_PRIORITY_DEFAULT_IDLE,
+		(GSourceFunc) subscribable_emit_folder_subscribed_cb,
+		signal_data, (GDestroyNotify) signal_data_free);
+}
+
+/**
+ * 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_DEFAULT_IDLE.
+ *
+ * This function is only intended for Camel providers.
+ *
+ * Since: 3.2
+ **/
+void
+camel_subscribable_folder_unsubscribed (CamelSubscribable *subscribable,
+                                        CamelFolderInfo *folder_info)
+{
+	SignalData *signal_data;
+
+	g_return_if_fail (CAMEL_IS_SUBSCRIBABLE (subscribable));
+	g_return_if_fail (folder_info != NULL);
+
+	signal_data = g_slice_new0 (SignalData);
+	signal_data->subscribable = g_object_ref (subscribable);
+	signal_data->folder_info = camel_folder_info_clone (folder_info);
+
+	g_idle_add_full (
+		G_PRIORITY_DEFAULT_IDLE,
+		(GSourceFunc) subscribable_emit_folder_unsubscribed_cb,
+		signal_data, (GDestroyNotify) signal_data_free);
+}
+
diff --git a/camel/camel-subscribable.h b/camel/camel-subscribable.h
new file mode 100644
index 0000000..5a9c0a1
--- /dev/null
+++ b/camel/camel-subscribable.h
@@ -0,0 +1,151 @@
+/*
+ * camel-subscribable.h
+ *
+ * This program 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; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program 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 the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#if !defined (__CAMEL_H_INSIDE__) && !defined (CAMEL_COMPILATION)
+#error "Only <camel/camel.h> can be included directly."
+#endif
+
+#ifndef CAMEL_SUBSCRIBABLE_H
+#define CAMEL_SUBSCRIBABLE_H
+
+#include <camel/camel-store.h>
+
+/* Standard GObject macros */
+#define CAMEL_TYPE_SUBSCRIBABLE \
+	(camel_subscribable_get_type ())
+#define CAMEL_SUBSCRIBABLE(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), CAMEL_TYPE_SUBSCRIBABLE, CamelSubscribable))
+#define CAMEL_SUBSCRIBABLE_INTERFACE(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), CAMEL_TYPE_SUBSCRIBABLE, CamelSubscribableInterface))
+#define CAMEL_IS_SUBSCRIBABLE(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), CAMEL_TYPE_SUBSCRIBABLE))
+#define CAMEL_IS_SUBSCRIBABLE_INTERFACE(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), CAMEL_TYPE_SUBSCRIBABLE))
+#define CAMEL_SUBSCRIBABLE_GET_INTERFACE(obj) \
+	(G_TYPE_INSTANCE_GET_INTERFACE \
+	((obj), CAMEL_TYPE_SUBSCRIBABLE, CamelSubscribableInterface))
+
+G_BEGIN_DECLS
+
+typedef struct _CamelSubscribable CamelSubscribable;
+typedef struct _CamelSubscribableInterface CamelSubscribableInterface;
+
+struct _CamelSubscribableInterface {
+	GTypeInterface parent_interface;
+
+	/* Non-Blocking Methods */
+	gboolean	(*folder_is_subscribed)
+					(CamelSubscribable *subscribable,
+					 const gchar *folder_name);
+
+	/* Synchronous I/O Methods */
+	gboolean	(*subscribe_folder_sync)
+					(CamelSubscribable *subscribable,
+					 const gchar *folder_name,
+					 GCancellable *cancellable,
+					 GError **error);
+	gboolean	(*unsubscribe_folder_sync)
+					(CamelSubscribable *subscribable,
+					 const gchar *folder_name,
+					 GCancellable *cancellable,
+					 GError **error);
+
+	/* Asynchronous I/O Methods (all have defaults) */
+	void		(*subscribe_folder)
+					(CamelSubscribable *subscribable,
+					 const gchar *folder_name,
+					 gint io_priority,
+					 GCancellable *cancellable,
+					 GAsyncReadyCallback callback,
+					 gpointer user_data);
+	gboolean	(*subscribe_folder_finish)
+					(CamelSubscribable *subscribable,
+					 GAsyncResult *result,
+					 GError **error);
+	void		(*unsubscribe_folder)
+					(CamelSubscribable *subscribable,
+					 const gchar *folder_name,
+					 gint io_priority,
+					 GCancellable *cancellable,
+					 GAsyncReadyCallback callback,
+					 gpointer user_data);
+	gboolean	(*unsubscribe_folder_finish)
+					(CamelSubscribable *subscribable,
+					 GAsyncResult *result,
+					 GError **error);
+
+	/* Signals */
+	void		(*folder_subscribed)
+					(CamelSubscribable *subscribable,
+					 CamelFolderInfo *folder_info);
+	void		(*folder_unsubscribed)
+					(CamelSubscribable *subscribable,
+					 CamelFolderInfo *folder_info);
+};
+
+GType		camel_subscribable_get_type
+					(void) G_GNUC_CONST;
+gboolean	camel_subscribable_folder_is_subscribed
+					(CamelSubscribable *subscribable,
+					 const gchar *folder_name);
+gboolean	camel_subscribable_subscribe_folder_sync
+					(CamelSubscribable *subscribable,
+					 const gchar *folder_name,
+					 GCancellable *cancellable,
+					 GError **error);
+void		camel_subscribable_subscribe_folder
+					(CamelSubscribable *subscribable,
+					 const gchar *folder_name,
+					 gint io_priority,
+					 GCancellable *cancellable,
+					 GAsyncReadyCallback callback,
+					 gpointer user_data);
+gboolean	camel_subscribable_subscribe_folder_finish
+					(CamelSubscribable *subscribable,
+					 GAsyncResult *result,
+					 GError **error);
+gboolean	camel_subscribable_unsubscribe_folder_sync
+					(CamelSubscribable *subscribable,
+					 const gchar *folder_name,
+					 GCancellable *cancellable,
+					 GError **error);
+void		camel_subscribable_unsubscribe_folder
+					(CamelSubscribable *subscribable,
+					 const gchar *folder_name,
+					 gint io_priority,
+					 GCancellable *cancellable,
+					 GAsyncReadyCallback callback,
+					 gpointer user_data);
+gboolean	camel_subscribable_unsubscribe_folder_finish
+					(CamelSubscribable *subscribable,
+					 GAsyncResult *result,
+					 GError **error);
+void		camel_subscribable_folder_subscribed
+					(CamelSubscribable *subscribable,
+					 CamelFolderInfo *folder_info);
+void		camel_subscribable_folder_unsubscribed
+					(CamelSubscribable *subscribable,
+					 CamelFolderInfo *folder_info);
+
+G_END_DECLS
+
+#endif /* CAMEL_SUBSCRIBABLE_H */
diff --git a/camel/camel.h b/camel/camel.h
index 8ee6c61..44baa86 100644
--- a/camel/camel.h
+++ b/camel/camel.h
@@ -125,6 +125,7 @@
 #include <camel/camel-stream-process.h>
 #include <camel/camel-stream-vfs.h>
 #include <camel/camel-string-utils.h>
+#include <camel/camel-subscribable.h>
 #include <camel/camel-tcp-stream.h>
 #include <camel/camel-tcp-stream-raw.h>
 #include <camel/camel-tcp-stream-ssl.h>
diff --git a/camel/providers/imap/camel-imap-store.c b/camel/providers/imap/camel-imap-store.c
index 2dcd896..3e59038 100644
--- a/camel/providers/imap/camel-imap-store.c
+++ b/camel/providers/imap/camel-imap-store.c
@@ -76,9 +76,9 @@ static gint compare_folder_name (gconstpointer a, gconstpointer b);
 static CamelFolderInfo *imap_store_create_folder_sync (CamelStore *store, const gchar *parent_name, const gchar *folder_name, GCancellable *cancellable, GError **error);
 static gboolean imap_store_delete_folder_sync (CamelStore *store, const gchar *folder_name, GCancellable *cancellable, GError **error);
 static gboolean imap_store_rename_folder_sync (CamelStore *store, const gchar *old_name, const gchar *new_name, GCancellable *cancellable, GError **error);
-static gboolean folder_is_subscribed (CamelStore *store, const gchar *folder_name);
-static gboolean imap_store_subscribe_folder_sync (CamelStore *store, const gchar *folder_name, GCancellable *cancellable, GError **error);
-static gboolean imap_store_unsubscribe_folder_sync (CamelStore *store, const gchar *folder_name, GCancellable *cancellable, GError **error);
+static gboolean imap_store_folder_is_subscribed (CamelSubscribable *subscribable, const gchar *folder_name);
+static gboolean imap_store_subscribe_folder_sync (CamelSubscribable *subscribable, const gchar *folder_name, GCancellable *cancellable, GError **error);
+static gboolean imap_store_unsubscribe_folder_sync (CamelSubscribable *subscribable, const gchar *folder_name, GCancellable *cancellable, GError **error);
 
 static gboolean get_folders_sync (CamelImapStore *imap_store, const gchar *pattern, GCancellable *cancellable, GError **error);
 
@@ -118,6 +118,7 @@ static GInitableIface *parent_initable_interface;
 /* Forward Declarations */
 static void camel_imap_store_initable_init (GInitableIface *interface);
 static void camel_network_service_init (CamelNetworkServiceInterface *interface);
+static void camel_subscribable_init (CamelSubscribableInterface *interface);
 
 G_DEFINE_TYPE_WITH_CODE (
 	CamelImapStore,
@@ -128,7 +129,10 @@ G_DEFINE_TYPE_WITH_CODE (
 		camel_imap_store_initable_init)
 	G_IMPLEMENT_INTERFACE (
 		CAMEL_TYPE_NETWORK_SERVICE,
-		camel_network_service_init))
+		camel_network_service_init)
+	G_IMPLEMENT_INTERFACE (
+		CAMEL_TYPE_SUBSCRIBABLE,
+		camel_subscribable_init))
 
 static void
 parse_capability (CamelImapStore *store, gchar *capa)
@@ -197,6 +201,31 @@ imap_get_capability (CamelService *service,
 	return TRUE;
 }
 
+/* folder_name is path name */
+static CamelFolderInfo *
+imap_build_folder_info (CamelImapStore *imap_store, const gchar *folder_name)
+{
+	const gchar *name;
+	CamelFolderInfo *fi;
+
+	fi = camel_folder_info_new ();
+	fi->full_name = g_strdup (folder_name);
+	fi->unread = -1;
+	fi->total = -1;
+
+	name = strrchr (fi->full_name, '/');
+	if (name == NULL)
+		name = fi->full_name;
+	else
+		name++;
+	if (!g_ascii_strcasecmp (fi->full_name, "INBOX"))
+		fi->display_name = g_strdup (_("Inbox"));
+	else
+		fi->display_name = g_strdup (name);
+
+	return fi;
+}
+
 static gboolean
 connect_to_server (CamelService *service,
                    GCancellable *cancellable,
@@ -1274,6 +1303,123 @@ imap_store_get_default_port (CamelNetworkService *service,
 	return default_port;
 }
 
+static gboolean
+imap_store_folder_is_subscribed (CamelSubscribable *subscribable,
+                                 const gchar *folder_name)
+{
+	CamelImapStore *imap_store = CAMEL_IMAP_STORE (subscribable);
+	CamelStoreInfo *si;
+	gint truth = FALSE;
+
+	si = camel_store_summary_path ((CamelStoreSummary *) imap_store->summary, folder_name);
+	if (si) {
+		truth = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0;
+		camel_store_summary_info_free ((CamelStoreSummary *) imap_store->summary, si);
+	}
+
+	return truth;
+}
+
+/* Note: folder_name must match a folder as listed with get_folder_info() -> full_name */
+static gboolean
+imap_store_subscribe_folder_sync (CamelSubscribable *subscribable,
+                                  const gchar *folder_name,
+                                  GCancellable *cancellable,
+                                  GError **error)
+{
+	CamelService *service;
+	CamelImapStore *imap_store;
+	CamelImapResponse *response;
+	CamelFolderInfo *fi;
+	CamelStoreInfo *si;
+	gboolean success = TRUE;
+
+	service = CAMEL_SERVICE (subscribable);
+	imap_store = CAMEL_IMAP_STORE (subscribable);
+
+	camel_service_lock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
+
+	if (!camel_imap_store_connected (imap_store, error)) {
+		success = FALSE;
+		goto done;
+	}
+
+	response = camel_imap_command (imap_store, NULL, cancellable, error,
+				       "SUBSCRIBE %F", folder_name);
+	if (!response) {
+		success = FALSE;
+		goto done;
+	}
+
+	camel_imap_response_free (imap_store, response);
+
+	si = camel_store_summary_path ((CamelStoreSummary *) imap_store->summary, folder_name);
+	if (si) {
+		if ((si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) == 0) {
+			si->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
+			camel_store_summary_touch ((CamelStoreSummary *) imap_store->summary);
+			camel_store_summary_save ((CamelStoreSummary *) imap_store->summary);
+		}
+		camel_store_summary_info_free ((CamelStoreSummary *) imap_store->summary, si);
+	}
+
+	if (imap_store->renaming) {
+		/* we don't need to emit a "folder_subscribed" signal
+		   if we are in the process of renaming folders, so we
+		   are done here... */
+		goto done;
+	}
+
+	fi = imap_build_folder_info (imap_store, folder_name);
+	fi->flags |= CAMEL_FOLDER_NOCHILDREN;
+
+	camel_subscribable_folder_subscribed (subscribable, fi);
+	camel_folder_info_free (fi);
+
+done:
+	camel_service_unlock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
+
+	return success;
+}
+
+static gboolean
+imap_store_unsubscribe_folder_sync (CamelSubscribable *subscribable,
+                                    const gchar *folder_name,
+                                    GCancellable *cancellable,
+                                    GError **error)
+{
+	CamelService *service;
+	CamelImapStore *imap_store;
+	CamelImapResponse *response;
+	gboolean success = TRUE;
+
+	service = CAMEL_SERVICE (subscribable);
+	imap_store = CAMEL_IMAP_STORE (subscribable);
+
+	camel_service_lock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
+
+	if (!camel_imap_store_connected (imap_store, error)) {
+		success = FALSE;
+		goto done;
+	}
+
+	response = camel_imap_command (imap_store, NULL, cancellable, error,
+				       "UNSUBSCRIBE %F", folder_name);
+	if (!response) {
+		success = FALSE;
+		goto done;
+	}
+
+	camel_imap_response_free (imap_store, response);
+
+	success = imap_folder_effectively_unsubscribed (imap_store, folder_name, error);
+
+done:
+	camel_service_unlock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
+
+	return success;
+}
+
 static void
 camel_imap_store_class_init (CamelImapStoreClass *class)
 {
@@ -1296,7 +1442,6 @@ camel_imap_store_class_init (CamelImapStoreClass *class)
 	store_class->hash_folder_name = hash_folder_name;
 	store_class->compare_folder_name = compare_folder_name;
 	store_class->can_refresh_folder = imap_can_refresh_folder;
-	store_class->folder_is_subscribed = folder_is_subscribed;
 	store_class->free_folder_info = camel_store_free_folder_info_full;
 	store_class->get_folder_sync = imap_store_get_folder_sync;
 	store_class->get_folder_info_sync = imap_store_get_folder_info_sync;
@@ -1305,8 +1450,6 @@ camel_imap_store_class_init (CamelImapStoreClass *class)
 	store_class->create_folder_sync = imap_store_create_folder_sync;
 	store_class->delete_folder_sync = imap_store_delete_folder_sync;
 	store_class->rename_folder_sync = imap_store_rename_folder_sync;
-	store_class->subscribe_folder_sync = imap_store_subscribe_folder_sync;
-	store_class->unsubscribe_folder_sync = imap_store_unsubscribe_folder_sync;
 	store_class->noop_sync = imap_store_noop_sync;
 }
 
@@ -1326,6 +1469,14 @@ camel_network_service_init (CamelNetworkServiceInterface *interface)
 }
 
 static void
+camel_subscribable_init (CamelSubscribableInterface *interface)
+{
+	interface->folder_is_subscribed = imap_store_folder_is_subscribed;
+	interface->subscribe_folder_sync = imap_store_subscribe_folder_sync;
+	interface->unsubscribe_folder_sync = imap_store_unsubscribe_folder_sync;
+}
+
+static void
 camel_imap_store_init (CamelImapStore *imap_store)
 {
 	imap_store->istream = NULL;
@@ -1336,7 +1487,6 @@ camel_imap_store_init (CamelImapStore *imap_store)
 	imap_store->current_folder = NULL;
 	imap_store->connected = FALSE;
 	imap_store->preauthed = FALSE;
-	((CamelStore *) imap_store)->flags |= CAMEL_STORE_SUBSCRIPTIONS;
 
 	imap_store->tag_prefix = imap_tag_prefix++;
 	if (imap_tag_prefix > 'Z')
@@ -1355,40 +1505,6 @@ imap_set_server_level (CamelImapStore *store)
 		store->server_level = IMAP_LEVEL_UNKNOWN;
 }
 
-/* folder_name is path name */
-static CamelFolderInfo *
-imap_build_folder_info (CamelImapStore *imap_store, const gchar *folder_name)
-{
-	const gchar *name;
-	CamelFolderInfo *fi;
-
-	fi = camel_folder_info_new ();
-	fi->full_name = g_strdup (folder_name);
-	fi->unread = -1;
-	fi->total = -1;
-
-	name = strrchr (fi->full_name, '/');
-	if (name == NULL)
-		name = fi->full_name;
-	else
-		name++;
-	if (!g_ascii_strcasecmp (fi->full_name, "INBOX"))
-		fi->display_name = g_strdup (_("Inbox"));
-	/* Do not localize the rest, these are from a server, thus shouldn't be localized */
-	/*else if (!g_ascii_strcasecmp (fi->full_name, "Drafts"))
-		fi->display_name = g_strdup (_("Drafts"));
-	else if (!g_ascii_strcasecmp (fi->full_name, "Sent"))
-		fi->display_name = g_strdup (_("Sent"));
-	else if (!g_ascii_strcasecmp (fi->full_name, "Templates"))
-		fi->display_name = g_strdup (_("Templates"));
-	else if (!g_ascii_strcasecmp (fi->full_name, "Trash"))
-		fi->display_name = g_strdup (_("Trash"));*/
-	else
-		fi->display_name = g_strdup (name);
-
-	return fi;
-}
-
 static gboolean
 imap_folder_effectively_unsubscribed (CamelImapStore *imap_store,
                                       const gchar *folder_name,
@@ -1416,7 +1532,8 @@ imap_folder_effectively_unsubscribed (CamelImapStore *imap_store,
 	}
 
 	fi = imap_build_folder_info (imap_store, folder_name);
-	camel_store_folder_unsubscribed (CAMEL_STORE (imap_store), fi);
+	camel_subscribable_folder_unsubscribed (
+		CAMEL_SUBSCRIBABLE (imap_store), fi);
 	camel_folder_info_free (fi);
 
 	return TRUE;
@@ -2113,10 +2230,12 @@ manage_subscriptions (CamelStore *store,
 			if (strncmp (path, old_name, olen) == 0) {
 				if (subscribe)
 					imap_store_subscribe_folder_sync (
-						store, path, cancellable, NULL);
+						CAMEL_SUBSCRIBABLE (store),
+						path, cancellable, NULL);
 				else
 					imap_store_unsubscribe_folder_sync (
-						store, path, cancellable, NULL);
+						CAMEL_SUBSCRIBABLE (store),
+						path, cancellable, NULL);
 			}
 			camel_store_summary_info_free ((CamelStoreSummary *) imap_store->summary, si);
 		}
@@ -2650,7 +2769,7 @@ get_folders_sync (CamelImapStore *imap_store,
 					camel_store_summary_touch ((CamelStoreSummary *) imap_store->summary);
 
 					camel_store_folder_created (CAMEL_STORE (imap_store), fi);
-					camel_store_folder_subscribed (CAMEL_STORE (imap_store), fi);
+					camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (imap_store), fi);
 				}
 			} else {
 				gchar *dup_folder_name = g_strdup (camel_store_info_path (imap_store->summary, si));
@@ -3008,114 +3127,6 @@ get_folder_info_offline (CamelStore *store, const gchar *top,
 	return fi;
 }
 
-static gboolean
-folder_is_subscribed (CamelStore *store,
-                      const gchar *folder_name)
-{
-	CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
-	CamelStoreInfo *si;
-	gint truth = FALSE;
-
-	si = camel_store_summary_path ((CamelStoreSummary *) imap_store->summary, folder_name);
-	if (si) {
-		truth = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0;
-		camel_store_summary_info_free ((CamelStoreSummary *) imap_store->summary, si);
-	}
-
-	return truth;
-}
-
-/* Note: folder_name must match a folder as listed with get_folder_info() -> full_name */
-static gboolean
-imap_store_subscribe_folder_sync (CamelStore *store,
-                                  const gchar *folder_name,
-                                  GCancellable *cancellable,
-                                  GError **error)
-{
-	CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
-	CamelImapResponse *response;
-	CamelFolderInfo *fi;
-	CamelStoreInfo *si;
-	gboolean success = TRUE;
-
-	camel_service_lock (CAMEL_SERVICE (store), CAMEL_SERVICE_REC_CONNECT_LOCK);
-
-	if (!camel_imap_store_connected (imap_store, error)) {
-		success = FALSE;
-		goto done;
-	}
-
-	response = camel_imap_command (imap_store, NULL, cancellable, error,
-				       "SUBSCRIBE %F", folder_name);
-	if (!response) {
-		success = FALSE;
-		goto done;
-	}
-
-	camel_imap_response_free (imap_store, response);
-
-	si = camel_store_summary_path ((CamelStoreSummary *) imap_store->summary, folder_name);
-	if (si) {
-		if ((si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) == 0) {
-			si->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
-			camel_store_summary_touch ((CamelStoreSummary *) imap_store->summary);
-			camel_store_summary_save ((CamelStoreSummary *) imap_store->summary);
-		}
-		camel_store_summary_info_free ((CamelStoreSummary *) imap_store->summary, si);
-	}
-
-	if (imap_store->renaming) {
-		/* we don't need to emit a "folder_subscribed" signal
-		   if we are in the process of renaming folders, so we
-		   are done here... */
-		goto done;
-	}
-
-	fi = imap_build_folder_info (imap_store, folder_name);
-	fi->flags |= CAMEL_FOLDER_NOCHILDREN;
-
-	camel_store_folder_subscribed (store, fi);
-	camel_folder_info_free (fi);
-done:
-	camel_service_unlock (CAMEL_SERVICE (store), CAMEL_SERVICE_REC_CONNECT_LOCK);
-
-	return success;
-}
-
-static gboolean
-imap_store_unsubscribe_folder_sync (CamelStore *store,
-                                    const gchar *folder_name,
-                                    GCancellable *cancellable,
-                                    GError **error)
-{
-	CamelImapStore *imap_store = CAMEL_IMAP_STORE (store);
-	CamelImapResponse *response;
-	gboolean success = TRUE;
-
-	camel_service_lock (CAMEL_SERVICE (store), CAMEL_SERVICE_REC_CONNECT_LOCK);
-
-	if (!camel_imap_store_connected (imap_store, error)) {
-		success = FALSE;
-		goto done;
-	}
-
-	response = camel_imap_command (imap_store, NULL, cancellable, error,
-				       "UNSUBSCRIBE %F", folder_name);
-	if (!response) {
-		success = FALSE;
-		goto done;
-	}
-
-	camel_imap_response_free (imap_store, response);
-
-	success = imap_folder_effectively_unsubscribed (imap_store, folder_name, error);
-
-done:
-	camel_service_unlock (CAMEL_SERVICE (store), CAMEL_SERVICE_REC_CONNECT_LOCK);
-
-	return success;
-}
-
 #if 0
 static gboolean
 folder_flags_have_changed (CamelFolder *folder)
diff --git a/camel/providers/imapx/camel-imapx-store.c b/camel/providers/imapx/camel-imapx-store.c
index 034d2af..8c5263d 100644
--- a/camel/providers/imapx/camel-imapx-store.c
+++ b/camel/providers/imapx/camel-imapx-store.c
@@ -57,6 +57,7 @@ static GInitableIface *parent_initable_interface;
 /* Forward Declarations */
 static void camel_imapx_store_initable_init (GInitableIface *interface);
 static void camel_network_service_init (CamelNetworkServiceInterface *interface);
+static void camel_subscribable_init (CamelSubscribableInterface *interface);
 
 G_DEFINE_TYPE_WITH_CODE (
 	CamelIMAPXStore,
@@ -67,7 +68,10 @@ G_DEFINE_TYPE_WITH_CODE (
 		camel_imapx_store_initable_init)
 	G_IMPLEMENT_INTERFACE (
 		CAMEL_TYPE_NETWORK_SERVICE,
-		camel_network_service_init))
+		camel_network_service_init)
+	G_IMPLEMENT_INTERFACE (
+		CAMEL_TYPE_SUBSCRIBABLE,
+		camel_subscribable_init))
 
 static guint
 imapx_name_hash (gconstpointer key)
@@ -441,7 +445,8 @@ imapx_unmark_folder_subscribed (CamelIMAPXStore *istore, const gchar *folder_nam
 		CamelFolderInfo *fi;
 
 		fi = imapx_build_folder_info (istore, folder_name);
-		camel_store_folder_unsubscribed (CAMEL_STORE (istore), fi);
+		camel_subscribable_folder_unsubscribed (
+			CAMEL_SUBSCRIBABLE (istore), fi);
 		camel_folder_info_free (fi);
 	}
 }
@@ -465,7 +470,8 @@ imapx_mark_folder_subscribed (CamelIMAPXStore *istore, const gchar *folder_name,
 		CamelFolderInfo *fi;
 
 		fi = imapx_build_folder_info (istore, folder_name);
-		camel_store_folder_subscribed (CAMEL_STORE (istore), fi);
+		camel_subscribable_folder_subscribed (
+			CAMEL_SUBSCRIBABLE (istore), fi);
 		camel_folder_info_free (fi);
 	}
 }
@@ -952,7 +958,7 @@ sync_folders (CamelIMAPXStore *istore,
 					camel_store_summary_touch ((CamelStoreSummary *) istore->summary);
 
 					camel_store_folder_created (CAMEL_STORE (istore), fi);
-					camel_store_folder_subscribed (CAMEL_STORE (istore), fi);
+					camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (istore), fi);
 				}
 			} else {
 				gchar *dup_folder_name = g_strdup (camel_store_info_path (istore->summary, si));
@@ -1064,23 +1070,6 @@ imapx_can_refresh_folder (CamelStore *store,
 	return res;
 }
 
-static gboolean
-imapx_folder_is_subscribed (CamelStore *store,
-                            const gchar *folder_name)
-{
-	CamelIMAPXStore *istore = CAMEL_IMAPX_STORE (store);
-	CamelStoreInfo *si;
-	gint is_subscribed = FALSE;
-
-	si = camel_store_summary_path ((CamelStoreSummary *) istore->summary, folder_name);
-	if (si) {
-		is_subscribed = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0;
-		camel_store_summary_info_free ((CamelStoreSummary *) istore->summary, si);
-	}
-
-	return is_subscribed;
-}
-
 static CamelFolder *
 imapx_store_get_folder_sync (CamelStore *store,
                              const gchar *folder_name,
@@ -1466,26 +1455,6 @@ imapx_store_rename_folder_sync (CamelStore *store,
 }
 
 static gboolean
-imapx_store_subscribe_folder_sync (CamelStore *store,
-                                   const gchar *folder_name,
-                                   GCancellable *cancellable,
-                                   GError **error)
-{
-	return imapx_subscribe_folder (
-		store, folder_name, TRUE, cancellable, error);
-}
-
-static gboolean
-imapx_store_unsubscribe_folder_sync (CamelStore *store,
-                                     const gchar *folder_name,
-                                     GCancellable *cancellable,
-                                     GError **error)
-{
-	return imapx_unsubscribe_folder (
-		store, folder_name, TRUE, cancellable, error);
-}
-
-static gboolean
 imapx_store_noop_sync (CamelStore *store,
                        GCancellable *cancellable,
                        GError **error)
@@ -1589,6 +1558,45 @@ imapx_store_get_default_port (CamelNetworkService *service,
 	return default_port;
 }
 
+static gboolean
+imapx_store_folder_is_subscribed (CamelSubscribable *subscribable,
+                                  const gchar *folder_name)
+{
+	CamelIMAPXStore *istore = CAMEL_IMAPX_STORE (subscribable);
+	CamelStoreInfo *si;
+	gint is_subscribed = FALSE;
+
+	si = camel_store_summary_path ((CamelStoreSummary *) istore->summary, folder_name);
+	if (si) {
+		is_subscribed = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0;
+		camel_store_summary_info_free ((CamelStoreSummary *) istore->summary, si);
+	}
+
+	return is_subscribed;
+}
+
+static gboolean
+imapx_store_subscribe_folder_sync (CamelSubscribable *subscribable,
+                                   const gchar *folder_name,
+                                   GCancellable *cancellable,
+                                   GError **error)
+{
+	return imapx_subscribe_folder (
+		CAMEL_STORE (subscribable),
+		folder_name, TRUE, cancellable, error);
+}
+
+static gboolean
+imapx_store_unsubscribe_folder_sync (CamelSubscribable *subscribable,
+                                     const gchar *folder_name,
+                                     GCancellable *cancellable,
+                                     GError **error)
+{
+	return imapx_unsubscribe_folder (
+		CAMEL_STORE (subscribable),
+		folder_name, TRUE, cancellable, error);
+}
+
 static void
 camel_imapx_store_class_init (CamelIMAPXStoreClass *class)
 {
@@ -1611,7 +1619,6 @@ camel_imapx_store_class_init (CamelIMAPXStoreClass *class)
 	store_class->hash_folder_name = imapx_name_hash;
 	store_class->compare_folder_name = imapx_name_equal;
 	store_class->can_refresh_folder = imapx_can_refresh_folder;
-	store_class->folder_is_subscribed = imapx_folder_is_subscribed;
 	store_class->free_folder_info = camel_store_free_folder_info_full;
 	store_class->get_folder_sync = imapx_store_get_folder_sync;
 	store_class->get_folder_info_sync = imapx_store_get_folder_info_sync;
@@ -1620,8 +1627,6 @@ camel_imapx_store_class_init (CamelIMAPXStoreClass *class)
 	store_class->create_folder_sync = imapx_store_create_folder_sync;
 	store_class->delete_folder_sync = imapx_store_delete_folder_sync;
 	store_class->rename_folder_sync = imapx_store_rename_folder_sync;
-	store_class->subscribe_folder_sync = imapx_store_subscribe_folder_sync;
-	store_class->unsubscribe_folder_sync = imapx_store_unsubscribe_folder_sync;
 	store_class->noop_sync = imapx_store_noop_sync;
 }
 
@@ -1641,14 +1646,19 @@ camel_network_service_init (CamelNetworkServiceInterface *interface)
 }
 
 static void
-camel_imapx_store_init (CamelIMAPXStore *istore)
+camel_subscribable_init (CamelSubscribableInterface *interface)
 {
-	CamelStore *store = CAMEL_STORE (istore);
+	interface->folder_is_subscribed = imapx_store_folder_is_subscribed;
+	interface->subscribe_folder_sync = imapx_store_subscribe_folder_sync;
+	interface->unsubscribe_folder_sync = imapx_store_unsubscribe_folder_sync;
+}
 
-	store->flags |= CAMEL_STORE_ASYNC | CAMEL_STORE_SUBSCRIPTIONS;
+static void
+camel_imapx_store_init (CamelIMAPXStore *istore)
+{
 	istore->get_finfo_lock = g_mutex_new ();
 	istore->last_refresh_time = time (NULL) - (FINFO_REFRESH_INTERVAL + 10);
 	istore->dir_sep = '/';
-	istore->con_man = camel_imapx_conn_manager_new (store);
+	istore->con_man = camel_imapx_conn_manager_new (CAMEL_STORE (istore));
 }
 
diff --git a/camel/providers/nntp/camel-nntp-store.c b/camel/providers/nntp/camel-nntp-store.c
index 746aa93..469f476 100644
--- a/camel/providers/nntp/camel-nntp-store.c
+++ b/camel/providers/nntp/camel-nntp-store.c
@@ -59,6 +59,7 @@ static GInitableIface *parent_initable_interface;
 /* Forward Declarations */
 static void camel_nntp_store_initable_init (GInitableIface *interface);
 static void camel_network_service_init (CamelNetworkServiceInterface *interface);
+static void camel_subscribable_init (CamelSubscribableInterface *interface);
 
 G_DEFINE_TYPE_WITH_CODE (
 	CamelNNTPStore,
@@ -69,7 +70,10 @@ G_DEFINE_TYPE_WITH_CODE (
 		camel_nntp_store_initable_init)
 	G_IMPLEMENT_INTERFACE (
 		CAMEL_TYPE_NETWORK_SERVICE,
-		camel_network_service_init))
+		camel_network_service_init)
+	G_IMPLEMENT_INTERFACE (
+		CAMEL_TYPE_SUBSCRIBABLE,
+		camel_subscribable_init))
 
 static gint
 camel_nntp_try_authenticate (CamelNNTPStore *store,
@@ -1127,121 +1131,6 @@ nntp_get_folder_info_offline (CamelStore *store,
 		store, top, flags, FALSE, cancellable, error);
 }
 
-static gboolean
-nntp_store_folder_is_subscribed (CamelStore *store, const gchar *folder_name)
-{
-	CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (store);
-	CamelStoreInfo *si;
-	gint truth = FALSE;
-
-	si = camel_store_summary_path ((CamelStoreSummary *) nntp_store->summary, folder_name);
-	if (si) {
-		truth = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0;
-		camel_store_summary_info_free ((CamelStoreSummary *) nntp_store->summary, si);
-	}
-
-	return truth;
-}
-
-static gboolean
-nntp_store_subscribe_folder_sync (CamelStore *store,
-                                  const gchar *folder_name,
-                                  GCancellable *cancellable,
-                                  GError **error)
-{
-	CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (store);
-	CamelService *service;
-	CamelSettings *settings;
-	CamelStoreInfo *si;
-	CamelFolderInfo *fi;
-	gboolean short_folder_names;
-	gboolean success = TRUE;
-
-	service = CAMEL_SERVICE (store);
-	settings = camel_service_get_settings (service);
-
-	short_folder_names = camel_nntp_settings_get_short_folder_names (
-		CAMEL_NNTP_SETTINGS (settings));
-
-	camel_service_lock (CAMEL_SERVICE (nntp_store), CAMEL_SERVICE_REC_CONNECT_LOCK);
-
-	si = camel_store_summary_path (CAMEL_STORE_SUMMARY (nntp_store->summary), folder_name);
-	if (!si) {
-		g_set_error (
-			error, CAMEL_FOLDER_ERROR,
-			CAMEL_FOLDER_ERROR_INVALID,
-			_("You cannot subscribe to this newsgroup:\n\n"
-			  "No such newsgroup. The selected item is a "
-			  "probably a parent folder."));
-		success = FALSE;
-	} else {
-		if (!(si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)) {
-			si->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
-			fi = nntp_folder_info_from_store_info (nntp_store, short_folder_names, si);
-			fi->flags |= CAMEL_FOLDER_NOINFERIORS | CAMEL_FOLDER_NOCHILDREN;
-			camel_store_summary_touch ((CamelStoreSummary *) nntp_store->summary);
-			camel_store_summary_save ((CamelStoreSummary *) nntp_store->summary);
-			camel_service_unlock (CAMEL_SERVICE (nntp_store), CAMEL_SERVICE_REC_CONNECT_LOCK);
-			camel_store_folder_subscribed (CAMEL_STORE (nntp_store), fi);
-			camel_folder_info_free (fi);
-			return TRUE;
-		}
-	}
-
-	camel_service_unlock (CAMEL_SERVICE (nntp_store), CAMEL_SERVICE_REC_CONNECT_LOCK);
-
-	return success;
-}
-
-static gboolean
-nntp_store_unsubscribe_folder_sync (CamelStore *store,
-                                    const gchar *folder_name,
-                                    GCancellable *cancellable,
-                                    GError **error)
-{
-	CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (store);
-	CamelService *service;
-	CamelSettings *settings;
-	CamelFolderInfo *fi;
-	CamelStoreInfo *fitem;
-	gboolean short_folder_names;
-	gboolean success = TRUE;
-
-	service = CAMEL_SERVICE (store);
-	settings = camel_service_get_settings (service);
-
-	short_folder_names = camel_nntp_settings_get_short_folder_names (
-		CAMEL_NNTP_SETTINGS (settings));
-
-	camel_service_lock (CAMEL_SERVICE (nntp_store), CAMEL_SERVICE_REC_CONNECT_LOCK);
-
-	fitem = camel_store_summary_path (CAMEL_STORE_SUMMARY (nntp_store->summary), folder_name);
-
-	if (!fitem) {
-		g_set_error (
-			error, CAMEL_FOLDER_ERROR,
-			CAMEL_FOLDER_ERROR_INVALID,
-			_("You cannot unsubscribe to this newsgroup:\n\n"
-			  "newsgroup does not exist!"));
-		success = FALSE;
-	} else {
-		if (fitem->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) {
-			fitem->flags &= ~CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
-			fi = nntp_folder_info_from_store_info (nntp_store, short_folder_names, fitem);
-			camel_store_summary_touch ((CamelStoreSummary *) nntp_store->summary);
-			camel_store_summary_save ((CamelStoreSummary *) nntp_store->summary);
-			camel_service_unlock (CAMEL_SERVICE (nntp_store), CAMEL_SERVICE_REC_CONNECT_LOCK);
-			camel_store_folder_unsubscribed (CAMEL_STORE (nntp_store), fi);
-			camel_folder_info_free (fi);
-			return TRUE;
-		}
-	}
-
-	camel_service_unlock (CAMEL_SERVICE (nntp_store), CAMEL_SERVICE_REC_CONNECT_LOCK);
-
-	return success;
-}
-
 /* stubs for various folder operations we're not implementing */
 
 static CamelFolderInfo *
@@ -1281,8 +1170,14 @@ nntp_store_delete_folder_sync (CamelStore *store,
                                GCancellable *cancellable,
                                GError **error)
 {
-	nntp_store_unsubscribe_folder_sync (
-		store, folder_name, cancellable, NULL);
+	CamelSubscribable *subscribable;
+	CamelSubscribableInterface *interface;
+
+	subscribable = CAMEL_SUBSCRIBABLE (store);
+	interface = CAMEL_SUBSCRIBABLE_GET_INTERFACE (subscribable);
+
+	interface->unsubscribe_folder_sync (
+		subscribable, folder_name, cancellable, NULL);
 
 	g_set_error (
 		error, CAMEL_FOLDER_ERROR,
@@ -1387,6 +1282,122 @@ nntp_store_get_default_port (CamelNetworkService *service,
 	return default_port;
 }
 
+static gboolean
+nntp_store_folder_is_subscribed (CamelSubscribable *subscribable,
+                                 const gchar *folder_name)
+{
+	CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (subscribable);
+	CamelStoreInfo *si;
+	gint truth = FALSE;
+
+	si = camel_store_summary_path ((CamelStoreSummary *) nntp_store->summary, folder_name);
+	if (si) {
+		truth = (si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) != 0;
+		camel_store_summary_info_free ((CamelStoreSummary *) nntp_store->summary, si);
+	}
+
+	return truth;
+}
+
+static gboolean
+nntp_store_subscribe_folder_sync (CamelSubscribable *subscribable,
+                                  const gchar *folder_name,
+                                  GCancellable *cancellable,
+                                  GError **error)
+{
+	CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (subscribable);
+	CamelService *service;
+	CamelSettings *settings;
+	CamelStoreInfo *si;
+	CamelFolderInfo *fi;
+	gboolean short_folder_names;
+	gboolean success = TRUE;
+
+	service = CAMEL_SERVICE (subscribable);
+	settings = camel_service_get_settings (service);
+
+	short_folder_names = camel_nntp_settings_get_short_folder_names (
+		CAMEL_NNTP_SETTINGS (settings));
+
+	camel_service_lock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
+
+	si = camel_store_summary_path (CAMEL_STORE_SUMMARY (nntp_store->summary), folder_name);
+	if (!si) {
+		g_set_error (
+			error, CAMEL_FOLDER_ERROR,
+			CAMEL_FOLDER_ERROR_INVALID,
+			_("You cannot subscribe to this newsgroup:\n\n"
+			  "No such newsgroup. The selected item is a "
+			  "probably a parent folder."));
+		success = FALSE;
+	} else {
+		if (!(si->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED)) {
+			si->flags |= CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
+			fi = nntp_folder_info_from_store_info (nntp_store, short_folder_names, si);
+			fi->flags |= CAMEL_FOLDER_NOINFERIORS | CAMEL_FOLDER_NOCHILDREN;
+			camel_store_summary_touch ((CamelStoreSummary *) nntp_store->summary);
+			camel_store_summary_save ((CamelStoreSummary *) nntp_store->summary);
+			camel_service_unlock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
+			camel_subscribable_folder_subscribed (subscribable, fi);
+			camel_folder_info_free (fi);
+			return TRUE;
+		}
+	}
+
+	camel_service_unlock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
+
+	return success;
+}
+
+static gboolean
+nntp_store_unsubscribe_folder_sync (CamelSubscribable *subscribable,
+                                    const gchar *folder_name,
+                                    GCancellable *cancellable,
+                                    GError **error)
+{
+	CamelNNTPStore *nntp_store = CAMEL_NNTP_STORE (subscribable);
+	CamelService *service;
+	CamelSettings *settings;
+	CamelFolderInfo *fi;
+	CamelStoreInfo *fitem;
+	gboolean short_folder_names;
+	gboolean success = TRUE;
+
+	service = CAMEL_SERVICE (subscribable);
+	settings = camel_service_get_settings (service);
+
+	short_folder_names = camel_nntp_settings_get_short_folder_names (
+		CAMEL_NNTP_SETTINGS (settings));
+
+	camel_service_lock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
+
+	fitem = camel_store_summary_path (CAMEL_STORE_SUMMARY (nntp_store->summary), folder_name);
+
+	if (!fitem) {
+		g_set_error (
+			error, CAMEL_FOLDER_ERROR,
+			CAMEL_FOLDER_ERROR_INVALID,
+			_("You cannot unsubscribe to this newsgroup:\n\n"
+			  "newsgroup does not exist!"));
+		success = FALSE;
+	} else {
+		if (fitem->flags & CAMEL_STORE_INFO_FOLDER_SUBSCRIBED) {
+			fitem->flags &= ~CAMEL_STORE_INFO_FOLDER_SUBSCRIBED;
+			fi = nntp_folder_info_from_store_info (nntp_store, short_folder_names, fitem);
+			camel_store_summary_touch ((CamelStoreSummary *) nntp_store->summary);
+			camel_store_summary_save ((CamelStoreSummary *) nntp_store->summary);
+			camel_service_unlock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
+			camel_subscribable_folder_unsubscribed (subscribable, fi);
+			camel_folder_info_free (fi);
+			return TRUE;
+		}
+	}
+
+	camel_service_unlock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
+
+	return success;
+}
+
 static void
 camel_nntp_store_class_init (CamelNNTPStoreClass *class)
 {
@@ -1407,12 +1418,9 @@ camel_nntp_store_class_init (CamelNNTPStoreClass *class)
 	store_class = CAMEL_STORE_CLASS (class);
 	store_class->can_refresh_folder = nntp_can_refresh_folder;
 	store_class->free_folder_info = camel_store_free_folder_info_full;
-	store_class->folder_is_subscribed = nntp_store_folder_is_subscribed;
 	store_class->create_folder_sync = nntp_store_create_folder_sync;
 	store_class->delete_folder_sync = nntp_store_delete_folder_sync;
 	store_class->rename_folder_sync = nntp_store_rename_folder_sync;
-	store_class->subscribe_folder_sync = nntp_store_subscribe_folder_sync;
-	store_class->unsubscribe_folder_sync = nntp_store_unsubscribe_folder_sync;
 
 	disco_store_class = CAMEL_DISCO_STORE_CLASS (class);
 	disco_store_class->can_work_offline = nntp_can_work_offline;
@@ -1444,13 +1452,20 @@ camel_network_service_init (CamelNetworkServiceInterface *interface)
 }
 
 static void
-camel_nntp_store_init (CamelNNTPStore *nntp_store)
+camel_subscribable_init (CamelSubscribableInterface *interface)
 {
-	CamelStore *store = CAMEL_STORE (nntp_store);
-
-	store->flags = CAMEL_STORE_SUBSCRIPTIONS;
+	interface->folder_is_subscribed = nntp_store_folder_is_subscribed;
+	interface->subscribe_folder_sync = nntp_store_subscribe_folder_sync;
+	interface->unsubscribe_folder_sync = nntp_store_unsubscribe_folder_sync;
+}
 
+static void
+camel_nntp_store_init (CamelNNTPStore *nntp_store)
+{
 	nntp_store->mem = (CamelStreamMem *) camel_stream_mem_new ();
+
+	/* Clear the VJUNK and VTRASH flags, which are set by default. */
+	CAMEL_STORE (nntp_store)->flags = 0;
 }
 
 /* Enter owning lock */
diff --git a/docs/reference/camel/camel-docs.sgml b/docs/reference/camel/camel-docs.sgml
index a1097aa..621f2d4 100644
--- a/docs/reference/camel/camel-docs.sgml
+++ b/docs/reference/camel/camel-docs.sgml
@@ -92,6 +92,7 @@
       <xi:include href="xml/camel-service.xml"/>
       <xi:include href="xml/camel-network-service.xml"/>
       <xi:include href="xml/camel-store.xml"/>
+      <xi:include href="xml/camel-subscribable.xml"/>
       <xi:include href="xml/camel-store-summary.xml"/>
       <xi:include href="xml/camel-offline-store.xml"/>
       <xi:include href="xml/camel-transport.xml"/>
diff --git a/docs/reference/camel/camel-sections.txt b/docs/reference/camel/camel-sections.txt
index bf57731..7522fc7 100644
--- a/docs/reference/camel/camel-sections.txt
+++ b/docs/reference/camel/camel-sections.txt
@@ -2098,8 +2098,6 @@ camel_store_folder_created
 camel_store_folder_deleted
 camel_store_folder_opened
 camel_store_folder_renamed
-camel_store_folder_subscribed
-camel_store_folder_unsubscribed
 camel_store_free_folder_info
 camel_store_free_folder_info_full
 camel_store_free_folder_info_nop
@@ -2107,8 +2105,6 @@ camel_folder_info_new
 camel_folder_info_free
 camel_folder_info_build
 camel_folder_info_clone
-camel_store_supports_subscriptions
-camel_store_folder_is_subscribed
 camel_store_can_refresh_folder
 CamelStoreLock
 camel_store_lock
@@ -2137,12 +2133,6 @@ camel_store_delete_folder_finish
 camel_store_rename_folder_sync
 camel_store_rename_folder
 camel_store_rename_folder_finish
-camel_store_subscribe_folder_sync
-camel_store_subscribe_folder
-camel_store_subscribe_folder_finish
-camel_store_unsubscribe_folder_sync
-camel_store_unsubscribe_folder
-camel_store_unsubscribe_folder_finish
 camel_store_synchronize_sync
 camel_store_synchronize
 camel_store_synchronize_finish
@@ -2399,6 +2389,31 @@ camel_stream_vfs_get_type
 </SECTION>
 
 <SECTION>
+<FILE>camel-subscribable</FILE>
+<TITLE>CamelSubscribable</TITLE>
+CamelSubscribable
+camel_subscribable_folder_is_subscribed
+camel_subscribable_subscribe_folder_sync
+camel_subscribable_subscribe_folder
+camel_subscribable_subscribe_folder_finish
+camel_subscribable_unsubscribe_folder_sync
+camel_subscribable_unsubscribe_folder
+camel_subscribable_unsubscribe_folder_finish
+camel_subscribable_folder_subscribed
+camel_subscribable_folder_unsubscribed
+<SUBSECTION Standard>
+CAMEL_SUBSCRIBABLE
+CAMEL_IS_SUBSCRIBABLE
+CAMEL_TYPE_SUBSCRIBABLE
+CAMEL_SUBSCRIBABLE_INTERFACE
+CAMEL_IS_SUBSCRIBABLE_INTERFACE
+CAMEL_SUBSCRIBABLE_GET_INTERFACE
+CamelSubscribableInterface
+<SUBSECTION Private>
+camel_subscribable_get_type
+</SECTION>
+
+<SECTION>
 <FILE>camel-tcp-stream-raw</FILE>
 <TITLE>CamelTcpStreamRaw</TITLE>
 CamelTcpStreamRaw
diff --git a/docs/reference/camel/camel.types b/docs/reference/camel/camel.types
index 35e8b5e..70e8aa3 100644
--- a/docs/reference/camel/camel.types
+++ b/docs/reference/camel/camel.types
@@ -81,6 +81,7 @@ camel_stream_mem_get_type
 camel_stream_null_get_type
 camel_stream_process_get_type
 camel_stream_vfs_get_type
+camel_subscribable_get_type
 camel_tcp_stream_get_type
 camel_tcp_stream_raw_get_type
 camel_text_index_cursor_get_type
diff --git a/docs/reference/camel/tmpl/camel-store.sgml b/docs/reference/camel/tmpl/camel-store.sgml
index 24bf067..2e5f7a7 100644
--- a/docs/reference/camel/tmpl/camel-store.sgml
+++ b/docs/reference/camel/tmpl/camel-store.sgml
@@ -59,22 +59,6 @@ CamelStore
 @arg1: 
 @arg2: 
 
-<!-- ##### SIGNAL CamelStore::folder-subscribed ##### -->
-<para>
-
-</para>
-
- camelstore: the object which received the signal.
- Param2: 
-
-<!-- ##### SIGNAL CamelStore::folder-unsubscribed ##### -->
-<para>
-
-</para>
-
- camelstore: the object which received the signal.
- Param2: 
-
 <!-- ##### MACRO CAMEL_STORE_ERROR ##### -->
 <para>
 
@@ -149,7 +133,6 @@ CamelStore
 
 </para>
 
- CAMEL_STORE_SUBSCRIPTIONS: 
 @CAMEL_STORE_VTRASH: 
 @CAMEL_STORE_VJUNK: 
 @CAMEL_STORE_PROXY: 
@@ -230,24 +213,6 @@ CamelStore
 @folder_info: 
 
 
-<!-- ##### FUNCTION camel_store_folder_subscribed ##### -->
-<para>
-
-</para>
-
- store: 
- folder_info: 
-
-
-<!-- ##### FUNCTION camel_store_folder_unsubscribed ##### -->
-<para>
-
-</para>
-
- store: 
- folder_info: 
-
-
 <!-- ##### FUNCTION camel_store_free_folder_info ##### -->
 <para>
 
@@ -313,25 +278,6 @@ CamelStore
 @Returns: 
 
 
-<!-- ##### FUNCTION camel_store_supports_subscriptions ##### -->
-<para>
-
-</para>
-
- store: 
- Returns: 
-
-
-<!-- ##### FUNCTION camel_store_folder_is_subscribed ##### -->
-<para>
-
-</para>
-
- store: 
- folder_name: 
- Returns: 
-
-
 <!-- ##### FUNCTION camel_store_can_refresh_folder ##### -->
 <para>
 
@@ -658,78 +604,6 @@ CamelStore
 @Returns: 
 
 
-<!-- ##### FUNCTION camel_store_subscribe_folder_sync ##### -->
-<para>
-
-</para>
-
- store: 
- folder_name: 
- cancellable: 
- error: 
- Returns: 
-
-
-<!-- ##### FUNCTION camel_store_subscribe_folder ##### -->
-<para>
-
-</para>
-
- store: 
- folder_name: 
- io_priority: 
- cancellable: 
- callback: 
- user_data: 
-
-
-<!-- ##### FUNCTION camel_store_subscribe_folder_finish ##### -->
-<para>
-
-</para>
-
- store: 
- result: 
- error: 
- Returns: 
-
-
-<!-- ##### FUNCTION camel_store_unsubscribe_folder_sync ##### -->
-<para>
-
-</para>
-
- store: 
- folder_name: 
- cancellable: 
- error: 
- Returns: 
-
-
-<!-- ##### FUNCTION camel_store_unsubscribe_folder ##### -->
-<para>
-
-</para>
-
- store: 
- folder_name: 
- io_priority: 
- cancellable: 
- callback: 
- user_data: 
-
-
-<!-- ##### FUNCTION camel_store_unsubscribe_folder_finish ##### -->
-<para>
-
-</para>
-
- store: 
- result: 
- error: 
- Returns: 
-
-
 <!-- ##### FUNCTION camel_store_synchronize_sync ##### -->
 <para>
 
diff --git a/docs/reference/camel/tmpl/camel-subscribable.sgml b/docs/reference/camel/tmpl/camel-subscribable.sgml
new file mode 100644
index 0000000..803d38b
--- /dev/null
+++ b/docs/reference/camel/tmpl/camel-subscribable.sgml
@@ -0,0 +1,144 @@
+<!-- ##### SECTION Title ##### -->
+CamelSubscribable
+
+<!-- ##### SECTION Short_Description ##### -->
+
+
+<!-- ##### SECTION Long_Description ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION See_Also ##### -->
+<para>
+
+</para>
+
+<!-- ##### SECTION Stability_Level ##### -->
+
+
+<!-- ##### SECTION Image ##### -->
+
+
+<!-- ##### STRUCT CamelSubscribable ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### SIGNAL CamelSubscribable::folder-subscribed ##### -->
+<para>
+
+</para>
+
+ camelsubscribable: the object which received the signal.
+ arg1: 
+
+<!-- ##### SIGNAL CamelSubscribable::folder-unsubscribed ##### -->
+<para>
+
+</para>
+
+ camelsubscribable: the object which received the signal.
+ arg1: 
+
+<!-- ##### FUNCTION camel_subscribable_folder_is_subscribed ##### -->
+<para>
+
+</para>
+
+ subscribable: 
+ folder_name: 
+ Returns: 
+
+
+<!-- ##### FUNCTION camel_subscribable_subscribe_folder_sync ##### -->
+<para>
+
+</para>
+
+ subscribable: 
+ folder_name: 
+ cancellable: 
+ error: 
+ Returns: 
+
+
+<!-- ##### FUNCTION camel_subscribable_subscribe_folder ##### -->
+<para>
+
+</para>
+
+ subscribable: 
+ folder_name: 
+ io_priority: 
+ cancellable: 
+ callback: 
+ user_data: 
+
+
+<!-- ##### FUNCTION camel_subscribable_subscribe_folder_finish ##### -->
+<para>
+
+</para>
+
+ subscribable: 
+ result: 
+ error: 
+ Returns: 
+
+
+<!-- ##### FUNCTION camel_subscribable_unsubscribe_folder_sync ##### -->
+<para>
+
+</para>
+
+ subscribable: 
+ folder_name: 
+ cancellable: 
+ error: 
+ Returns: 
+
+
+<!-- ##### FUNCTION camel_subscribable_unsubscribe_folder ##### -->
+<para>
+
+</para>
+
+ subscribable: 
+ folder_name: 
+ io_priority: 
+ cancellable: 
+ callback: 
+ user_data: 
+
+
+<!-- ##### FUNCTION camel_subscribable_unsubscribe_folder_finish ##### -->
+<para>
+
+</para>
+
+ subscribable: 
+ result: 
+ error: 
+ Returns: 
+
+
+<!-- ##### FUNCTION camel_subscribable_folder_subscribed ##### -->
+<para>
+
+</para>
+
+ subscribable: 
+ folder_info: 
+
+
+<!-- ##### FUNCTION camel_subscribable_folder_unsubscribed ##### -->
+<para>
+
+</para>
+
+ subscribable: 
+ folder_info: 
+
+
diff --git a/docs/reference/camel/tmpl/camel-unused.sgml b/docs/reference/camel/tmpl/camel-unused.sgml
index 32b42ea..2827799 100644
--- a/docs/reference/camel/tmpl/camel-unused.sgml
+++ b/docs/reference/camel/tmpl/camel-unused.sgml
@@ -4482,6 +4482,22 @@ streams
 
 @parent: 
 
+<!-- ##### SIGNAL CamelStore::folder-subscribed ##### -->
+<para>
+
+</para>
+
+ camelstore: the object which received the signal.
+ Param2: 
+
+<!-- ##### SIGNAL CamelStore::folder-unsubscribed ##### -->
+<para>
+
+</para>
+
+ camelstore: the object which received the signal.
+ Param2: 
+
 <!-- ##### ARG CamelStore:filter-inbox ##### -->
 <para>
 
@@ -8929,6 +8945,31 @@ streams
 @xevline: 
 @Returns: 
 
+<!-- ##### FUNCTION camel_store_folder_is_subscribed ##### -->
+<para>
+
+</para>
+
+ store: 
+ folder_name: 
+ Returns: 
+
+<!-- ##### FUNCTION camel_store_folder_subscribed ##### -->
+<para>
+
+</para>
+
+ store: 
+ folder_info: 
+
+<!-- ##### FUNCTION camel_store_folder_unsubscribed ##### -->
+<para>
+
+</para>
+
+ store: 
+ folder_info: 
+
 <!-- ##### FUNCTION camel_store_folder_uri_equal ##### -->
 <para>
 
@@ -8985,6 +9026,47 @@ streams
 @store: 
 @filter_inbox: 
 
+<!-- ##### FUNCTION camel_store_subscribe_folder ##### -->
+<para>
+
+</para>
+
+ store: 
+ folder_name: 
+ io_priority: 
+ cancellable: 
+ callback: 
+ user_data: 
+
+<!-- ##### FUNCTION camel_store_subscribe_folder_finish ##### -->
+<para>
+
+</para>
+
+ store: 
+ result: 
+ error: 
+ Returns: 
+
+<!-- ##### FUNCTION camel_store_subscribe_folder_sync ##### -->
+<para>
+
+</para>
+
+ store: 
+ folder_name: 
+ cancellable: 
+ error: 
+ Returns: 
+
+<!-- ##### FUNCTION camel_store_supports_subscriptions ##### -->
+<para>
+
+</para>
+
+ store: 
+ Returns: 
+
 <!-- ##### FUNCTION camel_store_sync ##### -->
 <para>
 
@@ -8996,6 +9078,39 @@ streams
 @error: 
 @Returns: 
 
+<!-- ##### FUNCTION camel_store_unsubscribe_folder ##### -->
+<para>
+
+</para>
+
+ store: 
+ folder_name: 
+ io_priority: 
+ cancellable: 
+ callback: 
+ user_data: 
+
+<!-- ##### FUNCTION camel_store_unsubscribe_folder_finish ##### -->
+<para>
+
+</para>
+
+ store: 
+ result: 
+ error: 
+ Returns: 
+
+<!-- ##### FUNCTION camel_store_unsubscribe_folder_sync ##### -->
+<para>
+
+</para>
+
+ store: 
+ folder_name: 
+ cancellable: 
+ error: 
+ Returns: 
+
 <!-- ##### FUNCTION camel_stream_filter_new_with_stream ##### -->
 <para>
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]