diff options
Diffstat (limited to 'widgets/misc/e-attachment-store.c')
-rw-r--r-- | widgets/misc/e-attachment-store.c | 1281 |
1 files changed, 0 insertions, 1281 deletions
diff --git a/widgets/misc/e-attachment-store.c b/widgets/misc/e-attachment-store.c deleted file mode 100644 index 95465eba81..0000000000 --- a/widgets/misc/e-attachment-store.c +++ /dev/null @@ -1,1281 +0,0 @@ -/* - * e-attachment-store.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/> - * - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "e-attachment-store.h" - -#include <errno.h> -#include <glib/gi18n.h> - -#include "e-util/e-util.h" -#include "e-util/e-mktemp.h" - -#define E_ATTACHMENT_STORE_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_ATTACHMENT_STORE, EAttachmentStorePrivate)) - -struct _EAttachmentStorePrivate { - GHashTable *attachment_index; - - guint ignore_row_changed : 1; -}; - -enum { - PROP_0, - PROP_NUM_ATTACHMENTS, - PROP_NUM_LOADING, - PROP_TOTAL_SIZE -}; - -G_DEFINE_TYPE ( - EAttachmentStore, - e_attachment_store, - GTK_TYPE_LIST_STORE) - -static void -attachment_store_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_NUM_ATTACHMENTS: - g_value_set_uint ( - value, - e_attachment_store_get_num_attachments ( - E_ATTACHMENT_STORE (object))); - return; - - case PROP_NUM_LOADING: - g_value_set_uint ( - value, - e_attachment_store_get_num_loading ( - E_ATTACHMENT_STORE (object))); - return; - - case PROP_TOTAL_SIZE: - g_value_set_uint64 ( - value, - e_attachment_store_get_total_size ( - E_ATTACHMENT_STORE (object))); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -attachment_store_dispose (GObject *object) -{ - e_attachment_store_remove_all (E_ATTACHMENT_STORE (object)); - - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (e_attachment_store_parent_class)->dispose (object); -} - -static void -attachment_store_finalize (GObject *object) -{ - EAttachmentStorePrivate *priv; - - priv = E_ATTACHMENT_STORE_GET_PRIVATE (object); - - g_hash_table_destroy (priv->attachment_index); - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (e_attachment_store_parent_class)->finalize (object); -} - -static void -e_attachment_store_class_init (EAttachmentStoreClass *class) -{ - GObjectClass *object_class; - - g_type_class_add_private (class, sizeof (EAttachmentStorePrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->get_property = attachment_store_get_property; - object_class->dispose = attachment_store_dispose; - object_class->finalize = attachment_store_finalize; - - g_object_class_install_property ( - object_class, - PROP_NUM_ATTACHMENTS, - g_param_spec_uint ( - "num-attachments", - "Num Attachments", - NULL, - 0, - G_MAXUINT, - 0, - G_PARAM_READABLE)); - - g_object_class_install_property ( - object_class, - PROP_NUM_LOADING, - g_param_spec_uint ( - "num-loading", - "Num Loading", - NULL, - 0, - G_MAXUINT, - 0, - G_PARAM_READABLE)); - - g_object_class_install_property ( - object_class, - PROP_TOTAL_SIZE, - g_param_spec_uint64 ( - "total-size", - "Total Size", - NULL, - 0, - G_MAXUINT64, - 0, - G_PARAM_READABLE)); -} - -static void -e_attachment_store_init (EAttachmentStore *store) -{ - GType types[E_ATTACHMENT_STORE_NUM_COLUMNS]; - GHashTable *attachment_index; - gint column = 0; - - attachment_index = g_hash_table_new_full ( - g_direct_hash, g_direct_equal, - (GDestroyNotify) g_object_unref, - (GDestroyNotify) gtk_tree_row_reference_free); - - store->priv = E_ATTACHMENT_STORE_GET_PRIVATE (store); - store->priv->attachment_index = attachment_index; - - types[column++] = E_TYPE_ATTACHMENT; /* COLUMN_ATTACHMENT */ - types[column++] = G_TYPE_STRING; /* COLUMN_CAPTION */ - types[column++] = G_TYPE_STRING; /* COLUMN_CONTENT_TYPE */ - types[column++] = G_TYPE_STRING; /* COLUMN_DESCRIPTION */ - types[column++] = G_TYPE_ICON; /* COLUMN_ICON */ - types[column++] = G_TYPE_BOOLEAN; /* COLUMN_LOADING */ - types[column++] = G_TYPE_INT; /* COLUMN_PERCENT */ - types[column++] = G_TYPE_BOOLEAN; /* COLUMN_SAVING */ - types[column++] = G_TYPE_UINT64; /* COLUMN_SIZE */ - - g_assert (column == E_ATTACHMENT_STORE_NUM_COLUMNS); - - gtk_list_store_set_column_types ( - GTK_LIST_STORE (store), G_N_ELEMENTS (types), types); -} - -GtkTreeModel * -e_attachment_store_new (void) -{ - return g_object_new (E_TYPE_ATTACHMENT_STORE, NULL); -} - -void -e_attachment_store_add_attachment (EAttachmentStore *store, - EAttachment *attachment) -{ - GtkTreeRowReference *reference; - GtkTreeModel *model; - GtkTreePath *path; - GtkTreeIter iter; - - g_return_if_fail (E_IS_ATTACHMENT_STORE (store)); - g_return_if_fail (E_IS_ATTACHMENT (attachment)); - - gtk_list_store_append (GTK_LIST_STORE (store), &iter); - - gtk_list_store_set ( - GTK_LIST_STORE (store), &iter, - E_ATTACHMENT_STORE_COLUMN_ATTACHMENT, attachment, -1); - - model = GTK_TREE_MODEL (store); - path = gtk_tree_model_get_path (model, &iter); - reference = gtk_tree_row_reference_new (model, path); - gtk_tree_path_free (path); - - g_hash_table_insert ( - store->priv->attachment_index, - g_object_ref (attachment), reference); - - /* This lets the attachment tell us when to update. */ - e_attachment_set_reference (attachment, reference); - - g_object_freeze_notify (G_OBJECT (store)); - g_object_notify (G_OBJECT (store), "num-attachments"); - g_object_notify (G_OBJECT (store), "total-size"); - g_object_thaw_notify (G_OBJECT (store)); -} - -gboolean -e_attachment_store_remove_attachment (EAttachmentStore *store, - EAttachment *attachment) -{ - GtkTreeRowReference *reference; - GHashTable *hash_table; - GtkTreeModel *model; - GtkTreePath *path; - GtkTreeIter iter; - - g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), FALSE); - g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE); - - hash_table = store->priv->attachment_index; - reference = g_hash_table_lookup (hash_table, attachment); - - if (reference == NULL) - return FALSE; - - if (!gtk_tree_row_reference_valid (reference)) { - g_hash_table_remove (hash_table, attachment); - return FALSE; - } - - e_attachment_cancel (attachment); - e_attachment_set_reference (attachment, NULL); - - model = gtk_tree_row_reference_get_model (reference); - path = gtk_tree_row_reference_get_path (reference); - gtk_tree_model_get_iter (model, &iter, path); - gtk_tree_path_free (path); - - gtk_list_store_remove (GTK_LIST_STORE (store), &iter); - g_hash_table_remove (hash_table, attachment); - - g_object_freeze_notify (G_OBJECT (store)); - g_object_notify (G_OBJECT (store), "num-attachments"); - g_object_notify (G_OBJECT (store), "total-size"); - g_object_thaw_notify (G_OBJECT (store)); - - return TRUE; -} - -void -e_attachment_store_remove_all (EAttachmentStore *store) -{ - GList *list, *iter; - - g_return_if_fail (E_IS_ATTACHMENT_STORE (store)); - - if (!g_hash_table_size (store->priv->attachment_index)) - return; - - g_object_freeze_notify (G_OBJECT (store)); - - list = e_attachment_store_get_attachments (store); - for (iter = list; iter; iter = iter->next) { - EAttachment *attachment = iter->data; - - e_attachment_cancel (attachment); - g_hash_table_remove (store->priv->attachment_index, iter->data); - } - - g_list_foreach (list, (GFunc) g_object_unref, NULL); - g_list_free (list); - - gtk_list_store_clear (GTK_LIST_STORE (store)); - - g_object_notify (G_OBJECT (store), "num-attachments"); - g_object_notify (G_OBJECT (store), "total-size"); - g_object_thaw_notify (G_OBJECT (store)); -} - -void -e_attachment_store_add_to_multipart (EAttachmentStore *store, - CamelMultipart *multipart, - const gchar *default_charset) -{ - GList *list, *iter; - - g_return_if_fail (E_IS_ATTACHMENT_STORE (store)); - g_return_if_fail (CAMEL_MULTIPART (multipart)); - - list = e_attachment_store_get_attachments (store); - - for (iter = list; iter != NULL; iter = iter->next) { - EAttachment *attachment = iter->data; - - /* Skip the attachment if it's still loading. */ - if (!e_attachment_get_loading (attachment)) - e_attachment_add_to_multipart ( - attachment, multipart, default_charset); - } - - g_list_foreach (list, (GFunc) g_object_unref, NULL); - g_list_free (list); -} - -GList * -e_attachment_store_get_attachments (EAttachmentStore *store) -{ - GList *list = NULL; - GtkTreeModel *model; - GtkTreeIter iter; - gboolean valid; - - g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), NULL); - - model = GTK_TREE_MODEL (store); - valid = gtk_tree_model_get_iter_first (model, &iter); - - while (valid) { - EAttachment *attachment; - gint column_id; - - column_id = E_ATTACHMENT_STORE_COLUMN_ATTACHMENT; - gtk_tree_model_get (model, &iter, column_id, &attachment, -1); - - list = g_list_prepend (list, attachment); - - valid = gtk_tree_model_iter_next (model, &iter); - } - - return g_list_reverse (list); -} - -guint -e_attachment_store_get_num_attachments (EAttachmentStore *store) -{ - g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), 0); - - return g_hash_table_size (store->priv->attachment_index); -} - -guint -e_attachment_store_get_num_loading (EAttachmentStore *store) -{ - GList *list, *iter; - guint num_loading = 0; - - g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), 0); - - list = e_attachment_store_get_attachments (store); - - for (iter = list; iter != NULL; iter = iter->next) { - EAttachment *attachment = iter->data; - - if (e_attachment_get_loading (attachment)) - num_loading++; - } - - g_list_foreach (list, (GFunc) g_object_unref, NULL); - g_list_free (list); - - return num_loading; -} - -goffset -e_attachment_store_get_total_size (EAttachmentStore *store) -{ - GList *list, *iter; - goffset total_size = 0; - - g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), 0); - - list = e_attachment_store_get_attachments (store); - - for (iter = list; iter != NULL; iter = iter->next) { - EAttachment *attachment = iter->data; - GFileInfo *file_info; - - file_info = e_attachment_get_file_info (attachment); - if (file_info != NULL) - total_size += g_file_info_get_size (file_info); - } - - g_list_foreach (list, (GFunc) g_object_unref, NULL); - g_list_free (list); - - return total_size; -} - -void -e_attachment_store_run_load_dialog (EAttachmentStore *store, - GtkWindow *parent) -{ - GtkFileChooser *file_chooser; - GtkWidget *dialog; - GtkWidget *option; - GSList *files, *iter; - const gchar *disposition; - gboolean active; - gint response; - - g_return_if_fail (E_IS_ATTACHMENT_STORE (store)); - g_return_if_fail (GTK_IS_WINDOW (parent)); - - dialog = gtk_file_chooser_dialog_new ( - _("Add Attachment"), parent, - GTK_FILE_CHOOSER_ACTION_OPEN, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - _("A_ttach"), GTK_RESPONSE_OK, NULL); - - file_chooser = GTK_FILE_CHOOSER (dialog); - gtk_file_chooser_set_local_only (file_chooser, FALSE); - gtk_file_chooser_set_select_multiple (file_chooser, TRUE); - gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); - gtk_window_set_icon_name (GTK_WINDOW (dialog), "mail-attachment"); - - option = gtk_check_button_new_with_mnemonic ( - _("_Suggest automatic display of attachment")); - gtk_file_chooser_set_extra_widget (file_chooser, option); - gtk_widget_show (option); - - response = gtk_dialog_run (GTK_DIALOG (dialog)); - - if (response != GTK_RESPONSE_OK) - goto exit; - - files = gtk_file_chooser_get_files (file_chooser); - active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (option)); - disposition = active ? "inline" : "attachment"; - - for (iter = files; iter != NULL; iter = g_slist_next (iter)) { - EAttachment *attachment; - GFile *file = iter->data; - - attachment = e_attachment_new (); - e_attachment_set_file (attachment, file); - e_attachment_set_disposition (attachment, disposition); - e_attachment_store_add_attachment (store, attachment); - e_attachment_load_async ( - attachment, (GAsyncReadyCallback) - e_attachment_load_handle_error, parent); - g_object_unref (attachment); - } - - g_slist_foreach (files, (GFunc) g_object_unref, NULL); - g_slist_free (files); - -exit: - gtk_widget_destroy (dialog); -} - -GFile * -e_attachment_store_run_save_dialog (EAttachmentStore *store, - GList *attachment_list, - GtkWindow *parent) -{ - GtkFileChooser *file_chooser; - GtkFileChooserAction action; - GtkWidget *dialog; - GFile *destination; - const gchar *title; - gint response; - guint length; - - g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), NULL); - - length = g_list_length (attachment_list); - - if (length == 0) - return NULL; - - title = ngettext ("Save Attachment", "Save Attachments", length); - - if (length == 1) - action = GTK_FILE_CHOOSER_ACTION_SAVE; - else - action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; - - dialog = gtk_file_chooser_dialog_new ( - title, parent, action, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GTK_STOCK_SAVE, GTK_RESPONSE_OK, NULL); - - file_chooser = GTK_FILE_CHOOSER (dialog); - gtk_file_chooser_set_local_only (file_chooser, FALSE); - gtk_file_chooser_set_do_overwrite_confirmation (file_chooser, TRUE); - gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); - gtk_window_set_icon_name (GTK_WINDOW (dialog), "mail-attachment"); - - if (action == GTK_FILE_CHOOSER_ACTION_SAVE) { - EAttachment *attachment; - GFileInfo *file_info; - const gchar *name = NULL; - - attachment = attachment_list->data; - file_info = e_attachment_get_file_info (attachment); - if (file_info != NULL) - name = g_file_info_get_display_name (file_info); - if (name == NULL) - /* Translators: Default attachment filename. */ - name = _("attachment.dat"); - gtk_file_chooser_set_current_name (file_chooser, name); - } - - response = gtk_dialog_run (GTK_DIALOG (dialog)); - - if (response == GTK_RESPONSE_OK) - destination = gtk_file_chooser_get_file (file_chooser); - else - destination = NULL; - - gtk_widget_destroy (dialog); - - return destination; -} - -/******************** e_attachment_store_get_uris_async() ********************/ - -typedef struct _UriContext UriContext; - -struct _UriContext { - GSimpleAsyncResult *simple; - GList *attachment_list; - GError *error; - gchar **uris; - gint index; -}; - -static UriContext * -attachment_store_uri_context_new (EAttachmentStore *store, - GList *attachment_list, - GAsyncReadyCallback callback, - gpointer user_data) -{ - UriContext *uri_context; - GSimpleAsyncResult *simple; - guint length; - gchar **uris; - - simple = g_simple_async_result_new ( - G_OBJECT (store), callback, user_data, - e_attachment_store_get_uris_async); - - /* Add one for NULL terminator. */ - length = g_list_length (attachment_list) + 1; - uris = g_malloc0 (sizeof (gchar *) * length); - - uri_context = g_slice_new0 (UriContext); - uri_context->simple = simple; - uri_context->attachment_list = g_list_copy (attachment_list); - uri_context->uris = uris; - - g_list_foreach ( - uri_context->attachment_list, - (GFunc) g_object_ref, NULL); - - return uri_context; -} - -static void -attachment_store_uri_context_free (UriContext *uri_context) -{ - g_object_unref (uri_context->simple); - - /* The attachment list should be empty now. */ - g_warn_if_fail (uri_context->attachment_list == NULL); - - /* So should the error. */ - g_warn_if_fail (uri_context->error == NULL); - - g_strfreev (uri_context->uris); - - g_slice_free (UriContext, uri_context); -} - -static void -attachment_store_get_uris_save_cb (EAttachment *attachment, - GAsyncResult *result, - UriContext *uri_context) -{ - GSimpleAsyncResult *simple; - GFile *file; - gchar **uris; - gchar *uri; - GError *error = NULL; - - file = e_attachment_save_finish (attachment, result, &error); - - /* Remove the attachment from the list. */ - uri_context->attachment_list = g_list_remove ( - uri_context->attachment_list, attachment); - g_object_unref (attachment); - - if (file != NULL) { - uri = g_file_get_uri (file); - uri_context->uris[uri_context->index++] = uri; - g_object_unref (file); - - } else if (error != NULL) { - /* If this is the first error, cancel the other jobs. */ - if (uri_context->error == NULL) { - g_propagate_error (&uri_context->error, error); - g_list_foreach ( - uri_context->attachment_list, - (GFunc) e_attachment_cancel, NULL); - error = NULL; - - /* Otherwise, we can only report back one error. So if - * this is something other than cancellation, dump it to - * the terminal. */ - } else if (!g_error_matches ( - error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("%s", error->message); - } - - if (error != NULL) - g_error_free (error); - - /* If there's still jobs running, let them finish. */ - if (uri_context->attachment_list != NULL) - return; - - /* Steal the URI list. */ - uris = uri_context->uris; - uri_context->uris = NULL; - - /* And the error. */ - error = uri_context->error; - uri_context->error = NULL; - - simple = uri_context->simple; - - if (error == NULL) - g_simple_async_result_set_op_res_gpointer (simple, uris, NULL); - else - g_simple_async_result_take_error (simple, error); - - g_simple_async_result_complete (simple); - - attachment_store_uri_context_free (uri_context); -} - -void -e_attachment_store_get_uris_async (EAttachmentStore *store, - GList *attachment_list, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GFile *temp_directory; - UriContext *uri_context; - GList *iter, *trash = NULL; - gchar *template; - gchar *path; - - g_return_if_fail (E_IS_ATTACHMENT_STORE (store)); - - uri_context = attachment_store_uri_context_new ( - store, attachment_list, callback, user_data); - - /* Grab the copied attachment list. */ - attachment_list = uri_context->attachment_list; - - /* First scan the list for attachments with a GFile. */ - for (iter = attachment_list; iter != NULL; iter = iter->next) { - EAttachment *attachment = iter->data; - GFile *file; - gchar *uri; - - file = e_attachment_get_file (attachment); - if (file == NULL) - continue; - - uri = g_file_get_uri (file); - uri_context->uris[uri_context->index++] = uri; - - /* Mark the list node for deletion. */ - trash = g_list_prepend (trash, iter); - g_object_unref (attachment); - } - - /* Expunge the list. */ - for (iter = trash; iter != NULL; iter = iter->next) { - GList *link = iter->data; - attachment_list = g_list_delete_link (attachment_list, link); - } - g_list_free (trash); - - uri_context->attachment_list = attachment_list; - - /* If we got them all then we're done. */ - if (attachment_list == NULL) { - GSimpleAsyncResult *simple; - gchar **uris; - - /* Steal the URI list. */ - uris = uri_context->uris; - uri_context->uris = NULL; - - simple = uri_context->simple; - g_simple_async_result_set_op_res_gpointer (simple, uris, NULL); - g_simple_async_result_complete (simple); - - attachment_store_uri_context_free (uri_context); - return; - } - - /* Any remaining attachments in the list should have MIME parts - * only, so we need to save them all to a temporary directory. - * We use a directory so the files can retain their basenames. - * XXX This could trigger a blocking temp directory cleanup. */ - template = g_strdup_printf (PACKAGE "-%s-XXXXXX", g_get_user_name ()); - path = e_mkdtemp (template); - g_free (template); - - /* XXX Let's hope errno got set properly. */ - if (path == NULL) { - GSimpleAsyncResult *simple; - - simple = uri_context->simple; - g_simple_async_result_set_error ( - simple, G_FILE_ERROR, - g_file_error_from_errno (errno), - "%s", g_strerror (errno)); - g_simple_async_result_complete (simple); - - attachment_store_uri_context_free (uri_context); - return; - } - - temp_directory = g_file_new_for_path (path); - - for (iter = attachment_list; iter != NULL; iter = iter->next) - e_attachment_save_async ( - E_ATTACHMENT (iter->data), - temp_directory, (GAsyncReadyCallback) - attachment_store_get_uris_save_cb, - uri_context); - - g_object_unref (temp_directory); - g_free (path); -} - -gchar ** -e_attachment_store_get_uris_finish (EAttachmentStore *store, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - gchar **uris; - - g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), NULL); - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL); - - simple = G_SIMPLE_ASYNC_RESULT (result); - uris = g_simple_async_result_get_op_res_gpointer (simple); - g_simple_async_result_propagate_error (simple, error); - - return uris; -} - -/********************** e_attachment_store_load_async() **********************/ - -typedef struct _LoadContext LoadContext; - -struct _LoadContext { - GSimpleAsyncResult *simple; - GList *attachment_list; - GError *error; -}; - -static LoadContext * -attachment_store_load_context_new (EAttachmentStore *store, - GList *attachment_list, - GAsyncReadyCallback callback, - gpointer user_data) -{ - LoadContext *load_context; - GSimpleAsyncResult *simple; - - simple = g_simple_async_result_new ( - G_OBJECT (store), callback, user_data, - e_attachment_store_load_async); - - load_context = g_slice_new0 (LoadContext); - load_context->simple = simple; - load_context->attachment_list = g_list_copy (attachment_list); - - g_list_foreach ( - load_context->attachment_list, - (GFunc) g_object_ref, NULL); - - return load_context; -} - -static void -attachment_store_load_context_free (LoadContext *load_context) -{ - g_object_unref (load_context->simple); - - /* The attachment list should be empty now. */ - g_warn_if_fail (load_context->attachment_list == NULL); - - /* So should the error. */ - g_warn_if_fail (load_context->error == NULL); - - g_slice_free (LoadContext, load_context); -} - -static void -attachment_store_load_ready_cb (EAttachment *attachment, - GAsyncResult *result, - LoadContext *load_context) -{ - GSimpleAsyncResult *simple; - GError *error = NULL; - - e_attachment_load_finish (attachment, result, &error); - - /* Remove the attachment from the list. */ - load_context->attachment_list = g_list_remove ( - load_context->attachment_list, attachment); - g_object_unref (attachment); - - if (error != NULL) { - /* If this is the first error, cancel the other jobs. */ - if (load_context->error == NULL) { - g_propagate_error (&load_context->error, error); - g_list_foreach ( - load_context->attachment_list, - (GFunc) e_attachment_cancel, NULL); - error = NULL; - - /* Otherwise, we can only report back one error. So if - * this is something other than cancellation, dump it to - * the terminal. */ - } else if (!g_error_matches ( - error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("%s", error->message); - } - - if (error != NULL) - g_error_free (error); - - /* If there's still jobs running, let them finish. */ - if (load_context->attachment_list != NULL) - return; - - /* Steal the error. */ - error = load_context->error; - load_context->error = NULL; - - simple = load_context->simple; - - if (error == NULL) - g_simple_async_result_set_op_res_gboolean (simple, TRUE); - else - g_simple_async_result_take_error (simple, error); - - g_simple_async_result_complete (simple); - - attachment_store_load_context_free (load_context); -} - -void -e_attachment_store_load_async (EAttachmentStore *store, - GList *attachment_list, - GAsyncReadyCallback callback, - gpointer user_data) -{ - LoadContext *load_context; - GList *iter; - - g_return_if_fail (E_IS_ATTACHMENT_STORE (store)); - - load_context = attachment_store_load_context_new ( - store, attachment_list, callback, user_data); - - if (attachment_list == NULL) { - GSimpleAsyncResult *simple; - - simple = load_context->simple; - g_simple_async_result_set_op_res_gboolean (simple, TRUE); - g_simple_async_result_complete (simple); - - attachment_store_load_context_free (load_context); - return; - } - - for (iter = attachment_list; iter != NULL; iter = iter->next) { - EAttachment *attachment = E_ATTACHMENT (iter->data); - - e_attachment_store_add_attachment (store, attachment); - - e_attachment_load_async ( - attachment, (GAsyncReadyCallback) - attachment_store_load_ready_cb, - load_context); - } -} - -gboolean -e_attachment_store_load_finish (EAttachmentStore *store, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - gboolean success; - - g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), FALSE); - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - success = g_simple_async_result_get_op_res_gboolean (simple); - g_simple_async_result_propagate_error (simple, error); - - return success; -} - -/********************** e_attachment_store_save_async() **********************/ - -typedef struct _SaveContext SaveContext; - -struct _SaveContext { - GSimpleAsyncResult *simple; - GFile *destination; - gchar *filename_prefix; - GFile *fresh_directory; - GFile *trash_directory; - GList *attachment_list; - GError *error; - gchar **uris; - gint index; -}; - -static SaveContext * -attachment_store_save_context_new (EAttachmentStore *store, - GFile *destination, - const gchar *filename_prefix, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SaveContext *save_context; - GSimpleAsyncResult *simple; - GList *attachment_list; - guint length; - gchar **uris; - - simple = g_simple_async_result_new ( - G_OBJECT (store), callback, user_data, - e_attachment_store_save_async); - - attachment_list = e_attachment_store_get_attachments (store); - - /* Add one for NULL terminator. */ - length = g_list_length (attachment_list) + 1; - uris = g_malloc0 (sizeof (gchar *) * length); - - save_context = g_slice_new0 (SaveContext); - save_context->simple = simple; - save_context->destination = g_object_ref (destination); - save_context->filename_prefix = g_strdup (filename_prefix); - save_context->attachment_list = attachment_list; - save_context->uris = uris; - - return save_context; -} - -static void -attachment_store_save_context_free (SaveContext *save_context) -{ - g_object_unref (save_context->simple); - - /* The attachment list should be empty now. */ - g_warn_if_fail (save_context->attachment_list == NULL); - - /* So should the error. */ - g_warn_if_fail (save_context->error == NULL); - - if (save_context->destination) { - g_object_unref (save_context->destination); - save_context->destination = NULL; - } - - g_free (save_context->filename_prefix); - save_context->filename_prefix = NULL; - - if (save_context->fresh_directory) { - g_object_unref (save_context->fresh_directory); - save_context->fresh_directory = NULL; - } - - if (save_context->trash_directory) { - g_object_unref (save_context->trash_directory); - save_context->trash_directory = NULL; - } - - g_strfreev (save_context->uris); - - g_slice_free (SaveContext, save_context); -} - -static void -attachment_store_move_file (SaveContext *save_context, - GFile *source, - GFile *destination, - GError **error) -{ - gchar *tmpl; - gchar *path; - - g_return_if_fail (save_context != NULL); - g_return_if_fail (source != NULL); - g_return_if_fail (destination != NULL); - g_return_if_fail (error != NULL); - - /* Attachments are all saved to a temporary directory. - * Now we need to move the existing destination directory - * out of the way (if it exists). Instead of testing for - * existence we'll just attempt the move and ignore any - * G_IO_ERROR_NOT_FOUND errors. */ - - /* First, however, we need another temporary directory to - * move the existing destination directory to. Note we're - * not actually creating the directory yet, just picking a - * name for it. The usual raciness with this approach - * applies here (read up on mktemp(3)), but worst case is - * we get a spurious G_IO_ERROR_WOULD_MERGE error and the - * user has to try saving attachments again. */ - tmpl = g_strdup_printf (PACKAGE "-%s-XXXXXX", g_get_user_name ()); - path = e_mktemp (tmpl); - g_free (tmpl); - - save_context->trash_directory = g_file_new_for_path (path); - g_free (path); - - /* XXX No asynchronous move operation in GIO? */ - g_file_move ( - destination, - save_context->trash_directory, - G_FILE_COPY_NONE, NULL, NULL, NULL, error); - - if (*error != NULL && !g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) - return; - - g_clear_error (error); - - /* Now we can move the file from the temporary directory - * to the user-specified destination. */ - g_file_move ( - source, - destination, - G_FILE_COPY_NONE, NULL, NULL, NULL, error); -} - -static void -attachment_store_save_cb (EAttachment *attachment, - GAsyncResult *result, - SaveContext *save_context) -{ - GSimpleAsyncResult *simple; - GFile *file; - gchar **uris; - GError *error = NULL; - - file = e_attachment_save_finish (attachment, result, &error); - - /* Remove the attachment from the list. */ - save_context->attachment_list = g_list_remove ( - save_context->attachment_list, attachment); - g_object_unref (attachment); - - if (file != NULL) { - /* Assemble the file's final URI from its basename. */ - gchar *basename; - gchar *uri; - GFile *source = NULL, *destination = NULL; - - basename = g_file_get_basename (file); - g_object_unref (file); - - source = g_file_get_child (save_context->fresh_directory, basename); - - if (save_context->filename_prefix && *save_context->filename_prefix) { - gchar *tmp = basename; - - basename = g_strconcat (save_context->filename_prefix, basename, NULL); - g_free (tmp); - } - - file = save_context->destination; - destination = g_file_get_child (file, basename); - uri = g_file_get_uri (destination); - - /* move them file-by-file */ - attachment_store_move_file (save_context, source, destination, &error); - - if (!error) - save_context->uris[save_context->index++] = uri; - - g_object_unref (source); - g_object_unref (destination); - } - - if (error != NULL) { - /* If this is the first error, cancel the other jobs. */ - if (save_context->error == NULL) { - g_propagate_error (&save_context->error, error); - g_list_foreach ( - save_context->attachment_list, - (GFunc) e_attachment_cancel, NULL); - error = NULL; - - /* Otherwise, we can only report back one error. So if - * this is something other than cancellation, dump it to - * the terminal. */ - } else if (!g_error_matches ( - error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - g_warning ("%s", error->message); - } - - g_clear_error (&error); - - /* If there's still jobs running, let them finish. */ - if (save_context->attachment_list != NULL) - return; - - /* If an error occurred while saving, we're done. */ - if (save_context->error != NULL) { - - /* Steal the error. */ - error = save_context->error; - save_context->error = NULL; - - simple = save_context->simple; - g_simple_async_result_take_error (simple, error); - g_simple_async_result_complete (simple); - - attachment_store_save_context_free (save_context); - return; - } - - if (error != NULL) { - simple = save_context->simple; - g_simple_async_result_take_error (simple, error); - g_simple_async_result_complete (simple); - - attachment_store_save_context_free (save_context); - return; - } - - /* clean-up left directory */ - g_file_delete (save_context->fresh_directory, NULL, NULL); - - /* And the URI list. */ - uris = save_context->uris; - save_context->uris = NULL; - - simple = save_context->simple; - g_simple_async_result_set_op_res_gpointer (simple, uris, NULL); - g_simple_async_result_complete (simple); - - attachment_store_save_context_free (save_context); -} -/* - * @filename_prefix: prefix to use for a file name; can be %NULL for none - **/ -void -e_attachment_store_save_async (EAttachmentStore *store, - GFile *destination, - const gchar *filename_prefix, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SaveContext *save_context; - GList *attachment_list, *iter; - GFile *temp_directory; - gchar *template; - gchar *path; - - g_return_if_fail (E_IS_ATTACHMENT_STORE (store)); - g_return_if_fail (G_IS_FILE (destination)); - - save_context = attachment_store_save_context_new ( - store, destination, filename_prefix, callback, user_data); - - attachment_list = save_context->attachment_list; - - /* Deal with an empty attachment store. The caller will get - * an empty NULL-terminated list as opposed to NULL, to help - * distinguish it from an error. */ - if (attachment_list == NULL) { - GSimpleAsyncResult *simple; - gchar **uris; - - /* Steal the URI list. */ - uris = save_context->uris; - save_context->uris = NULL; - - simple = save_context->simple; - g_simple_async_result_set_op_res_gpointer (simple, uris, NULL); - g_simple_async_result_complete (simple); - - attachment_store_save_context_free (save_context); - return; - } - - /* Save all attachments to a temporary directory, which we'll - * then move to its proper location. We use a directory so - * files can retain their basenames. - * XXX This could trigger a blocking temp directory cleanup. */ - template = g_strdup_printf (PACKAGE "-%s-XXXXXX", g_get_user_name ()); - path = e_mkdtemp (template); - g_free (template); - - /* XXX Let's hope errno got set properly. */ - if (path == NULL) { - GSimpleAsyncResult *simple; - - simple = save_context->simple; - g_simple_async_result_set_error ( - simple, G_FILE_ERROR, - g_file_error_from_errno (errno), - "%s", g_strerror (errno)); - g_simple_async_result_complete (simple); - - attachment_store_save_context_free (save_context); - return; - } - - temp_directory = g_file_new_for_path (path); - save_context->fresh_directory = temp_directory; - g_free (path); - - for (iter = attachment_list; iter != NULL; iter = iter->next) - e_attachment_save_async ( - E_ATTACHMENT (iter->data), - temp_directory, (GAsyncReadyCallback) - attachment_store_save_cb, save_context); -} - -gchar ** -e_attachment_store_save_finish (EAttachmentStore *store, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - gchar **uris; - - g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), NULL); - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), NULL); - - simple = G_SIMPLE_ASYNC_RESULT (result); - uris = g_simple_async_result_get_op_res_gpointer (simple); - g_simple_async_result_propagate_error (simple, error); - - return uris; -} |