[evolution-data-server] IMAPX: Support the QUOTA extension (RFC 2087).
- From: Matthew Barnes <mbarnes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] IMAPX: Support the QUOTA extension (RFC 2087).
- Date: Sat, 8 Dec 2012 19:29:27 +0000 (UTC)
commit 95d730c78c4748469c50163c2d59e65fb0e1227a
Author: Matthew Barnes <mbarnes redhat com>
Date: Sat Dec 8 14:22:41 2012 -0500
IMAPX: Support the QUOTA extension (RFC 2087).
camel/camel-imapx-folder.c | 325 ++++++++++++++++++++++++++++++--------------
camel/camel-imapx-folder.h | 7 +
camel/camel-imapx-server.c | 225 ++++++++++++++++++++++++++++++-
camel/camel-imapx-server.h | 5 +
camel/camel-imapx-store.c | 74 ++++++++++-
camel/camel-imapx-store.h | 18 ++-
camel/camel-imapx-utils.c | 182 +++++++++++++++++++++++++
camel/camel-imapx-utils.h | 16 ++
8 files changed, 741 insertions(+), 111 deletions(-)
---
diff --git a/camel/camel-imapx-folder.c b/camel/camel-imapx-folder.c
index b9bc966..6a9522f 100644
--- a/camel/camel-imapx-folder.c
+++ b/camel/camel-imapx-folder.c
@@ -38,10 +38,20 @@
#define d(...) camel_imapx_debug(debug, '?', __VA_ARGS__)
+#define CAMEL_IMAPX_FOLDER_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), CAMEL_TYPE_IMAPX_FOLDER, CamelIMAPXFolderPrivate))
+
+struct _CamelIMAPXFolderPrivate {
+ GMutex property_lock;
+ gchar **quota_root_names;
+};
+
/* The custom property ID is a CamelArg artifact.
* It still identifies the property in state files. */
enum {
PROP_0,
+ PROP_QUOTA_ROOT_NAMES,
PROP_APPLY_FILTERS = 0x2501
};
@@ -49,105 +59,6 @@ G_DEFINE_TYPE (CamelIMAPXFolder, camel_imapx_folder, CAMEL_TYPE_OFFLINE_FOLDER)
static gboolean imapx_folder_get_apply_filters (CamelIMAPXFolder *folder);
-CamelFolder *
-camel_imapx_folder_new (CamelStore *store,
- const gchar *folder_dir,
- const gchar *folder_name,
- GError **error)
-{
- CamelFolder *folder;
- CamelService *service;
- CamelSettings *settings;
- CamelIMAPXFolder *ifolder;
- const gchar *short_name;
- gchar *state_file;
- gboolean filter_all;
- gboolean filter_inbox;
- gboolean filter_junk;
- gboolean filter_junk_inbox;
-
- d ("opening imap folder '%s'\n", folder_dir);
-
- service = CAMEL_SERVICE (store);
-
- settings = camel_service_ref_settings (service);
-
- g_object_get (
- settings,
- "filter-all", &filter_all,
- "filter-inbox", &filter_inbox,
- "filter-junk", &filter_junk,
- "filter-junk-inbox", &filter_junk_inbox,
- NULL);
-
- g_object_unref (settings);
-
- short_name = strrchr (folder_name, '/');
- if (short_name)
- short_name++;
- else
- short_name = folder_name;
-
- folder = g_object_new (
- CAMEL_TYPE_IMAPX_FOLDER,
- "display-name", short_name,
- "full_name", folder_name,
- "parent-store", store, NULL);
- ifolder = (CamelIMAPXFolder *) folder;
-
- ((CamelIMAPXFolder *) folder)->raw_name = g_strdup (folder_name);
-
- folder->summary = camel_imapx_summary_new (folder);
- if (!folder->summary) {
- g_set_error (
- error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
- _("Could not create folder summary for %s"),
- short_name);
- return NULL;
- }
-
- ifolder->cache = camel_data_cache_new (folder_dir, error);
- if (!ifolder->cache) {
- g_prefix_error (
- error, _("Could not create cache for %s: "),
- short_name);
- return NULL;
- }
-
- state_file = g_build_filename (folder_dir, "cmeta", NULL);
- camel_object_set_state_filename (CAMEL_OBJECT (folder), state_file);
- g_free (state_file);
- camel_object_state_read (CAMEL_OBJECT (folder));
-
- ifolder->search = camel_folder_search_new ();
- g_mutex_init (&ifolder->search_lock);
- g_mutex_init (&ifolder->stream_lock);
- ifolder->ignore_recent = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
- ifolder->exists_on_server = 0;
- ifolder->unread_on_server = 0;
- ifolder->modseq_on_server = 0;
- ifolder->uidnext_on_server = 0;
-
- if (!g_ascii_strcasecmp (folder_name, "INBOX")) {
- if (filter_inbox || filter_all)
- folder->folder_flags |= CAMEL_FOLDER_FILTER_RECENT;
- if (filter_junk)
- folder->folder_flags |= CAMEL_FOLDER_FILTER_JUNK;
- } else {
- if (filter_junk && !filter_junk_inbox)
- folder->folder_flags |= CAMEL_FOLDER_FILTER_JUNK;
-
- if (filter_all || imapx_folder_get_apply_filters (ifolder))
- folder->folder_flags |= CAMEL_FOLDER_FILTER_RECENT;
- }
-
- camel_store_summary_connect_folder_summary (
- (CamelStoreSummary *) ((CamelIMAPXStore *) store)->summary,
- folder_name, folder->summary);
-
- return folder;
-}
-
static gboolean
imapx_folder_get_apply_filters (CamelIMAPXFolder *folder)
{
@@ -184,6 +95,12 @@ imapx_folder_set_property (GObject *object,
CAMEL_IMAPX_FOLDER (object),
g_value_get_boolean (value));
return;
+
+ case PROP_QUOTA_ROOT_NAMES:
+ camel_imapx_folder_set_quota_root_names (
+ CAMEL_IMAPX_FOLDER (object),
+ g_value_get_boxed (value));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -198,7 +115,15 @@ imapx_folder_get_property (GObject *object,
switch (property_id) {
case PROP_APPLY_FILTERS:
g_value_set_boolean (
- value, imapx_folder_get_apply_filters (
+ value,
+ imapx_folder_get_apply_filters (
+ CAMEL_IMAPX_FOLDER (object)));
+ return;
+
+ case PROP_QUOTA_ROOT_NAMES:
+ g_value_take_boxed (
+ value,
+ camel_imapx_folder_dup_quota_root_names (
CAMEL_IMAPX_FOLDER (object)));
return;
}
@@ -244,6 +169,8 @@ imapx_folder_finalize (GObject *object)
g_mutex_clear (&folder->search_lock);
g_mutex_clear (&folder->stream_lock);
+ g_mutex_clear (&folder->priv->property_lock);
+
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (camel_imapx_folder_parent_class)->finalize (object);
}
@@ -582,6 +509,54 @@ imapx_get_message_sync (CamelFolder *folder,
return msg;
}
+static CamelFolderQuotaInfo *
+imapx_get_quota_info_sync (CamelFolder *folder,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelStore *parent_store;
+ CamelIMAPXServer *server;
+ CamelFolderQuotaInfo *quota_info = NULL;
+ const gchar *folder_name;
+ gchar **quota_root_names;
+ gboolean success = FALSE;
+
+ folder_name = camel_folder_get_full_name (folder);
+ parent_store = camel_folder_get_parent_store (folder);
+
+ server = camel_imapx_store_get_server (
+ CAMEL_IMAPX_STORE (parent_store),
+ folder_name, cancellable, error);
+
+ if (server != NULL) {
+ success = camel_imapx_server_update_quota_info (
+ server, folder_name, cancellable, error);
+ g_object_unref (server);
+ }
+
+ if (!success)
+ return NULL;
+
+ quota_root_names = camel_imapx_folder_dup_quota_root_names (
+ CAMEL_IMAPX_FOLDER (folder));
+
+ /* XXX Just return info for the first quota root name, I guess. */
+ if (quota_root_names != NULL && quota_root_names[0] != NULL)
+ quota_info = camel_imapx_store_dup_quota_info (
+ CAMEL_IMAPX_STORE (parent_store),
+ quota_root_names[0]);
+
+ g_strfreev (quota_root_names);
+
+ if (quota_info == NULL)
+ g_set_error (
+ error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ _("No quota information available for folder '%s'"),
+ folder_name);
+
+ return quota_info;
+}
+
static gboolean
imapx_purge_message_cache_sync (CamelFolder *folder,
gchar *start_uid,
@@ -763,6 +738,8 @@ camel_imapx_folder_class_init (CamelIMAPXFolderClass *class)
GObjectClass *object_class;
CamelFolderClass *folder_class;
+ g_type_class_add_private (class, sizeof (CamelIMAPXFolderPrivate));
+
object_class = G_OBJECT_CLASS (class);
object_class->set_property = imapx_folder_set_property;
object_class->get_property = imapx_folder_get_property;
@@ -780,6 +757,7 @@ camel_imapx_folder_class_init (CamelIMAPXFolderClass *class)
folder_class->expunge_sync = imapx_expunge_sync;
folder_class->fetch_messages_sync = imapx_fetch_messages_sync;
folder_class->get_message_sync = imapx_get_message_sync;
+ folder_class->get_quota_info_sync = imapx_get_quota_info_sync;
folder_class->purge_message_cache_sync = imapx_purge_message_cache_sync;
folder_class->refresh_info_sync = imapx_refresh_info_sync;
folder_class->synchronize_sync = imapx_synchronize_sync;
@@ -796,6 +774,17 @@ camel_imapx_folder_class_init (CamelIMAPXFolderClass *class)
FALSE,
G_PARAM_READWRITE |
CAMEL_PARAM_PERSISTENT));
+
+ g_object_class_install_property (
+ object_class,
+ PROP_QUOTA_ROOT_NAMES,
+ g_param_spec_boxed (
+ "quota-root-names",
+ "Quota Root Names",
+ "Quota root names for this folder",
+ G_TYPE_STRV,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
}
static void
@@ -803,6 +792,8 @@ camel_imapx_folder_init (CamelIMAPXFolder *imapx_folder)
{
CamelFolder *folder = CAMEL_FOLDER (imapx_folder);
+ imapx_folder->priv = CAMEL_IMAPX_FOLDER_GET_PRIVATE (imapx_folder);
+
folder->folder_flags |= CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY;
folder->permanent_flags = CAMEL_MESSAGE_ANSWERED |
@@ -810,5 +801,139 @@ camel_imapx_folder_init (CamelIMAPXFolder *imapx_folder)
CAMEL_MESSAGE_FLAGGED | CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_USER;
camel_folder_set_lock_async (folder, TRUE);
+
+ g_mutex_init (&imapx_folder->priv->property_lock);
+}
+
+CamelFolder *
+camel_imapx_folder_new (CamelStore *store,
+ const gchar *folder_dir,
+ const gchar *folder_name,
+ GError **error)
+{
+ CamelFolder *folder;
+ CamelService *service;
+ CamelSettings *settings;
+ CamelIMAPXFolder *ifolder;
+ const gchar *short_name;
+ gchar *state_file;
+ gboolean filter_all;
+ gboolean filter_inbox;
+ gboolean filter_junk;
+ gboolean filter_junk_inbox;
+
+ d ("opening imap folder '%s'\n", folder_dir);
+
+ service = CAMEL_SERVICE (store);
+
+ settings = camel_service_ref_settings (service);
+
+ g_object_get (
+ settings,
+ "filter-all", &filter_all,
+ "filter-inbox", &filter_inbox,
+ "filter-junk", &filter_junk,
+ "filter-junk-inbox", &filter_junk_inbox,
+ NULL);
+
+ g_object_unref (settings);
+
+ short_name = strrchr (folder_name, '/');
+ if (short_name)
+ short_name++;
+ else
+ short_name = folder_name;
+
+ folder = g_object_new (
+ CAMEL_TYPE_IMAPX_FOLDER,
+ "display-name", short_name,
+ "full_name", folder_name,
+ "parent-store", store, NULL);
+ ifolder = (CamelIMAPXFolder *) folder;
+
+ ((CamelIMAPXFolder *) folder)->raw_name = g_strdup (folder_name);
+
+ folder->summary = camel_imapx_summary_new (folder);
+ if (!folder->summary) {
+ g_set_error (
+ error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
+ _("Could not create folder summary for %s"),
+ short_name);
+ return NULL;
+ }
+
+ ifolder->cache = camel_data_cache_new (folder_dir, error);
+ if (!ifolder->cache) {
+ g_prefix_error (
+ error, _("Could not create cache for %s: "),
+ short_name);
+ return NULL;
+ }
+
+ state_file = g_build_filename (folder_dir, "cmeta", NULL);
+ camel_object_set_state_filename (CAMEL_OBJECT (folder), state_file);
+ g_free (state_file);
+ camel_object_state_read (CAMEL_OBJECT (folder));
+
+ ifolder->search = camel_folder_search_new ();
+ g_mutex_init (&ifolder->search_lock);
+ g_mutex_init (&ifolder->stream_lock);
+ ifolder->ignore_recent = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
+ ifolder->exists_on_server = 0;
+ ifolder->unread_on_server = 0;
+ ifolder->modseq_on_server = 0;
+ ifolder->uidnext_on_server = 0;
+
+ if (!g_ascii_strcasecmp (folder_name, "INBOX")) {
+ if (filter_inbox || filter_all)
+ folder->folder_flags |= CAMEL_FOLDER_FILTER_RECENT;
+ if (filter_junk)
+ folder->folder_flags |= CAMEL_FOLDER_FILTER_JUNK;
+ } else {
+ if (filter_junk && !filter_junk_inbox)
+ folder->folder_flags |= CAMEL_FOLDER_FILTER_JUNK;
+
+ if (filter_all || imapx_folder_get_apply_filters (ifolder))
+ folder->folder_flags |= CAMEL_FOLDER_FILTER_RECENT;
+ }
+
+ camel_store_summary_connect_folder_summary (
+ (CamelStoreSummary *) ((CamelIMAPXStore *) store)->summary,
+ folder_name, folder->summary);
+
+ return folder;
+}
+
+gchar **
+camel_imapx_folder_dup_quota_root_names (CamelIMAPXFolder *folder)
+{
+ gchar **duplicate;
+
+ g_return_val_if_fail (CAMEL_IS_IMAPX_FOLDER (folder), NULL);
+
+ g_mutex_lock (&folder->priv->property_lock);
+
+ duplicate = g_strdupv (folder->priv->quota_root_names);
+
+ g_mutex_unlock (&folder->priv->property_lock);
+
+ return duplicate;
+}
+
+void
+camel_imapx_folder_set_quota_root_names (CamelIMAPXFolder *folder,
+ const gchar **quota_root_names)
+{
+ g_return_if_fail (CAMEL_IS_IMAPX_FOLDER (folder));
+
+ g_mutex_lock (&folder->priv->property_lock);
+
+ g_strfreev (folder->priv->quota_root_names);
+ folder->priv->quota_root_names =
+ g_strdupv ((gchar **) quota_root_names);
+
+ g_mutex_unlock (&folder->priv->property_lock);
+
+ g_object_notify (G_OBJECT (folder), "quota-root-names");
}
diff --git a/camel/camel-imapx-folder.h b/camel/camel-imapx-folder.h
index 0b9c0d2..3ce36e7 100644
--- a/camel/camel-imapx-folder.h
+++ b/camel/camel-imapx-folder.h
@@ -56,9 +56,11 @@ G_BEGIN_DECLS
typedef struct _CamelIMAPXFolder CamelIMAPXFolder;
typedef struct _CamelIMAPXFolderClass CamelIMAPXFolderClass;
+typedef struct _CamelIMAPXFolderPrivate CamelIMAPXFolderPrivate;
struct _CamelIMAPXFolder {
CamelOfflineFolder parent;
+ CamelIMAPXFolderPrivate *priv;
gchar *raw_name;
CamelDataCache *cache;
@@ -91,6 +93,11 @@ CamelFolder * camel_imapx_folder_new (CamelStore *parent,
gchar * imapx_get_filename (CamelFolder *folder,
const gchar *uid,
GError **error);
+gchar ** camel_imapx_folder_dup_quota_root_names
+ (CamelIMAPXFolder *folder);
+void camel_imapx_folder_set_quota_root_names
+ (CamelIMAPXFolder *folder,
+ const gchar **quota_root_names);
G_END_DECLS
diff --git a/camel/camel-imapx-server.c b/camel/camel-imapx-server.c
index b000211..5d641a1 100644
--- a/camel/camel-imapx-server.c
+++ b/camel/camel-imapx-server.c
@@ -86,6 +86,7 @@ typedef struct _ManageSubscriptionsData ManageSubscriptionsData;
typedef struct _RenameFolderData RenameFolderData;
typedef struct _CreateFolderData CreateFolderData;
typedef struct _DeleteFolderData DeleteFolderData;
+typedef struct _QuotaData QuotaData;
struct _GetMessageData {
/* in: uid requested */
@@ -165,6 +166,10 @@ struct _DeleteFolderData {
gchar *folder_name;
};
+struct _QuotaData {
+ gchar *folder_name;
+};
+
/* untagged response handling */
/* May need to turn this into separate,
@@ -239,6 +244,14 @@ static gboolean imapx_untagged_preauth (CamelIMAPXServer *is,
CamelIMAPXStream *stream,
GCancellable *cancellable,
GError **error);
+static gboolean imapx_untagged_quota (CamelIMAPXServer *is,
+ CamelIMAPXStream *stream,
+ GCancellable *cancellable,
+ GError **error);
+static gboolean imapx_untagged_quotaroot (CamelIMAPXServer *is,
+ CamelIMAPXStream *stream,
+ GCancellable *cancellable,
+ GError **error);
static gboolean imapx_untagged_recent (CamelIMAPXServer *is,
CamelIMAPXStream *stream,
GCancellable *cancellable,
@@ -266,6 +279,8 @@ enum {
IMAPX_UNTAGGED_ID_NO,
IMAPX_UNTAGGED_ID_OK,
IMAPX_UNTAGGED_ID_PREAUTH,
+ IMAPX_UNTAGGED_ID_QUOTA,
+ IMAPX_UNTAGGED_ID_QUOTAROOT,
IMAPX_UNTAGGED_ID_RECENT,
IMAPX_UNTAGGED_ID_STATUS,
IMAPX_UNTAGGED_ID_VANISHED,
@@ -286,6 +301,8 @@ static const CamelIMAPXUntaggedRespHandlerDesc _untagged_descr[] = {
{CAMEL_IMAPX_UNTAGGED_NO, imapx_untagged_ok_no_bad, NULL, FALSE},
{CAMEL_IMAPX_UNTAGGED_OK, imapx_untagged_ok_no_bad, NULL, FALSE},
{CAMEL_IMAPX_UNTAGGED_PREAUTH, imapx_untagged_preauth, CAMEL_IMAPX_UNTAGGED_OK, TRUE /*overridden */ },
+ {CAMEL_IMAPX_UNTAGGED_QUOTA, imapx_untagged_quota, NULL, FALSE},
+ {CAMEL_IMAPX_UNTAGGED_QUOTAROOT, imapx_untagged_quotaroot, NULL, FALSE},
{CAMEL_IMAPX_UNTAGGED_RECENT, imapx_untagged_recent, NULL, TRUE},
{CAMEL_IMAPX_UNTAGGED_STATUS, imapx_untagged_status, NULL, TRUE},
{CAMEL_IMAPX_UNTAGGED_VANISHED, imapx_untagged_vanished, NULL, TRUE},
@@ -372,6 +389,7 @@ enum {
IMAPX_JOB_DELETE_FOLDER = 1 << 12,
IMAPX_JOB_RENAME_FOLDER = 1 << 13,
IMAPX_JOB_FETCH_MESSAGES = 1 << 14,
+ IMAPX_JOB_UPDATE_QUOTA_INFO = 1 << 15
};
/* Operations on the store (folder_tree) will have highest priority as we know for sure they are sync
@@ -391,7 +409,8 @@ enum {
IMAPX_PRIIORITY_COPY_MESSAGE = -60,
IMAPX_PRIORITY_LIST = -80,
IMAPX_PRIORITY_IDLE = -100,
- IMAPX_PRIORITY_SYNC_MESSAGE = -120
+ IMAPX_PRIORITY_SYNC_MESSAGE = -120,
+ IMAPX_PRIORITY_UPDATE_QUOTA_INFO = -80
};
struct _imapx_flag_change {
@@ -670,6 +689,14 @@ delete_folder_data_free (DeleteFolderData *data)
g_slice_free (DeleteFolderData, data);
}
+static void
+quota_data_free (QuotaData *data)
+{
+ g_free (data->folder_name);
+
+ g_slice_free (QuotaData, data);
+}
+
/*
this creates a uid (or sequence number) set directly into a command,
if total is set, then we break it up into total uids. (i.e. command time)
@@ -1915,6 +1942,101 @@ imapx_untagged_list (CamelIMAPXServer *is,
}
static gboolean
+imapx_untagged_quota (CamelIMAPXServer *is,
+ CamelIMAPXStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gchar *quota_root_name = NULL;
+ CamelFolderQuotaInfo *quota_info = NULL;
+ gboolean success;
+
+ success = camel_imapx_parse_quota (
+ stream, cancellable, "a_root_name, "a_info, error);
+
+ /* Sanity check */
+ g_return_val_if_fail (
+ (success && (quota_root_name != NULL)) ||
+ (!success && (quota_root_name == NULL)), FALSE);
+
+ if (success) {
+ CamelIMAPXStore *store;
+
+ store = camel_imapx_server_ref_store (is);
+ camel_imapx_store_set_quota_info (
+ store, quota_root_name, quota_info);
+ g_object_unref (store);
+
+ g_free (quota_root_name);
+ camel_folder_quota_info_free (quota_info);
+ }
+
+ return success;
+}
+
+static gboolean
+imapx_untagged_quotaroot (CamelIMAPXServer *is,
+ CamelIMAPXStream *stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelIMAPXStore *store;
+ CamelIMAPXStoreNamespace *ns;
+ CamelFolder *folder = NULL;
+ gchar *mailbox_name = NULL;
+ gchar **quota_root_names = NULL;
+ gboolean success;
+ GError *local_error = NULL;
+
+ success = camel_imapx_parse_quotaroot (
+ stream, cancellable, &mailbox_name, "a_root_names, error);
+
+ /* Sanity check */
+ g_return_val_if_fail (
+ (success && (mailbox_name != NULL)) ||
+ (!success && (mailbox_name == NULL)), FALSE);
+
+ if (!success)
+ return FALSE;
+
+ store = camel_imapx_server_ref_store (is);
+
+ ns = camel_imapx_store_summary_namespace_find_full (
+ store->summary, mailbox_name);
+ if (ns != NULL) {
+ gchar *folder_path;
+
+ folder_path = camel_imapx_store_summary_full_to_path (
+ store->summary, mailbox_name, ns->sep);
+ if (folder_path != NULL) {
+ folder = camel_store_get_folder_sync (
+ CAMEL_STORE (store), folder_path, 0,
+ cancellable, &local_error);
+ g_free (folder_path);
+ }
+ }
+
+ if (folder != NULL) {
+ camel_imapx_folder_set_quota_root_names (
+ CAMEL_IMAPX_FOLDER (folder),
+ (const gchar **) quota_root_names);
+ g_object_unref (folder);
+ }
+
+ if (local_error != NULL) {
+ g_warning (
+ "%s: Failed to get folder '%s': %s",
+ G_STRFUNC, mailbox_name, local_error->message);
+ g_error_free (local_error);
+ }
+
+ g_free (mailbox_name);
+ g_strfreev (quota_root_names);
+
+ return TRUE;
+}
+
+static gboolean
imapx_untagged_recent (CamelIMAPXServer *is,
CamelIMAPXStream *stream,
GCancellable *cancellable,
@@ -5813,6 +5935,69 @@ imapx_job_rename_folder_start (CamelIMAPXJob *job,
/* ********************************************************************** */
static gboolean
+imapx_command_update_quota_info_done (CamelIMAPXServer *is,
+ CamelIMAPXCommand *ic,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelIMAPXJob *job;
+ gboolean success = TRUE;
+
+ job = camel_imapx_command_get_job (ic);
+ g_return_val_if_fail (CAMEL_IS_IMAPX_JOB (job), FALSE);
+
+ if (camel_imapx_command_set_error_if_failed (ic, error)) {
+ g_prefix_error (
+ error, "%s: ",
+ _("Error retrieving quota information"));
+ success = FALSE;
+ }
+
+ imapx_unregister_job (is, job);
+ camel_imapx_command_unref (ic);
+
+ return success;
+}
+
+static gboolean
+imapx_job_update_quota_info_start (CamelIMAPXJob *job,
+ CamelIMAPXServer *is,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelIMAPXCommand *ic;
+ CamelIMAPXStore *store;
+ QuotaData *data;
+ gchar *encoded_folder_name;
+ gboolean success;
+
+ data = camel_imapx_job_get_data (job);
+ g_return_val_if_fail (data != NULL, FALSE);
+
+ store = camel_imapx_server_ref_store (is);
+
+ encoded_folder_name =
+ imapx_encode_folder_name (store, data->folder_name);
+
+ ic = camel_imapx_command_new (
+ is, "GETQUOTAROOT", NULL,
+ "GETQUOTAROOT %s", encoded_folder_name);
+ ic->pri = job->pri;
+ camel_imapx_command_set_job (ic, job);
+ ic->complete = imapx_command_update_quota_info_done;
+
+ success = imapx_command_queue (is, ic, cancellable, error);
+
+ g_free (encoded_folder_name);
+
+ g_object_unref (store);
+
+ return success;
+}
+
+/* ********************************************************************** */
+
+static gboolean
imapx_command_noop_done (CamelIMAPXServer *is,
CamelIMAPXCommand *ic,
GCancellable *cancellable,
@@ -7552,6 +7737,44 @@ camel_imapx_server_rename_folder (CamelIMAPXServer *is,
return success;
}
+gboolean
+camel_imapx_server_update_quota_info (CamelIMAPXServer *is,
+ const gchar *folder_name,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelIMAPXJob *job;
+ QuotaData *data;
+ gboolean success;
+
+ g_return_val_if_fail (CAMEL_IS_IMAPX_SERVER (is), FALSE);
+ g_return_val_if_fail (folder_name != NULL, FALSE);
+
+ if (is->cinfo && (is->cinfo->capa & IMAPX_CAPABILITY_QUOTA) == 0) {
+ g_set_error_literal (
+ error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("IMAP server does not support quotas"));
+ return FALSE;
+ }
+
+ data = g_slice_new0 (QuotaData);
+ data->folder_name = g_strdup (folder_name);
+
+ job = camel_imapx_job_new (cancellable);
+ job->type = IMAPX_JOB_UPDATE_QUOTA_INFO;
+ job->start = imapx_job_update_quota_info_start;
+ job->pri = IMAPX_PRIORITY_UPDATE_QUOTA_INFO;
+
+ camel_imapx_job_set_data (
+ job, data, (GDestroyNotify) quota_data_free);
+
+ success = imapx_submit_job (is, job, error);
+
+ camel_imapx_job_unref (job);
+
+ return success;
+}
+
IMAPXJobQueueInfo *
camel_imapx_server_get_job_queue_info (CamelIMAPXServer *is)
{
diff --git a/camel/camel-imapx-server.h b/camel/camel-imapx-server.h
index 02cc44a..15cdfce 100644
--- a/camel/camel-imapx-server.h
+++ b/camel/camel-imapx-server.h
@@ -272,6 +272,11 @@ gboolean camel_imapx_server_rename_folder
const gchar *new_name,
GCancellable *cancellable,
GError **error);
+gboolean camel_imapx_server_update_quota_info
+ (CamelIMAPXServer *is,
+ const gchar *folder_name,
+ GCancellable *cancellable,
+ GError **error);
struct _IMAPXJobQueueInfo *
camel_imapx_server_get_job_queue_info
(CamelIMAPXServer *is);
diff --git a/camel/camel-imapx-store.c b/camel/camel-imapx-store.c
index 792e61c..bfa95c3 100644
--- a/camel/camel-imapx-store.c
+++ b/camel/camel-imapx-store.c
@@ -52,6 +52,15 @@
#define FINFO_REFRESH_INTERVAL 60
+#define CAMEL_IMAPX_STORE_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), CAMEL_TYPE_IMAPX_STORE, CamelIMAPXStorePrivate))
+
+struct _CamelIMAPXStorePrivate {
+ GHashTable *quota_info;
+ GMutex quota_info_lock;
+};
+
static GInitableIface *parent_initable_interface;
/* Forward Declarations */
@@ -130,6 +139,9 @@ imapx_store_finalize (GObject *object)
g_mutex_clear (&imapx_store->get_finfo_lock);
+ g_hash_table_destroy (imapx_store->priv->quota_info);
+ g_mutex_clear (&imapx_store->priv->quota_info_lock);
+
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (camel_imapx_store_parent_class)->finalize (object);
}
@@ -1770,6 +1782,8 @@ camel_imapx_store_class_init (CamelIMAPXStoreClass *class)
CamelServiceClass *service_class;
CamelStoreClass *store_class;
+ g_type_class_add_private (class, sizeof (CamelIMAPXStorePrivate));
+
object_class = G_OBJECT_CLASS (class);
object_class->dispose = imapx_store_dispose;
object_class->finalize = imapx_store_finalize;
@@ -1821,13 +1835,63 @@ camel_subscribable_init (CamelSubscribableInterface *interface)
}
static void
-camel_imapx_store_init (CamelIMAPXStore *istore)
+camel_imapx_store_init (CamelIMAPXStore *store)
{
- g_mutex_init (&istore->get_finfo_lock);
- istore->last_refresh_time = time (NULL) - (FINFO_REFRESH_INTERVAL + 10);
- istore->dir_sep = '/';
- istore->con_man = camel_imapx_conn_manager_new (CAMEL_STORE (istore));
+ store->priv = CAMEL_IMAPX_STORE_GET_PRIVATE (store);
+
+ g_mutex_init (&store->get_finfo_lock);
+ store->last_refresh_time = time (NULL) - (FINFO_REFRESH_INTERVAL + 10);
+ store->dir_sep = '/';
+ store->con_man = camel_imapx_conn_manager_new (CAMEL_STORE (store));
+
+ store->priv->quota_info = g_hash_table_new_full (
+ (GHashFunc) g_str_hash,
+ (GEqualFunc) g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) camel_folder_quota_info_free);
+ g_mutex_init (&store->priv->quota_info_lock);
imapx_utils_init ();
}
+CamelFolderQuotaInfo *
+camel_imapx_store_dup_quota_info (CamelIMAPXStore *store,
+ const gchar *quota_root_name)
+{
+ CamelFolderQuotaInfo *info;
+
+ g_return_val_if_fail (CAMEL_IS_IMAPX_STORE (store), NULL);
+ g_return_val_if_fail (quota_root_name != NULL, NULL);
+
+ g_mutex_lock (&store->priv->quota_info_lock);
+
+ info = g_hash_table_lookup (
+ store->priv->quota_info, quota_root_name);
+
+ /* camel_folder_quota_info_clone() handles NULL gracefully. */
+ info = camel_folder_quota_info_clone (info);
+
+ g_mutex_unlock (&store->priv->quota_info_lock);
+
+ return info;
+}
+
+void
+camel_imapx_store_set_quota_info (CamelIMAPXStore *store,
+ const gchar *quota_root_name,
+ const CamelFolderQuotaInfo *info)
+{
+ g_return_if_fail (CAMEL_IS_IMAPX_STORE (store));
+ g_return_if_fail (quota_root_name != NULL);
+
+ g_mutex_lock (&store->priv->quota_info_lock);
+
+ /* camel_folder_quota_info_clone() handles NULL gracefully. */
+ g_hash_table_insert (
+ store->priv->quota_info,
+ g_strdup (quota_root_name),
+ camel_folder_quota_info_clone (info));
+
+ g_mutex_unlock (&store->priv->quota_info_lock);
+}
+
diff --git a/camel/camel-imapx-store.h b/camel/camel-imapx-store.h
index dd7fc90..3eac2e0 100644
--- a/camel/camel-imapx-store.h
+++ b/camel/camel-imapx-store.h
@@ -92,12 +92,20 @@ struct _CamelIMAPXStoreClass {
GType camel_imapx_store_get_type (void);
CamelIMAPXServer *
camel_imapx_store_get_server (CamelIMAPXStore *store,
- const gchar *folder_name,
- GCancellable *cancellable,
- GError **error);
+ const gchar *folder_name,
+ GCancellable *cancellable,
+ GError **error);
void camel_imapx_store_op_done (CamelIMAPXStore *istore,
- CamelIMAPXServer *server,
- const gchar *folder_name);
+ CamelIMAPXServer *server,
+ const gchar *folder_name);
+CamelFolderQuotaInfo *
+ camel_imapx_store_dup_quota_info
+ (CamelIMAPXStore *store,
+ const gchar *quota_root_name);
+void camel_imapx_store_set_quota_info
+ (CamelIMAPXStore *store,
+ const gchar *quota_root_name,
+ const CamelFolderQuotaInfo *info);
G_END_DECLS
diff --git a/camel/camel-imapx-utils.c b/camel/camel-imapx-utils.c
index d653679..1d72d3f 100644
--- a/camel/camel-imapx-utils.c
+++ b/camel/camel-imapx-utils.c
@@ -371,6 +371,7 @@ struct {
{ "QRESYNC", IMAPX_CAPABILITY_QRESYNC },
{ "LIST-EXTENDED", IMAPX_CAPABILITY_LIST_EXTENDED },
{ "LIST-STATUS", IMAPX_CAPABILITY_LIST_STATUS },
+ { "QUOTA", IMAPX_CAPABILITY_QUOTA }
};
static GMutex capa_htable_lock; /* capabilities lookup table lock */
@@ -1991,6 +1992,187 @@ imapx_free_list (struct _list_info *linfo)
}
}
+gboolean
+camel_imapx_parse_quota (CamelIMAPXStream *is,
+ GCancellable *cancellable,
+ gchar **out_quota_root_name,
+ CamelFolderQuotaInfo **out_quota_info,
+ GError **error)
+{
+ GQueue queue = G_QUEUE_INIT;
+ CamelFolderQuotaInfo *info;
+ CamelFolderQuotaInfo *next;
+ gint tok;
+ guint len;
+ guchar *token;
+ gchar *quota_root_name = NULL;
+ gchar *resource_name = NULL;
+ guint64 resource_usage;
+ guint64 resource_limit;
+ GError *local_error = NULL;
+
+ g_return_val_if_fail (CAMEL_IS_IMAPX_STREAM (is), FALSE);
+ g_return_val_if_fail (out_quota_root_name != NULL, FALSE);
+ g_return_val_if_fail (out_quota_info != NULL, FALSE);
+
+ /* quota_response ::= "QUOTA" SP astring SP quota_list
+ * quota_list ::= "(" #quota_resource ")"
+ * quota_resource ::= atom SP number SP number */
+
+ tok = camel_imapx_stream_astring (is, &token, cancellable, error);
+ if (tok != 0)
+ goto fail;
+ quota_root_name = g_strdup ((gchar *) token);
+
+ tok = camel_imapx_stream_token (is, &token, &len, cancellable, error);
+ switch (tok) {
+ case IMAPX_TOK_ERROR:
+ case IMAPX_TOK_PROTOCOL:
+ goto fail;
+ case '(':
+ break;
+ default:
+ g_set_error (
+ error, CAMEL_IMAPX_ERROR, 1,
+ "quota_response: expecting '('");
+ goto fail;
+ }
+
+quota_resource:
+
+ tok = camel_imapx_stream_atom (is, &token, &len, cancellable, error);
+ if (tok != 0)
+ goto fail;
+ resource_name = g_strdup ((gchar *) token);
+
+ resource_usage = camel_imapx_stream_number (
+ is, cancellable, &local_error);
+ if (local_error != NULL) {
+ g_propagate_error (error, local_error);
+ goto fail;
+ }
+
+ resource_limit = camel_imapx_stream_number (
+ is, cancellable, &local_error);
+ if (local_error != NULL) {
+ g_propagate_error (error, local_error);
+ goto fail;
+ }
+
+ info = camel_folder_quota_info_new (
+ resource_name, resource_usage, resource_limit);
+ g_queue_push_tail (&queue, info);
+
+ g_free (resource_name);
+ resource_name = NULL;
+
+ tok = camel_imapx_stream_token (is, &token, &len, cancellable, error);
+ switch (tok) {
+ case IMAPX_TOK_ERROR:
+ case IMAPX_TOK_PROTOCOL:
+ goto fail;
+ case ')':
+ break;
+ default:
+ camel_imapx_stream_ungettoken (is, tok, token, len);
+ goto quota_resource;
+ }
+
+ /* Eat the newline. */
+ if (camel_imapx_stream_skip (is, cancellable, error) != 0)
+ goto fail;
+
+ /* String together all the CamelFolderQuotaInfo structs. */
+
+ info = next = NULL;
+
+ while (!g_queue_is_empty (&queue)) {
+ info = g_queue_pop_tail (&queue);
+ info->next = next;
+ next = info;
+ }
+
+ *out_quota_root_name = quota_root_name;
+ *out_quota_info = info;
+
+ return TRUE;
+
+fail:
+ g_free (quota_root_name);
+ g_free (resource_name);
+
+ while (!g_queue_is_empty (&queue)) {
+ info = g_queue_pop_head (&queue);
+ camel_folder_quota_info_free (info);
+ }
+
+ return FALSE;
+}
+
+gboolean
+camel_imapx_parse_quotaroot (CamelIMAPXStream *is,
+ GCancellable *cancellable,
+ gchar **out_mailbox_name,
+ gchar ***out_quota_root_names,
+ GError **error)
+{
+ GQueue queue = G_QUEUE_INIT;
+ gint tok;
+ guint len;
+ guchar *token;
+ gchar *mailbox_name = NULL;
+ gchar **quota_root_names = NULL;
+ gint ii = 0;
+
+ g_return_val_if_fail (CAMEL_IS_IMAPX_STREAM (is), FALSE);
+ g_return_val_if_fail (out_mailbox_name != NULL, FALSE);
+ g_return_val_if_fail (out_quota_root_names != NULL, FALSE);
+
+ /* quotaroot_response ::= "QUOTAROOT" SP astring *(SP astring) */
+
+ tok = camel_imapx_stream_astring (is, &token, cancellable, error);
+ if (tok != 0)
+ goto fail;
+ mailbox_name = camel_utf7_utf8 ((gchar *) token);
+
+ while (TRUE) {
+ /* Peek at the next token, and break
+ * out of the loop if we get a newline. */
+ tok = camel_imapx_stream_token (
+ is, &token, &len, cancellable, error);
+ if (tok == '\n')
+ break;
+ if (tok == IMAPX_TOK_ERROR || tok == IMAPX_TOK_PROTOCOL)
+ goto fail;
+ camel_imapx_stream_ungettoken (is, tok, token, len);
+
+ tok = camel_imapx_stream_astring (
+ is, &token, cancellable, error);
+ if (tok == 0)
+ g_queue_push_tail (
+ &queue, g_strdup ((gchar *) token));
+ else
+ goto fail;
+ }
+
+ quota_root_names = g_new0 (gchar *, queue.length + 1);
+ while (!g_queue_is_empty (&queue))
+ quota_root_names[ii++] = g_queue_pop_head (&queue);
+
+ *out_mailbox_name = mailbox_name;
+ *out_quota_root_names = quota_root_names;
+
+ return TRUE;
+
+fail:
+ g_free (mailbox_name);
+
+ while (!g_queue_is_empty (&queue))
+ g_free (g_queue_pop_head (&queue));
+
+ return FALSE;
+}
+
/* ********************************************************************** */
/*
diff --git a/camel/camel-imapx-utils.h b/camel/camel-imapx-utils.h
index e4abaa6..ed08313 100644
--- a/camel/camel-imapx-utils.h
+++ b/camel/camel-imapx-utils.h
@@ -90,6 +90,8 @@ typedef enum _camel_imapx_id_t {
#define CAMEL_IMAPX_UNTAGGED_NO "NO"
#define CAMEL_IMAPX_UNTAGGED_OK "OK"
#define CAMEL_IMAPX_UNTAGGED_PREAUTH "PREAUTH"
+#define CAMEL_IMAPX_UNTAGGED_QUOTA "QUOTA"
+#define CAMEL_IMAPX_UNTAGGED_QUOTAROOT "QUOTAROOT"
#define CAMEL_IMAPX_UNTAGGED_RECENT "RECENT"
#define CAMEL_IMAPX_UNTAGGED_STATUS "STATUS"
#define CAMEL_IMAPX_UNTAGGED_VANISHED "VANISHED"
@@ -144,6 +146,7 @@ enum {
IMAPX_CAPABILITY_QRESYNC = (1 << 9),
IMAPX_CAPABILITY_LIST_STATUS = (1 << 10),
IMAPX_CAPABILITY_LIST_EXTENDED = (1 << 11),
+ IMAPX_CAPABILITY_QUOTA = (1 << 12)
};
struct _capability_info {
@@ -303,6 +306,19 @@ gchar * imapx_list_get_path (struct _list_info *linfo);
void imapx_free_list (struct _list_info *linfo);
/* ********************************************************************** */
+
+gboolean camel_imapx_parse_quota (struct _CamelIMAPXStream *is,
+ GCancellable *cancellable,
+ gchar **out_quota_root_name,
+ CamelFolderQuotaInfo **out_quota_info,
+ GError **error);
+gboolean camel_imapx_parse_quotaroot (struct _CamelIMAPXStream *is,
+ GCancellable *cancellable,
+ gchar **out_mailbox_name,
+ gchar ***out_quota_root_names,
+ GError **error);
+
+/* ********************************************************************** */
typedef struct _IMAPXJobQueueInfo {
guint queue_len;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]