1373 lines
36 KiB
C
1373 lines
36 KiB
C
/*
|
|
* 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"
|
|
#include "e-util/gconf-bridge.h"
|
|
|
|
struct _EAttachmentStorePrivate {
|
|
GHashTable *attachment_index;
|
|
gchar *current_folder_uri;
|
|
|
|
guint ignore_row_changed : 1;
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_CURRENT_FOLDER_URI,
|
|
PROP_NUM_ATTACHMENTS,
|
|
PROP_NUM_LOADING,
|
|
PROP_TOTAL_SIZE
|
|
};
|
|
|
|
G_DEFINE_TYPE (
|
|
EAttachmentStore,
|
|
e_attachment_store,
|
|
GTK_TYPE_LIST_STORE)
|
|
|
|
static void
|
|
attachment_store_set_property (GObject *object,
|
|
guint property_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (property_id) {
|
|
case PROP_CURRENT_FOLDER_URI:
|
|
e_attachment_store_set_current_folder_uri (
|
|
E_ATTACHMENT_STORE (object),
|
|
g_value_get_string (value));
|
|
return;
|
|
}
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
}
|
|
|
|
static void
|
|
attachment_store_get_property (GObject *object,
|
|
guint property_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
switch (property_id) {
|
|
case PROP_CURRENT_FOLDER_URI:
|
|
g_value_set_string (
|
|
value,
|
|
e_attachment_store_get_current_folder_uri (
|
|
E_ATTACHMENT_STORE (object)));
|
|
return;
|
|
|
|
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 (object)->priv;
|
|
|
|
g_hash_table_destroy (priv->attachment_index);
|
|
|
|
g_free (priv->current_folder_uri);
|
|
|
|
/* Chain up to parent's finalize() method. */
|
|
G_OBJECT_CLASS (e_attachment_store_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
attachment_store_constructed (GObject *object)
|
|
{
|
|
GConfBridge *bridge;
|
|
const gchar *key;
|
|
|
|
bridge = gconf_bridge_get ();
|
|
|
|
key = "/apps/evolution/shell/file_chooser_folder";
|
|
gconf_bridge_bind_property (bridge, key, object, "current-folder-uri");
|
|
|
|
/* Chain up to parent's constructed() method. */
|
|
G_OBJECT_CLASS (e_attachment_store_parent_class)->constructed (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->set_property = attachment_store_set_property;
|
|
object_class->get_property = attachment_store_get_property;
|
|
object_class->dispose = attachment_store_dispose;
|
|
object_class->finalize = attachment_store_finalize;
|
|
object_class->constructed = attachment_store_constructed;
|
|
|
|
g_object_class_install_property (
|
|
object_class,
|
|
PROP_CURRENT_FOLDER_URI,
|
|
g_param_spec_string (
|
|
"current-folder-uri",
|
|
"Current Folder URI",
|
|
NULL,
|
|
NULL,
|
|
G_PARAM_READWRITE |
|
|
G_PARAM_CONSTRUCT));
|
|
|
|
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 = G_TYPE_INSTANCE_GET_PRIVATE (
|
|
store, E_TYPE_ATTACHMENT_STORE, EAttachmentStorePrivate);
|
|
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);
|
|
}
|
|
|
|
const gchar *
|
|
e_attachment_store_get_current_folder_uri (EAttachmentStore *store)
|
|
{
|
|
g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), NULL);
|
|
|
|
return store->priv->current_folder_uri;
|
|
}
|
|
|
|
void
|
|
e_attachment_store_set_current_folder_uri (EAttachmentStore *store,
|
|
const gchar *current_folder_uri)
|
|
{
|
|
gchar *allocated;
|
|
|
|
g_return_if_fail (E_IS_ATTACHMENT_STORE (store));
|
|
|
|
if (current_folder_uri == NULL) {
|
|
const gchar *home_dir = g_get_home_dir ();
|
|
allocated = g_filename_to_uri (home_dir, NULL, NULL);
|
|
} else
|
|
allocated = g_strdup (current_folder_uri);
|
|
|
|
g_free (store->priv->current_folder_uri);
|
|
store->priv->current_folder_uri = allocated;
|
|
|
|
g_object_notify (G_OBJECT (store), "current-folder-uri");
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
gint
|
|
e_attachment_store_run_file_chooser_dialog (EAttachmentStore *store,
|
|
GtkWidget *dialog)
|
|
{
|
|
GtkFileChooser *file_chooser;
|
|
gint response = GTK_RESPONSE_NONE;
|
|
const gchar *current_uri;
|
|
gboolean update_folder;
|
|
|
|
g_return_val_if_fail (E_IS_ATTACHMENT_STORE (store), response);
|
|
g_return_val_if_fail (GTK_IS_FILE_CHOOSER_DIALOG (dialog), response);
|
|
|
|
file_chooser = GTK_FILE_CHOOSER (dialog);
|
|
current_uri = e_attachment_store_get_current_folder_uri (store);
|
|
gtk_file_chooser_set_current_folder_uri (file_chooser, current_uri);
|
|
|
|
response = gtk_dialog_run (GTK_DIALOG (dialog));
|
|
|
|
update_folder =
|
|
(response == GTK_RESPONSE_ACCEPT) ||
|
|
(response == GTK_RESPONSE_OK) ||
|
|
(response == GTK_RESPONSE_YES) ||
|
|
(response == GTK_RESPONSE_APPLY);
|
|
|
|
if (update_folder) {
|
|
gchar *uri;
|
|
|
|
uri = gtk_file_chooser_get_current_folder_uri (file_chooser);
|
|
e_attachment_store_set_current_folder_uri (store, uri);
|
|
g_free (uri);
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
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 = e_attachment_store_run_file_chooser_dialog (store, 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 = e_attachment_store_run_file_chooser_dialog (store, 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_set_from_error (simple, error);
|
|
g_error_free (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_set_from_error (simple, error);
|
|
g_error_free (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;
|
|
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,
|
|
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->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;
|
|
}
|
|
|
|
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_save_cb (EAttachment *attachment,
|
|
GAsyncResult *result,
|
|
SaveContext *save_context)
|
|
{
|
|
GSimpleAsyncResult *simple;
|
|
GFile *file;
|
|
gchar **uris;
|
|
gchar *template;
|
|
gchar *path;
|
|
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;
|
|
|
|
basename = g_file_get_basename (file);
|
|
g_object_unref (file);
|
|
|
|
file = save_context->destination;
|
|
file = g_file_get_child (file, basename);
|
|
uri = g_file_get_uri (file);
|
|
g_object_unref (file);
|
|
|
|
save_context->uris[save_context->index++] = uri;
|
|
|
|
} else 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);
|
|
}
|
|
|
|
if (error != NULL)
|
|
g_error_free (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_set_from_error (simple, error);
|
|
g_simple_async_result_complete (simple);
|
|
|
|
attachment_store_save_context_free (save_context);
|
|
g_error_free (error);
|
|
return;
|
|
}
|
|
|
|
/* 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. */
|
|
template = g_strdup_printf (PACKAGE "-%s-XXXXXX", g_get_user_name ());
|
|
path = e_mktemp (template);
|
|
g_free (template);
|
|
|
|
save_context->trash_directory = g_file_new_for_path (path);
|
|
g_free (path);
|
|
|
|
/* XXX No asynchronous move operation in GIO? */
|
|
g_file_move (
|
|
save_context->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)) {
|
|
|
|
simple = save_context->simple;
|
|
g_simple_async_result_set_from_error (simple, error);
|
|
g_simple_async_result_complete (simple);
|
|
|
|
attachment_store_save_context_free (save_context);
|
|
g_error_free (error);
|
|
return;
|
|
}
|
|
|
|
g_clear_error (&error);
|
|
|
|
/* Now we can move the first temporary directory containing
|
|
* the newly saved files to the user-specified destination. */
|
|
g_file_move (
|
|
save_context->fresh_directory,
|
|
save_context->destination,
|
|
G_FILE_COPY_NONE, NULL, NULL, NULL, &error);
|
|
|
|
if (error != NULL) {
|
|
simple = save_context->simple;
|
|
g_simple_async_result_set_from_error (simple, error);
|
|
g_simple_async_result_complete (simple);
|
|
|
|
attachment_store_save_context_free (save_context);
|
|
g_error_free (error);
|
|
return;
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
void
|
|
e_attachment_store_save_async (EAttachmentStore *store,
|
|
GFile *destination,
|
|
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, 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;
|
|
}
|