aboutsummaryrefslogtreecommitdiffstats
path: root/libemail-engine/e-mail-folder-utils.c
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@redhat.com>2012-01-18 00:07:19 +0800
committerMatthew Barnes <mbarnes@redhat.com>2012-01-19 12:48:47 +0800
commit61ae36351b24cc676f60483d576706bf827f2987 (patch)
treec55d9e000efd47fa14865fad2defa79b5ed61ffd /libemail-engine/e-mail-folder-utils.c
parent37644b9d257369c5c158121ca4807cafbe844595 (diff)
downloadgsoc2013-evolution-61ae36351b24cc676f60483d576706bf827f2987.tar
gsoc2013-evolution-61ae36351b24cc676f60483d576706bf827f2987.tar.gz
gsoc2013-evolution-61ae36351b24cc676f60483d576706bf827f2987.tar.bz2
gsoc2013-evolution-61ae36351b24cc676f60483d576706bf827f2987.tar.lz
gsoc2013-evolution-61ae36351b24cc676f60483d576706bf827f2987.tar.xz
gsoc2013-evolution-61ae36351b24cc676f60483d576706bf827f2987.tar.zst
gsoc2013-evolution-61ae36351b24cc676f60483d576706bf827f2987.zip
Introduce libemail-engine and libemail-utils.
These libraries are bound for E-D-S so they live at the lowest layer of Evolution for now -- even libeutil can link to them (but please don't). This is the first step toward moving mail handing to a D-Bus service.
Diffstat (limited to 'libemail-engine/e-mail-folder-utils.c')
-rw-r--r--libemail-engine/e-mail-folder-utils.c1666
1 files changed, 1666 insertions, 0 deletions
diff --git a/libemail-engine/e-mail-folder-utils.c b/libemail-engine/e-mail-folder-utils.c
new file mode 100644
index 0000000000..25754f4f96
--- /dev/null
+++ b/libemail-engine/e-mail-folder-utils.c
@@ -0,0 +1,1666 @@
+/*
+ * e-mail-folder-utils.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/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-mail-folder-utils.h"
+
+#include <glib/gi18n-lib.h>
+
+#include <libemail-engine/mail-tools.h>
+
+/* X-Mailer header value */
+#define X_MAILER ("Evolution " VERSION SUB_VERSION " " VERSION_COMMENT)
+
+typedef struct _AsyncContext AsyncContext;
+
+struct _AsyncContext {
+ CamelMimeMessage *message;
+ CamelMessageInfo *info;
+ CamelMimePart *part;
+ GHashTable *hash_table;
+ GPtrArray *ptr_array;
+ GFile *destination;
+ gchar *fwd_subject;
+ gchar *message_uid;
+};
+
+static void
+async_context_free (AsyncContext *context)
+{
+ if (context->message != NULL)
+ g_object_unref (context->message);
+
+ if (context->info != NULL)
+ camel_message_info_free (context->info);
+
+ if (context->part != NULL)
+ g_object_unref (context->part);
+
+ if (context->hash_table != NULL)
+ g_hash_table_unref (context->hash_table);
+
+ if (context->ptr_array != NULL)
+ g_ptr_array_unref (context->ptr_array);
+
+ if (context->destination != NULL)
+ g_object_unref (context->destination);
+
+ g_free (context->fwd_subject);
+ g_free (context->message_uid);
+
+ g_slice_free (AsyncContext, context);
+}
+
+static void
+mail_folder_append_message_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ e_mail_folder_append_message_sync (
+ CAMEL_FOLDER (object), context->message,
+ context->info, &context->message_uid,
+ cancellable, &error);
+
+ if (error != NULL)
+ g_simple_async_result_take_error (simple, error);
+}
+
+gboolean
+e_mail_folder_append_message_sync (CamelFolder *folder,
+ CamelMimeMessage *message,
+ CamelMessageInfo *info,
+ gchar **appended_uid,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelMedium *medium;
+ gboolean success;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
+
+ medium = CAMEL_MEDIUM (message);
+
+ camel_operation_push_message (
+ cancellable,
+ _("Saving message to folder '%s'"),
+ camel_folder_get_full_name (folder));
+
+ if (camel_medium_get_header (medium, "X-Mailer") == NULL)
+ camel_medium_set_header (medium, "X-Mailer", X_MAILER);
+
+ camel_mime_message_set_date (message, CAMEL_MESSAGE_DATE_CURRENT, 0);
+
+ success = camel_folder_append_message_sync (
+ folder, message, info, appended_uid, cancellable, error);
+
+ camel_operation_pop_message (cancellable);
+
+ return success;
+}
+
+void
+e_mail_folder_append_message (CamelFolder *folder,
+ CamelMimeMessage *message,
+ CamelMessageInfo *info,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ context = g_slice_new0 (AsyncContext);
+ context->message = g_object_ref (message);
+
+ if (info != NULL)
+ context->info = camel_message_info_ref (info);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (folder), callback, user_data,
+ e_mail_folder_append_message);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, mail_folder_append_message_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_folder_append_message_finish (CamelFolder *folder,
+ GAsyncResult *result,
+ gchar **appended_uid,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (folder),
+ e_mail_folder_append_message), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (appended_uid != NULL) {
+ *appended_uid = context->message_uid;
+ context->message_uid = NULL;
+ }
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+static void
+mail_folder_build_attachment_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ context->part = e_mail_folder_build_attachment_sync (
+ CAMEL_FOLDER (object), context->ptr_array,
+ &context->fwd_subject, cancellable, &error);
+
+ if (error != NULL)
+ g_simple_async_result_take_error (simple, error);
+}
+
+CamelMimePart *
+e_mail_folder_build_attachment_sync (CamelFolder *folder,
+ GPtrArray *message_uids,
+ gchar **fwd_subject,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GHashTable *hash_table;
+ CamelMimeMessage *message;
+ CamelMimePart *part;
+ const gchar *uid;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+ g_return_val_if_fail (message_uids != NULL, NULL);
+
+ /* Need at least one message UID to make an attachment. */
+ g_return_val_if_fail (message_uids->len > 0, NULL);
+
+ hash_table = e_mail_folder_get_multiple_messages_sync (
+ folder, message_uids, cancellable, error);
+
+ if (hash_table == NULL)
+ return NULL;
+
+ /* Create the forward subject from the first message. */
+
+ uid = g_ptr_array_index (message_uids, 0);
+ g_return_val_if_fail (uid != NULL, NULL);
+
+ message = g_hash_table_lookup (hash_table, uid);
+ g_return_val_if_fail (message != NULL, NULL);
+
+ if (fwd_subject != NULL)
+ *fwd_subject = mail_tool_generate_forward_subject (message);
+
+ if (message_uids->len == 1) {
+ part = mail_tool_make_message_attachment (message);
+
+ } else {
+ CamelMultipart *multipart;
+ guint ii;
+
+ multipart = camel_multipart_new ();
+ camel_data_wrapper_set_mime_type (
+ CAMEL_DATA_WRAPPER (multipart), "multipart/digest");
+ camel_multipart_set_boundary (multipart, NULL);
+
+ for (ii = 0; ii < message_uids->len; ii++) {
+ uid = g_ptr_array_index (message_uids, ii);
+ g_return_val_if_fail (uid != NULL, NULL);
+
+ message = g_hash_table_lookup (hash_table, uid);
+ g_return_val_if_fail (message != NULL, NULL);
+
+ part = mail_tool_make_message_attachment (message);
+ camel_multipart_add_part (multipart, part);
+ g_object_unref (part);
+ }
+
+ part = camel_mime_part_new ();
+
+ camel_medium_set_content (
+ CAMEL_MEDIUM (part),
+ CAMEL_DATA_WRAPPER (multipart));
+
+ camel_mime_part_set_description (
+ part, _("Forwarded messages"));
+
+ g_object_unref (multipart);
+ }
+
+ g_hash_table_unref (hash_table);
+
+ return part;
+}
+
+void
+e_mail_folder_build_attachment (CamelFolder *folder,
+ GPtrArray *message_uids,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (message_uids != NULL);
+
+ /* Need at least one message UID to make an attachment. */
+ g_return_if_fail (message_uids->len > 0);
+
+ context = g_slice_new0 (AsyncContext);
+ context->ptr_array = g_ptr_array_ref (message_uids);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (folder), callback, user_data,
+ e_mail_folder_build_attachment);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, mail_folder_build_attachment_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+CamelMimePart *
+e_mail_folder_build_attachment_finish (CamelFolder *folder,
+ GAsyncResult *result,
+ gchar **fwd_subject,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (folder),
+ e_mail_folder_build_attachment), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ if (fwd_subject != NULL) {
+ *fwd_subject = context->fwd_subject;
+ context->fwd_subject = NULL;
+ }
+
+ g_return_val_if_fail (CAMEL_IS_MIME_PART (context->part), NULL);
+
+ return g_object_ref (context->part);
+}
+
+static void
+mail_folder_find_duplicate_messages_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ context->hash_table = e_mail_folder_find_duplicate_messages_sync (
+ CAMEL_FOLDER (object), context->ptr_array,
+ cancellable, &error);
+
+ if (error != NULL)
+ g_simple_async_result_take_error (simple, error);
+}
+
+GHashTable *
+e_mail_folder_find_duplicate_messages_sync (CamelFolder *folder,
+ GPtrArray *message_uids,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GQueue trash = G_QUEUE_INIT;
+ GHashTable *hash_table;
+ GHashTable *unique_ids;
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+ g_return_val_if_fail (message_uids != NULL, NULL);
+
+ /* hash_table = { MessageUID : CamelMessage } */
+ hash_table = e_mail_folder_get_multiple_messages_sync (
+ folder, message_uids, cancellable, error);
+
+ if (hash_table == NULL)
+ return NULL;
+
+ camel_operation_push_message (
+ cancellable, _("Scanning messages for duplicates"));
+
+ unique_ids = g_hash_table_new_full (
+ (GHashFunc) g_int64_hash,
+ (GEqualFunc) g_int64_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_free);
+
+ g_hash_table_iter_init (&iter, hash_table);
+
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ const CamelSummaryMessageID *message_id;
+ CamelDataWrapper *content;
+ CamelMessageFlags flags;
+ CamelMessageInfo *info;
+ CamelStream *stream;
+ GByteArray *buffer;
+ gboolean duplicate;
+ gssize n_bytes;
+ gchar *digest;
+
+ info = camel_folder_get_message_info (folder, key);
+ message_id = camel_message_info_message_id (info);
+ flags = camel_message_info_flags (info);
+
+ /* Skip messages marked for deletion. */
+ if (flags & CAMEL_MESSAGE_DELETED) {
+ g_queue_push_tail (&trash, key);
+ camel_message_info_free (info);
+ continue;
+ }
+
+ /* Generate a digest string from the message's content. */
+
+ content = camel_medium_get_content (CAMEL_MEDIUM (value));
+
+ if (content == NULL) {
+ g_queue_push_tail (&trash, key);
+ camel_message_info_free (info);
+ continue;
+ }
+
+ stream = camel_stream_mem_new ();
+
+ n_bytes = camel_data_wrapper_decode_to_stream_sync (
+ content, stream, cancellable, error);
+
+ if (n_bytes < 0) {
+ camel_message_info_free (info);
+ g_object_unref (stream);
+ goto fail;
+ }
+
+ /* The CamelStreamMem owns the buffer. */
+ buffer = camel_stream_mem_get_byte_array (
+ CAMEL_STREAM_MEM (stream));
+ g_return_val_if_fail (buffer != NULL, NULL);
+
+ digest = g_compute_checksum_for_data (
+ G_CHECKSUM_SHA256, buffer->data, buffer->len);
+
+ g_object_unref (stream);
+
+ /* Determine if the message a duplicate. */
+
+ value = g_hash_table_lookup (unique_ids, &message_id->id.id);
+ duplicate = (value != NULL) && g_str_equal (digest, value);
+
+ if (duplicate)
+ g_free (digest);
+ else {
+ gint64 *v_int64;
+
+ /* XXX Might be better to create a GArray
+ * of 64-bit integers and have the hash
+ * table keys point to array elements. */
+ v_int64 = g_new0 (gint64, 1);
+ *v_int64 = (gint64) message_id->id.id;
+
+ g_hash_table_insert (unique_ids, v_int64, digest);
+ g_queue_push_tail (&trash, key);
+ }
+
+ camel_message_info_free (info);
+ }
+
+ /* Delete all non-duplicate messages from the hash table. */
+ while ((key = g_queue_pop_head (&trash)) != NULL)
+ g_hash_table_remove (hash_table, key);
+
+ goto exit;
+
+fail:
+ g_hash_table_destroy (hash_table);
+ hash_table = NULL;
+
+exit:
+ camel_operation_pop_message (cancellable);
+
+ g_hash_table_destroy (unique_ids);
+
+ return hash_table;
+}
+
+void
+e_mail_folder_find_duplicate_messages (CamelFolder *folder,
+ GPtrArray *message_uids,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (message_uids != NULL);
+
+ context = g_slice_new0 (AsyncContext);
+ context->ptr_array = g_ptr_array_ref (message_uids);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (folder), callback, user_data,
+ e_mail_folder_find_duplicate_messages);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, mail_folder_find_duplicate_messages_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+GHashTable *
+e_mail_folder_find_duplicate_messages_finish (CamelFolder *folder,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (folder),
+ e_mail_folder_find_duplicate_messages), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ return g_hash_table_ref (context->hash_table);
+}
+
+static void
+mail_folder_get_multiple_messages_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ context->hash_table = e_mail_folder_get_multiple_messages_sync (
+ CAMEL_FOLDER (object), context->ptr_array,
+ cancellable, &error);
+
+ if (error != NULL)
+ g_simple_async_result_take_error (simple, error);
+}
+
+GHashTable *
+e_mail_folder_get_multiple_messages_sync (CamelFolder *folder,
+ GPtrArray *message_uids,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GHashTable *hash_table;
+ CamelMimeMessage *message;
+ guint ii;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+ g_return_val_if_fail (message_uids != NULL, NULL);
+
+ camel_operation_push_message (
+ cancellable,
+ ngettext (
+ "Retrieving %d message",
+ "Retrieving %d messages",
+ message_uids->len),
+ message_uids->len);
+
+ hash_table = g_hash_table_new_full (
+ (GHashFunc) g_str_hash,
+ (GEqualFunc) g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_object_unref);
+
+ /* This is an all or nothing operation. Destroy the
+ * hash table if we fail to retrieve any message. */
+
+ for (ii = 0; ii < message_uids->len; ii++) {
+ const gchar *uid;
+ gint percent;
+
+ uid = g_ptr_array_index (message_uids, ii);
+ percent = ((ii + 1) * 100) / message_uids->len;
+
+ message = camel_folder_get_message_sync (
+ folder, uid, cancellable, error);
+
+ camel_operation_progress (cancellable, percent);
+
+ if (CAMEL_IS_MIME_MESSAGE (message)) {
+ g_hash_table_insert (
+ hash_table, g_strdup (uid), message);
+ } else {
+ g_hash_table_destroy (hash_table);
+ hash_table = NULL;
+ break;
+ }
+ }
+
+ camel_operation_pop_message (cancellable);
+
+ return hash_table;
+}
+
+void
+e_mail_folder_get_multiple_messages (CamelFolder *folder,
+ GPtrArray *message_uids,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (message_uids != NULL);
+
+ context = g_slice_new0 (AsyncContext);
+ context->ptr_array = g_ptr_array_ref (message_uids);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (folder), callback, user_data,
+ e_mail_folder_get_multiple_messages);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, mail_folder_get_multiple_messages_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+GHashTable *
+e_mail_folder_get_multiple_messages_finish (CamelFolder *folder,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (folder),
+ e_mail_folder_get_multiple_messages), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ return g_hash_table_ref (context->hash_table);
+}
+
+static void
+mail_folder_remove_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ GError *error = NULL;
+
+ e_mail_folder_remove_sync (
+ CAMEL_FOLDER (object), cancellable, &error);
+
+ if (error != NULL)
+ g_simple_async_result_take_error (simple, error);
+}
+
+static gboolean
+mail_folder_remove_recursive (CamelStore *store,
+ CamelFolderInfo *folder_info,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean success = TRUE;
+
+ while (folder_info != NULL) {
+ CamelFolder *folder;
+
+ if (folder_info->child != NULL) {
+ success = mail_folder_remove_recursive (
+ store, folder_info->child, cancellable, error);
+ if (!success)
+ break;
+ }
+
+ folder = camel_store_get_folder_sync (
+ store, folder_info->full_name, 0, cancellable, error);
+ if (folder == NULL) {
+ success = FALSE;
+ break;
+ }
+
+ if (!CAMEL_IS_VEE_FOLDER (folder)) {
+ GPtrArray *uids;
+ guint ii;
+
+ /* Delete every message in this folder,
+ * then expunge it. */
+
+ camel_folder_freeze (folder);
+
+ uids = camel_folder_get_uids (folder);
+
+ for (ii = 0; ii < uids->len; ii++)
+ camel_folder_delete_message (
+ folder, uids->pdata[ii]);
+
+ camel_folder_free_uids (folder, uids);
+
+ success = camel_folder_synchronize_sync (
+ folder, TRUE, cancellable, error);
+
+ camel_folder_thaw (folder);
+ }
+
+ g_object_unref (folder);
+
+ if (!success)
+ break;
+
+ /* If the store supports subscriptions,
+ * then unsubscribe from this folder. */
+ if (CAMEL_IS_SUBSCRIBABLE (store)) {
+ success = camel_subscribable_unsubscribe_folder_sync (
+ CAMEL_SUBSCRIBABLE (store),
+ folder_info->full_name,
+ cancellable, error);
+ if (!success)
+ break;
+ }
+
+ success = camel_store_delete_folder_sync (
+ store, folder_info->full_name, cancellable, error);
+ if (!success)
+ break;
+
+ folder_info = folder_info->next;
+ }
+
+ return success;
+}
+
+static void
+follow_cancel_cb (GCancellable *cancellable,
+ GCancellable *transparent_cancellable)
+{
+ g_cancellable_cancel (transparent_cancellable);
+}
+
+gboolean
+e_mail_folder_remove_sync (CamelFolder *folder,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelFolderInfo *folder_info;
+ CamelFolderInfo *to_remove;
+ CamelFolderInfo *next = NULL;
+ CamelStore *parent_store;
+ const gchar *full_name;
+ gboolean success = TRUE;
+ GCancellable *transparent_cancellable = NULL;
+ gulong cbid = 0;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+
+ full_name = camel_folder_get_full_name (folder);
+ parent_store = camel_folder_get_parent_store (folder);
+
+ folder_info = camel_store_get_folder_info_sync (
+ parent_store, full_name,
+ CAMEL_STORE_FOLDER_INFO_FAST |
+ CAMEL_STORE_FOLDER_INFO_RECURSIVE |
+ CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
+ cancellable, error);
+
+ if (folder_info == NULL)
+ return FALSE;
+
+ to_remove = folder_info;
+
+ /* For cases when the top-level folder_info contains siblings,
+ * such as when full_name contains a wildcard letter, compare
+ * the folder name against folder_info->full_name to avoid
+ * removing more folders than requested. */
+ if (folder_info->next != NULL) {
+ while (to_remove != NULL) {
+ if (g_strcmp0 (to_remove->full_name, full_name) == 0)
+ break;
+ to_remove = to_remove->next;
+ }
+
+ /* XXX Should we set a GError and return FALSE here? */
+ if (to_remove == NULL) {
+ g_warning (
+ "%s: Failed to find folder '%s'",
+ G_STRFUNC, full_name);
+ camel_store_free_folder_info (
+ parent_store, folder_info);
+ return TRUE;
+ }
+
+ /* Prevent iterating over siblings. */
+ next = to_remove->next;
+ to_remove->next = NULL;
+ }
+
+ camel_operation_push_message (
+ cancellable, _("Removing folder '%s'"),
+ camel_folder_get_full_name (folder));
+
+ if (cancellable) {
+ transparent_cancellable = g_cancellable_new ();
+ cbid = g_cancellable_connect (cancellable, G_CALLBACK (follow_cancel_cb), transparent_cancellable, NULL);
+ }
+
+ success = mail_folder_remove_recursive (
+ parent_store, to_remove, transparent_cancellable, error);
+
+ if (transparent_cancellable) {
+ g_cancellable_disconnect (cancellable, cbid);
+ g_object_unref (transparent_cancellable);
+ }
+
+ camel_operation_pop_message (cancellable);
+
+ /* Restore the folder_info tree to its original
+ * state so we don't leak folder_info nodes. */
+ to_remove->next = next;
+
+ camel_store_free_folder_info (parent_store, folder_info);
+
+ return success;
+}
+
+void
+e_mail_folder_remove (CamelFolder *folder,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (folder), callback,
+ user_data, e_mail_folder_remove);
+
+ g_simple_async_result_run_in_thread (
+ simple, mail_folder_remove_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_folder_remove_finish (CamelFolder *folder,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (folder),
+ e_mail_folder_remove), 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
+mail_folder_remove_attachments_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ e_mail_folder_remove_attachments_sync (
+ CAMEL_FOLDER (object), context->ptr_array,
+ cancellable, &error);
+
+ if (error != NULL)
+ g_simple_async_result_take_error (simple, error);
+}
+
+/* Helper for e_mail_folder_remove_attachments_sync() */
+static gboolean
+mail_folder_strip_message (CamelFolder *folder,
+ CamelMimeMessage *message,
+ const gchar *message_uid,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelDataWrapper *content;
+ CamelMultipart *multipart;
+ gboolean modified = FALSE;
+ gboolean success = TRUE;
+ guint ii, n_parts;
+
+ content = camel_medium_get_content (CAMEL_MEDIUM (message));
+
+ if (!CAMEL_IS_MULTIPART (content))
+ return TRUE;
+
+ multipart = CAMEL_MULTIPART (content);
+ n_parts = camel_multipart_get_number (multipart);
+
+ /* Replace MIME parts with "attachment" or "inline" dispositions
+ * with a small "text/plain" part saying the file was removed. */
+ for (ii = 0; ii < n_parts; ii++) {
+ CamelMimePart *mime_part;
+ const gchar *disposition;
+ gboolean is_attachment;
+
+ mime_part = camel_multipart_get_part (multipart, ii);
+ disposition = camel_mime_part_get_disposition (mime_part);
+
+ is_attachment =
+ (g_strcmp0 (disposition, "attachment") == 0) ||
+ (g_strcmp0 (disposition, "inline") == 0);
+
+ if (is_attachment) {
+ const gchar *filename;
+ const gchar *content_type;
+ gchar *content;
+
+ disposition = "inline";
+ content_type = "text/plain";
+ filename = camel_mime_part_get_filename (mime_part);
+
+ if (filename != NULL && *filename != '\0')
+ content = g_strdup_printf (
+ _("File \"%s\" has been removed."),
+ filename);
+ else
+ content = g_strdup (
+ _("File has been removed."));
+
+ camel_mime_part_set_content (
+ mime_part, content,
+ strlen (content), content_type);
+ camel_mime_part_set_content_type (
+ mime_part, content_type);
+ camel_mime_part_set_disposition (
+ mime_part, disposition);
+
+ modified = TRUE;
+ }
+ }
+
+ /* Append the modified message with removed attachments to
+ * the folder and mark the original message for deletion. */
+ if (modified) {
+ CamelMessageInfo *orig_info;
+ CamelMessageInfo *copy_info;
+ CamelMessageFlags flags;
+
+ orig_info =
+ camel_folder_get_message_info (folder, message_uid);
+ copy_info =
+ camel_message_info_new_from_header (
+ NULL, CAMEL_MIME_PART (message)->headers);
+
+ flags = camel_folder_get_message_flags (folder, message_uid);
+ camel_message_info_set_flags (copy_info, flags, flags);
+
+ success = camel_folder_append_message_sync (
+ folder, message, copy_info, NULL, cancellable, error);
+ if (success)
+ camel_message_info_set_flags (
+ orig_info,
+ CAMEL_MESSAGE_DELETED,
+ CAMEL_MESSAGE_DELETED);
+
+ camel_folder_free_message_info (folder, orig_info);
+ camel_message_info_free (copy_info);
+ }
+
+ return success;
+}
+
+gboolean
+e_mail_folder_remove_attachments_sync (CamelFolder *folder,
+ GPtrArray *message_uids,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean success = TRUE;
+ guint ii;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+ g_return_val_if_fail (message_uids != NULL, FALSE);
+
+ camel_folder_freeze (folder);
+
+ camel_operation_push_message (cancellable, _("Removing attachments"));
+
+ for (ii = 0; success && ii < message_uids->len; ii++) {
+ CamelMimeMessage *message;
+ const gchar *uid;
+ gint percent;
+
+ uid = g_ptr_array_index (message_uids, ii);
+
+ message = camel_folder_get_message_sync (
+ folder, uid, cancellable, error);
+
+ if (message == NULL) {
+ success = FALSE;
+ break;
+ }
+
+ success = mail_folder_strip_message (
+ folder, message, uid, cancellable, error);
+
+ percent = ((ii + 1) * 100) / message_uids->len;
+ camel_operation_progress (cancellable, percent);
+
+ g_object_unref (message);
+ }
+
+ camel_operation_pop_message (cancellable);
+
+ if (success)
+ camel_folder_synchronize_sync (
+ folder, FALSE, cancellable, error);
+
+ camel_folder_thaw (folder);
+
+ return success;
+}
+
+void
+e_mail_folder_remove_attachments (CamelFolder *folder,
+ GPtrArray *message_uids,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (message_uids != NULL);
+
+ context = g_slice_new0 (AsyncContext);
+ context->ptr_array = g_ptr_array_ref (message_uids);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (folder), callback, user_data,
+ e_mail_folder_remove_attachments);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, mail_folder_remove_attachments_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_folder_remove_attachments_finish (CamelFolder *folder,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (folder),
+ e_mail_folder_remove_attachments), 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
+mail_folder_save_messages_thread (GSimpleAsyncResult *simple,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ e_mail_folder_save_messages_sync (
+ CAMEL_FOLDER (object), context->ptr_array,
+ context->destination, cancellable, &error);
+
+ if (error != NULL)
+ g_simple_async_result_take_error (simple, error);
+}
+
+/* Helper for e_mail_folder_save_messages_sync() */
+static void
+mail_folder_save_prepare_part (CamelMimePart *mime_part)
+{
+ CamelDataWrapper *content;
+
+ content = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
+
+ if (content == NULL)
+ return;
+
+ if (CAMEL_IS_MULTIPART (content)) {
+ guint n_parts, ii;
+
+ n_parts = camel_multipart_get_number (
+ CAMEL_MULTIPART (content));
+ for (ii = 0; ii < n_parts; ii++) {
+ mime_part = camel_multipart_get_part (
+ CAMEL_MULTIPART (content), ii);
+ mail_folder_save_prepare_part (mime_part);
+ }
+
+ } else if (CAMEL_IS_MIME_MESSAGE (content)) {
+ mail_folder_save_prepare_part (CAMEL_MIME_PART (content));
+
+ } else {
+ CamelContentType *type;
+
+ /* Save textual parts as 8-bit, not encoded. */
+ type = camel_data_wrapper_get_mime_type_field (content);
+ if (camel_content_type_is (type, "text", "*"))
+ camel_mime_part_set_encoding (
+ mime_part, CAMEL_TRANSFER_ENCODING_8BIT);
+ }
+}
+
+gboolean
+e_mail_folder_save_messages_sync (CamelFolder *folder,
+ GPtrArray *message_uids,
+ GFile *destination,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GFileOutputStream *file_output_stream;
+ GByteArray *byte_array;
+ CamelStream *base_stream;
+ gboolean success = TRUE;
+ guint ii;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
+ g_return_val_if_fail (message_uids != NULL, FALSE);
+ g_return_val_if_fail (G_IS_FILE (destination), FALSE);
+
+ /* Need at least one message UID to save. */
+ g_return_val_if_fail (message_uids->len > 0, FALSE);
+
+ camel_operation_push_message (
+ cancellable, ngettext (
+ "Saving %d message",
+ "Saving %d messages",
+ message_uids->len),
+ message_uids->len);
+
+ file_output_stream = g_file_replace (
+ destination, NULL, FALSE,
+ G_FILE_CREATE_PRIVATE |
+ G_FILE_CREATE_REPLACE_DESTINATION,
+ cancellable, error);
+
+ if (file_output_stream == NULL) {
+ camel_operation_pop_message (cancellable);
+ return FALSE;
+ }
+
+ /* CamelStreamMem takes ownership of the GByteArray. */
+ byte_array = g_byte_array_new ();
+ base_stream = camel_stream_mem_new_with_byte_array (byte_array);
+
+ for (ii = 0; ii < message_uids->len; ii++) {
+ CamelMimeMessage *message;
+ CamelMimeFilter *filter;
+ CamelStream *stream;
+ const gchar *uid;
+ gchar *from_line;
+ gint percent;
+ gint retval;
+
+ uid = g_ptr_array_index (message_uids, ii);
+
+ message = camel_folder_get_message_sync (
+ folder, uid, cancellable, error);
+ if (message == NULL) {
+ success = FALSE;
+ goto exit;
+ }
+
+ mail_folder_save_prepare_part (CAMEL_MIME_PART (message));
+
+ from_line = camel_mime_message_build_mbox_from (message);
+ g_return_val_if_fail (from_line != NULL, FALSE);
+
+ success = g_output_stream_write_all (
+ G_OUTPUT_STREAM (file_output_stream),
+ from_line, strlen (from_line), NULL,
+ cancellable, error);
+
+ g_free (from_line);
+
+ if (!success) {
+ g_object_unref (message);
+ goto exit;
+ }
+
+ filter = camel_mime_filter_from_new ();
+ stream = camel_stream_filter_new (base_stream);
+ camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), filter);
+
+ retval = camel_data_wrapper_write_to_stream_sync (
+ CAMEL_DATA_WRAPPER (message),
+ stream, cancellable, error);
+
+ g_object_unref (filter);
+ g_object_unref (stream);
+
+ if (retval == -1) {
+ g_object_unref (message);
+ goto exit;
+ }
+
+ g_byte_array_append (byte_array, (guint8 *) "\n", 1);
+
+ success = g_output_stream_write_all (
+ G_OUTPUT_STREAM (file_output_stream),
+ byte_array->data, byte_array->len,
+ NULL, cancellable, error);
+
+ if (!success) {
+ g_object_unref (message);
+ goto exit;
+ }
+
+ percent = ((ii + 1) * 100) / message_uids->len;
+ camel_operation_progress (cancellable, percent);
+
+ /* Flush the buffer for the next message.
+ * For memory streams this never fails. */
+ g_seekable_seek (
+ G_SEEKABLE (base_stream),
+ 0, G_SEEK_SET, NULL, NULL);
+
+ g_object_unref (message);
+ }
+
+exit:
+ g_object_unref (file_output_stream);
+ g_object_unref (base_stream);
+
+ camel_operation_pop_message (cancellable);
+
+ if (!success) {
+ /* Try deleting the destination file. */
+ g_file_delete (destination, NULL, NULL);
+ }
+
+ return success;
+}
+
+void
+e_mail_folder_save_messages (CamelFolder *folder,
+ GPtrArray *message_uids,
+ GFile *destination,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ g_return_if_fail (message_uids != NULL);
+ g_return_if_fail (G_IS_FILE (destination));
+
+ /* Need at least one message UID to save. */
+ g_return_if_fail (message_uids->len > 0);
+
+ context = g_slice_new0 (AsyncContext);
+ context->ptr_array = g_ptr_array_ref (message_uids);
+ context->destination = g_object_ref (destination);
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (folder), callback, user_data,
+ e_mail_folder_save_messages);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ g_simple_async_result_run_in_thread (
+ simple, mail_folder_save_messages_thread,
+ io_priority, cancellable);
+
+ g_object_unref (simple);
+}
+
+gboolean
+e_mail_folder_save_messages_finish (CamelFolder *folder,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (folder),
+ e_mail_folder_save_messages), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+
+ /* Assume success unless a GError is set. */
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+
+/**
+ * e_mail_folder_uri_build:
+ * @store: a #CamelStore
+ * @folder_name: a folder name
+ *
+ * Builds a folder URI string from @store and @folder_name.
+ *
+ * Returns: a newly-allocated folder URI string
+ **/
+gchar *
+e_mail_folder_uri_build (CamelStore *store,
+ const gchar *folder_name)
+{
+ const gchar *uid;
+ gchar *encoded_name;
+ gchar *encoded_uid;
+ gchar *uri;
+
+ g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
+ g_return_val_if_fail (folder_name != NULL, NULL);
+
+ /* Skip the leading slash, if present. */
+ if (*folder_name == '/')
+ folder_name++;
+
+ uid = camel_service_get_uid (CAMEL_SERVICE (store));
+
+ encoded_uid = camel_url_encode (uid, ":;@/");
+ encoded_name = camel_url_encode (folder_name, "#");
+
+ uri = g_strdup_printf ("folder://%s/%s", encoded_uid, encoded_name);
+
+ g_free (encoded_uid);
+ g_free (encoded_name);
+
+ return uri;
+}
+
+/**
+ * e_mail_folder_uri_parse:
+ * @session: a #CamelSession
+ * @folder_uri: a folder URI
+ * @out_store: return location for a #CamelStore, or %NULL
+ * @out_folder_name: return location for a folder name, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Parses a folder URI generated by e_mail_folder_uri_build() and
+ * returns the corresponding #CamelStore instance in @out_store and
+ * folder name string in @out_folder_name. If the URI is malformed
+ * or no corresponding store exists, the function sets @error and
+ * returns %FALSE.
+ *
+ * If the function is able to parse the URI, the #CamelStore instance
+ * set in @out_store should be unreferenced with g_object_unref() when
+ * done with it, and the folder name string set in @out_folder_name
+ * should be freed with g_free().
+ *
+ * The function also handles older style URIs, such as ones where the
+ * #CamelStore's #CamelStore::uri string was embedded directly in the
+ * folder URI, and account-based URIs that used an "email://" prefix.
+ *
+ * Returns: %TRUE if @folder_uri could be parsed, %FALSE otherwise
+ **/
+gboolean
+e_mail_folder_uri_parse (CamelSession *session,
+ const gchar *folder_uri,
+ CamelStore **out_store,
+ gchar **out_folder_name,
+ GError **error)
+{
+ CamelURL *url;
+ CamelService *service = NULL;
+ gchar *folder_name = NULL;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
+ g_return_val_if_fail (folder_uri != NULL, FALSE);
+
+ url = camel_url_new (folder_uri, error);
+ if (url == NULL)
+ return FALSE;
+
+ /* Current URI Format: 'folder://' STORE_UID '/' FOLDER_PATH */
+ if (g_strcmp0 (url->protocol, "folder") == 0) {
+
+ if (url->host != NULL) {
+ gchar *uid;
+
+ if (url->user == NULL || *url->user == '\0')
+ uid = g_strdup (url->host);
+ else
+ uid = g_strconcat (
+ url->user, "@", url->host, NULL);
+
+ service = camel_session_get_service (session, uid);
+ g_free (uid);
+ }
+
+ if (url->path != NULL && *url->path == '/')
+ folder_name = camel_url_decode_path (url->path + 1);
+
+ /* This style was used to reference accounts by UID before
+ * CamelServices themselves had UIDs. Some examples are:
+ *
+ * Special cases:
+ *
+ * 'email://local@local/' FOLDER_PATH
+ * 'email://vfolder@local/' FOLDER_PATH
+ *
+ * General case:
+ *
+ * 'email://' ACCOUNT_UID '/' FOLDER_PATH
+ *
+ * Note: ACCOUNT_UID is now equivalent to STORE_UID, and
+ * the STORE_UIDs for the special cases are 'local'
+ * and 'vfolder'.
+ */
+ } else if (g_strcmp0 (url->protocol, "email") == 0) {
+ gchar *uid = NULL;
+
+ /* Handle the special cases. */
+ if (g_strcmp0 (url->host, "local") == 0) {
+ if (g_strcmp0 (url->user, "local") == 0)
+ uid = g_strdup ("local");
+ if (g_strcmp0 (url->user, "vfolder") == 0)
+ uid = g_strdup ("vfolder");
+ }
+
+ /* Handle the general case. */
+ if (uid == NULL && url->host != NULL) {
+ if (url->user == NULL)
+ uid = g_strdup (url->host);
+ else
+ uid = g_strdup_printf (
+ "%s@%s", url->user, url->host);
+ }
+
+ if (uid != NULL) {
+ service = camel_session_get_service (session, uid);
+ g_free (uid);
+ }
+
+ if (url->path != NULL && *url->path == '/')
+ folder_name = camel_url_decode_path (url->path + 1);
+
+ /* CamelFolderInfo URIs used to embed the store's URI, so the
+ * folder name is appended as either a path part or a fragment
+ * part, depending whether the store's URI used the path part.
+ * To determine which it is, you have to check the provider
+ * flags for CAMEL_URL_FRAGMENT_IS_PATH. */
+ } else {
+ service = camel_session_get_service_by_url (
+ session, url, CAMEL_PROVIDER_STORE);
+
+ if (CAMEL_IS_STORE (service)) {
+ CamelProvider *provider;
+
+ provider = camel_service_get_provider (service);
+
+ if (provider->url_flags & CAMEL_URL_FRAGMENT_IS_PATH)
+ folder_name = g_strdup (url->fragment);
+ else if (url->path != NULL && *url->path == '/')
+ folder_name = g_strdup (url->path + 1);
+ }
+ }
+
+ if (CAMEL_IS_STORE (service) && folder_name != NULL) {
+ if (out_store != NULL)
+ *out_store = g_object_ref (service);
+
+ if (out_folder_name != NULL) {
+ *out_folder_name = folder_name;
+ folder_name = NULL;
+ }
+
+ success = TRUE;
+ } else {
+ g_set_error (
+ error, CAMEL_FOLDER_ERROR,
+ CAMEL_FOLDER_ERROR_INVALID,
+ _("Invalid folder URI '%s'"),
+ folder_uri);
+ }
+
+ g_free (folder_name);
+
+ camel_url_free (url);
+
+ return success;
+}
+
+/**
+ * e_mail_folder_uri_equal:
+ * @session: a #CamelSession
+ * @folder_uri_a: a folder URI
+ * @folder_uri_b: another folder URI
+ *
+ * Compares two folder URIs for equality. If either URI is invalid,
+ * the function returns %FALSE.
+ *
+ * Returns: %TRUE if the URIs are equal, %FALSE if not
+ **/
+gboolean
+e_mail_folder_uri_equal (CamelSession *session,
+ const gchar *folder_uri_a,
+ const gchar *folder_uri_b)
+{
+ CamelStore *store_a;
+ CamelStore *store_b;
+ CamelStoreClass *class;
+ gchar *folder_name_a;
+ gchar *folder_name_b;
+ gboolean success_a;
+ gboolean success_b;
+ gboolean equal = FALSE;
+
+ g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
+ g_return_val_if_fail (folder_uri_a != NULL, FALSE);
+ g_return_val_if_fail (folder_uri_b != NULL, FALSE);
+
+ success_a = e_mail_folder_uri_parse (
+ session, folder_uri_a, &store_a, &folder_name_a, NULL);
+
+ success_b = e_mail_folder_uri_parse (
+ session, folder_uri_b, &store_b, &folder_name_b, NULL);
+
+ if (!success_a || !success_b)
+ goto exit;
+
+ if (store_a != store_b)
+ goto exit;
+
+ /* Doesn't matter which store we use since they're the same. */
+ class = CAMEL_STORE_GET_CLASS (store_a);
+ g_return_val_if_fail (class->compare_folder_name != NULL, FALSE);
+
+ equal = class->compare_folder_name (folder_name_a, folder_name_b);
+
+exit:
+ if (success_a) {
+ g_object_unref (store_a);
+ g_free (folder_name_a);
+ }
+
+ if (success_b) {
+ g_object_unref (store_b);
+ g_free (folder_name_b);
+ }
+
+ return equal;
+}
+
+/**
+ * e_mail_folder_uri_from_folder:
+ * @folder: a #CamelFolder
+ *
+ * Convenience function for building a folder URI from a #CamelFolder.
+ * Free the returned URI string with g_free().
+ *
+ * Returns: a newly-allocated folder URI string
+ **/
+gchar *
+e_mail_folder_uri_from_folder (CamelFolder *folder)
+{
+ CamelStore *store;
+ const gchar *folder_name;
+
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+
+ store = camel_folder_get_parent_store (folder);
+ folder_name = camel_folder_get_full_name (folder);
+
+ return e_mail_folder_uri_build (store, folder_name);
+}
+
+/**
+ * e_mail_folder_uri_to_markup:
+ * @session: a #CamelSession
+ * @folder_uri: a folder URI
+ * @error: return location for a #GError, or %NULL
+ *
+ * Converts @folder_uri to a markup string suitable for displaying to users.
+ * The string consists of the #CamelStore display name (in bold), followed
+ * by the folder path. If the URI is malformed or no corresponding store
+ * exists, the function sets @error and returns %NULL. Free the returned
+ * string with g_free().
+ *
+ * Returns: a newly-allocated markup string, or %NULL
+ **/
+gchar *
+e_mail_folder_uri_to_markup (CamelSession *session,
+ const gchar *folder_uri,
+ GError **error)
+{
+ CamelStore *store = NULL;
+ const gchar *display_name;
+ gchar *folder_name = NULL;
+ gchar *markup;
+ gboolean success;
+
+ g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
+ g_return_val_if_fail (folder_uri != NULL, NULL);
+
+ success = e_mail_folder_uri_parse (
+ session, folder_uri, &store, &folder_name, error);
+
+ if (!success)
+ return NULL;
+
+ g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
+ g_return_val_if_fail (folder_name != NULL, NULL);
+
+ display_name = camel_service_get_display_name (CAMEL_SERVICE (store));
+
+ markup = g_markup_printf_escaped (
+ "<b>%s</b> : %s", display_name, folder_name);
+
+ g_object_unref (store);
+ g_free (folder_name);
+
+ return markup;
+}