aboutsummaryrefslogtreecommitdiffstats
path: root/widgets/misc/e-attachment.c
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@src.gnome.org>2009-03-21 03:06:59 +0800
committerMatthew Barnes <mbarnes@src.gnome.org>2009-03-21 03:06:59 +0800
commit4cec9fc7169dc3b810321555a70cda916720867d (patch)
tree8fe739ab0d249fb35bedc572bd34b3717512283b /widgets/misc/e-attachment.c
parent7a92d9cc82b7775a0f5cb1fde233119d435a79b6 (diff)
downloadgsoc2013-evolution-4cec9fc7169dc3b810321555a70cda916720867d.tar
gsoc2013-evolution-4cec9fc7169dc3b810321555a70cda916720867d.tar.gz
gsoc2013-evolution-4cec9fc7169dc3b810321555a70cda916720867d.tar.bz2
gsoc2013-evolution-4cec9fc7169dc3b810321555a70cda916720867d.tar.lz
gsoc2013-evolution-4cec9fc7169dc3b810321555a70cda916720867d.tar.xz
gsoc2013-evolution-4cec9fc7169dc3b810321555a70cda916720867d.tar.zst
gsoc2013-evolution-4cec9fc7169dc3b810321555a70cda916720867d.zip
Saving progress on a massive attachment handling rewrite.
svn path=/branches/kill-bonobo/; revision=37465
Diffstat (limited to 'widgets/misc/e-attachment.c')
-rw-r--r--widgets/misc/e-attachment.c1584
1 files changed, 945 insertions, 639 deletions
diff --git a/widgets/misc/e-attachment.c b/widgets/misc/e-attachment.c
index 9bb6f09ade..6f9e80f02b 100644
--- a/widgets/misc/e-attachment.c
+++ b/widgets/misc/e-attachment.c
@@ -1,4 +1,5 @@
/*
+ * e-attachment.c
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -14,81 +15,275 @@
* License along with the program; if not, see <http://www.gnu.org/licenses/>
*
*
- * Authors:
- * Ettore Perazzoli <ettore@ximian.com>
- * Jeffrey Stedfast <fejj@ximian.com>
- * Srinivasa Ragavan <sragavan@novell.com>
- *
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
#include "e-attachment.h"
-#include "e-attachment-dialog.h"
-
-#ifdef G_OS_WIN32
-/* Include <windows.h> early (as the gio stuff below will
- * include it anyway, sigh) to workaround the DATADIR problem.
- * <windows.h> (and the headers it includes) stomps all over the
- * namespace like a baboon on crack, and especially the DATADIR enum
- * in objidl.h causes problems.
- */
-#undef DATADIR
-#define DATADIR crap_DATADIR
-#include <windows.h>
-#undef DATADIR
-#endif
-#include <sys/stat.h>
-#include <string.h>
#include <errno.h>
-
-#include <camel/camel.h>
-
#include <glib/gi18n.h>
-#include <glib/gstdio.h>
-
-#include <libebook/e-vcard.h>
+#include <camel/camel-iconv.h>
+#include <camel/camel-data-wrapper.h>
+#include <camel/camel-mime-message.h>
+#include <camel/camel-stream-filter.h>
+#include <camel/camel-stream-null.h>
+#include <camel/camel-stream-vfs.h>
#include "e-util/e-util.h"
-#include "e-util/e-error.h"
-#include "e-util/e-mktemp.h"
-#include "e-util/e-util-private.h"
#define E_ATTACHMENT_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_ATTACHMENT, EAttachmentPrivate))
+/* Attributes needed by EAttachmentStore, et al. */
+#define ATTACHMENT_QUERY "standard::*,preview::*,thumbnail::*"
+
struct _EAttachmentPrivate {
- gchar *filename;
- gchar *description;
+ GFile *file;
+ GFileInfo *file_info;
+ GCancellable *cancellable;
+ CamelMimePart *mime_part;
gchar *disposition;
- gchar *mime_type;
- GdkPixbuf *thumbnail;
- CamelMimePart *mime_part;
+ /* This is a reference to our row in an EAttachmentStore,
+ * serving as a means of broadcasting "row-changed" signals.
+ * If we are removed from the store, we lazily free the
+ * reference when it is found to be to be invalid. */
+ GtkTreeRowReference *reference;
};
enum {
PROP_0,
- PROP_DESCRIPTION,
PROP_DISPOSITION,
- PROP_FILENAME,
- PROP_THUMBNAIL
-};
-
-enum {
- CHANGED,
- UPDATE,
- LAST_SIGNAL
+ PROP_FILE,
+ PROP_FILE_INFO,
+ PROP_MIME_PART
};
static gpointer parent_class;
-static guint signals[LAST_SIGNAL];
+
+static void
+attachment_notify_model (EAttachment *attachment)
+{
+ GtkTreeRowReference *reference;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ reference = attachment->priv->reference;
+
+ if (reference == NULL)
+ return;
+
+ /* Free the reference if it's no longer valid.
+ * It means we've been removed from the store. */
+ if (!gtk_tree_row_reference_valid (reference)) {
+ gtk_tree_row_reference_free (reference);
+ attachment->priv->reference = NULL;
+ return;
+ }
+
+ 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_model_row_changed (model, path, &iter);
+ g_object_notify (G_OBJECT (model), "total-size");
+
+ gtk_tree_path_free (path);
+}
+
+static gchar *
+attachment_get_default_charset (void)
+{
+ GConfClient *client;
+ const gchar *key;
+ gchar *charset;
+
+ /* XXX This function doesn't really belong here. */
+
+ client = gconf_client_get_default ();
+ key = "/apps/evolution/mail/composer/charset";
+ charset = gconf_client_get_string (client, key, NULL);
+ if (charset == NULL || *charset == '\0') {
+ g_free (charset);
+ key = "/apps/evolution/mail/format/charset";
+ charset = gconf_client_get_string (client, key, NULL);
+ if (charset == NULL || *charset == '\0') {
+ g_free (charset);
+ charset = NULL;
+ }
+ }
+ g_object_unref (client);
+
+ if (charset == NULL)
+ charset = g_strdup (camel_iconv_locale_charset ());
+
+ if (charset == NULL)
+ charset = g_strdup ("us-ascii");
+
+ return charset;
+}
+
+static void
+attachment_set_file_info (EAttachment *attachment,
+ GFileInfo *file_info)
+{
+ GCancellable *cancellable;
+
+ cancellable = attachment->priv->cancellable;
+
+ /* Cancel any query operations in progress. */
+ if (!g_cancellable_is_cancelled (cancellable)) {
+ g_cancellable_cancel (cancellable);
+ g_cancellable_reset (cancellable);
+ }
+
+ if (file_info != NULL)
+ g_object_ref (file_info);
+
+ if (attachment->priv->file_info != NULL)
+ g_object_unref (attachment->priv->file_info);
+
+ attachment->priv->file_info = file_info;
+
+ g_object_notify (G_OBJECT (attachment), "file-info");
+
+ attachment_notify_model (attachment);
+}
+
+static void
+attachment_reset (EAttachment *attachment)
+{
+ GCancellable *cancellable;
+
+ cancellable = attachment->priv->cancellable;
+
+ g_object_freeze_notify (G_OBJECT (attachment));
+
+ /* Cancel any query operations in progress. */
+ if (!g_cancellable_is_cancelled (cancellable)) {
+ g_cancellable_cancel (cancellable);
+ g_cancellable_reset (cancellable);
+ }
+
+ if (attachment->priv->file != NULL) {
+ g_object_notify (G_OBJECT (attachment), "file");
+ g_object_unref (attachment->priv->file);
+ attachment->priv->file = NULL;
+ }
+
+ if (attachment->priv->mime_part != NULL) {
+ g_object_notify (G_OBJECT (attachment), "mime-part");
+ g_object_unref (attachment->priv->mime_part);
+ attachment->priv->mime_part = NULL;
+ }
+
+ attachment_set_file_info (attachment, NULL);
+
+ g_object_thaw_notify (G_OBJECT (attachment));
+}
+
+static void
+attachment_file_info_ready_cb (GFile *file,
+ GAsyncResult *result,
+ EAttachment *attachment)
+{
+ GFileInfo *file_info;
+ GError *error = NULL;
+
+ /* Even if we failed to obtain a GFileInfo, we still emit a
+ * "notify::file-info" to signal the async operation finished. */
+ file_info = g_file_query_info_finish (file, result, &error);
+ attachment_set_file_info (attachment, file_info);
+
+ if (file_info != NULL)
+ g_object_unref (file_info);
+ else {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+attachment_file_info_to_mime_part (EAttachment *attachment,
+ CamelMimePart *mime_part)
+{
+ GFileInfo *file_info;
+ const gchar *attribute;
+ const gchar *string;
+ gchar *allocated;
+
+ file_info = e_attachment_get_file_info (attachment);
+
+ if (file_info == NULL || mime_part == NULL)
+ return;
+
+ /* XXX Note that we skip "standard::size" here. */
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE;
+ string = g_file_info_get_attribute_string (file_info, attribute);
+ allocated = g_content_type_get_mime_type (string);
+ camel_mime_part_set_content_type (mime_part, allocated);
+ g_free (allocated);
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME;
+ string = g_file_info_get_attribute_string (file_info, attribute);
+ camel_mime_part_set_filename (mime_part, string);
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
+ string = g_file_info_get_attribute_string (file_info, attribute);
+ camel_mime_part_set_description (mime_part, string);
+
+ string = e_attachment_get_disposition (attachment);
+ camel_mime_part_set_disposition (mime_part, string);
+}
+
+static void
+attachment_mime_part_to_file_info (EAttachment *attachment)
+{
+ CamelContentType *content_type;
+ CamelMimePart *mime_part;
+ GFileInfo *file_info;
+ const gchar *attribute;
+ const gchar *string;
+ gchar *allocated;
+ guint64 v_uint64;
+
+ file_info = e_attachment_get_file_info (attachment);
+ mime_part = e_attachment_get_mime_part (attachment);
+
+ if (file_info == NULL || mime_part == NULL)
+ return;
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE;
+ content_type = camel_mime_part_get_content_type (mime_part);
+ allocated = camel_content_type_simple (content_type);
+ if (allocated != NULL)
+ g_file_info_set_attribute_string (
+ file_info, attribute, allocated);
+ g_free (allocated);
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME;
+ string = camel_mime_part_get_filename (mime_part);
+ if (string != NULL)
+ g_file_info_set_attribute_string (
+ file_info, attribute, string);
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
+ string = camel_mime_part_get_description (mime_part);
+ if (string != NULL)
+ g_file_info_set_attribute_string (
+ file_info, attribute, string);
+
+ attribute = G_FILE_ATTRIBUTE_STANDARD_SIZE;
+ v_uint64 = camel_mime_part_get_content_size (mime_part);
+ g_file_info_set_attribute_uint64 (file_info, attribute, v_uint64);
+
+ string = camel_mime_part_get_disposition (mime_part);
+ e_attachment_set_disposition (attachment, string);
+}
static void
attachment_set_property (GObject *object,
@@ -97,28 +292,22 @@ attachment_set_property (GObject *object,
GParamSpec *pspec)
{
switch (property_id) {
- case PROP_DESCRIPTION:
- e_attachment_set_description (
- E_ATTACHMENT (object),
- g_value_get_string (value));
- return;
-
case PROP_DISPOSITION:
e_attachment_set_disposition (
E_ATTACHMENT (object),
g_value_get_string (value));
return;
- case PROP_FILENAME:
- e_attachment_set_filename (
+ case PROP_FILE:
+ e_attachment_set_file (
E_ATTACHMENT (object),
- g_value_get_string (value));
+ g_value_get_object (value));
return;
- case PROP_THUMBNAIL:
- e_attachment_set_thumbnail (
+ case PROP_MIME_PART:
+ e_attachment_set_mime_part (
E_ATTACHMENT (object),
- g_value_get_object (value));
+ g_value_get_boxed (value));
return;
}
@@ -132,27 +321,27 @@ attachment_get_property (GObject *object,
GParamSpec *pspec)
{
switch (property_id) {
- case PROP_DESCRIPTION:
- g_value_set_string (
- value, e_attachment_get_description (
- E_ATTACHMENT (object)));
- return;
-
case PROP_DISPOSITION:
g_value_set_string (
value, e_attachment_get_disposition (
E_ATTACHMENT (object)));
return;
- case PROP_FILENAME:
- g_value_set_string (
- value, e_attachment_get_filename (
+ case PROP_FILE:
+ g_value_set_object (
+ value, e_attachment_get_file (
E_ATTACHMENT (object)));
return;
- case PROP_THUMBNAIL:
+ case PROP_FILE_INFO:
g_value_set_object (
- value, e_attachment_get_thumbnail (
+ value, e_attachment_get_file_info (
+ E_ATTACHMENT (object)));
+ return;
+
+ case PROP_MIME_PART:
+ g_value_set_boxed (
+ value, e_attachment_get_mime_part (
E_ATTACHMENT (object)));
return;
}
@@ -167,9 +356,20 @@ attachment_dispose (GObject *object)
priv = E_ATTACHMENT_GET_PRIVATE (object);
- if (priv->thumbnail != NULL) {
- g_object_unref (priv->thumbnail);
- priv->thumbnail = NULL;
+ if (priv->cancellable != NULL) {
+ g_cancellable_cancel (priv->cancellable);
+ g_object_unref (priv->cancellable);
+ priv->cancellable = NULL;
+ }
+
+ if (priv->file != NULL) {
+ g_object_unref (priv->file);
+ priv->file = NULL;
+ }
+
+ if (priv->file_info != NULL) {
+ g_object_unref (priv->file_info);
+ priv->file_info = NULL;
}
if (priv->mime_part != NULL) {
@@ -177,6 +377,10 @@ attachment_dispose (GObject *object)
priv->mime_part = NULL;
}
+ /* This accepts NULL arguments. */
+ gtk_tree_row_reference_free (priv->reference);
+ priv->reference = NULL;
+
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (parent_class)->dispose (object);
}
@@ -184,20 +388,11 @@ attachment_dispose (GObject *object)
static void
attachment_finalize (GObject *object)
{
- EAttachment *attachment = (EAttachment *) object;
-
- if (attachment->cancellable) {
- /* the operation is still running, so cancel it */
- g_cancellable_cancel (attachment->cancellable);
- attachment->cancellable = NULL;
- }
+ EAttachmentPrivate *priv;
- g_free (attachment->store_uri);
+ priv = E_ATTACHMENT_GET_PRIVATE (object);
- g_free (attachment->priv->filename);
- g_free (attachment->priv->description);
- g_free (attachment->priv->disposition);
- g_free (attachment->priv->mime_type);
+ g_free (priv->disposition);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (parent_class)->finalize (object);
@@ -219,75 +414,52 @@ attachment_class_init (EAttachmentClass *class)
g_object_class_install_property (
object_class,
- PROP_DESCRIPTION,
+ PROP_DISPOSITION,
g_param_spec_string (
- "description",
- "Description",
- NULL,
+ "disposition",
+ "Disposition",
NULL,
+ "attachment",
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (
object_class,
- PROP_DESCRIPTION,
- g_param_spec_string (
- "disposition",
- "Disposition",
- NULL,
+ PROP_FILE,
+ g_param_spec_object (
+ "file",
+ "File",
NULL,
+ G_TYPE_FILE,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT));
g_object_class_install_property (
object_class,
- PROP_DESCRIPTION,
- g_param_spec_string (
- "filename",
- "Filename",
- NULL,
+ PROP_FILE_INFO,
+ g_param_spec_object (
+ "file-info",
+ "File Info",
NULL,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT));
+ G_TYPE_FILE_INFO,
+ G_PARAM_READABLE));
g_object_class_install_property (
object_class,
- PROP_THUMBNAIL,
- g_param_spec_object (
- "thumbnail",
- "Thumbnail Image",
+ PROP_MIME_PART,
+ g_param_spec_boxed (
+ "mime-part",
+ "MIME Part",
NULL,
- GDK_TYPE_PIXBUF,
+ E_TYPE_CAMEL_OBJECT,
G_PARAM_READWRITE));
-
- signals[CHANGED] = g_signal_new (
- "changed",
- G_OBJECT_CLASS_TYPE (class),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (EAttachmentClass, changed),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
-
- signals[UPDATE] = g_signal_new (
- "update",
- G_OBJECT_CLASS_TYPE (class),
- G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (EAttachmentClass, update),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
}
static void
attachment_init (EAttachment *attachment)
{
attachment->priv = E_ATTACHMENT_GET_PRIVATE (attachment);
-
- attachment->index = -1;
- attachment->percentage = -1;
- attachment->sign = CAMEL_CIPHER_VALIDITY_SIGN_NONE;
- attachment->encrypt = CAMEL_CIPHER_VALIDITY_ENCRYPT_NONE;
+ attachment->priv->cancellable = g_cancellable_new ();
}
GType
@@ -316,711 +488,845 @@ e_attachment_get_type (void)
return type;
}
-/**
- * file_ext_is:
- * @param filename: path for file
- * @param ext: desired extension, with a dot
- * @return if filename has extension ext or not
- **/
-
-static gboolean
-file_ext_is (const char *filename, const char *ext)
+EAttachment *
+e_attachment_new (void)
{
- int i, dot = -1;
+ return g_object_new (E_TYPE_ATTACHMENT, NULL);
+}
- if (!filename || !ext)
- return FALSE;
+EAttachment *
+e_attachment_new_for_path (const gchar *path)
+{
+ EAttachment *attachment;
+ GFile *file;
- for (i = 0; filename[i]; i++) {
- if (filename [i] == '.')
- dot = i;
- }
+ g_return_val_if_fail (path != NULL, NULL);
- if (dot > 0) {
- return 0 == g_ascii_strcasecmp (filename + dot, ext);
- }
+ file = g_file_new_for_path (path);
+ attachment = g_object_new (E_TYPE_ATTACHMENT, "file", file, NULL);
+ g_object_unref (file);
- return FALSE;
+ return attachment;
}
-static char *
-attachment_guess_mime_type (const char *filename)
+EAttachment *
+e_attachment_new_for_uri (const gchar *uri)
{
- char *type;
- gchar *content = NULL;
+ EAttachment *attachment;
+ GFile *file;
- type = e_util_guess_mime_type (filename, TRUE);
+ g_return_val_if_fail (uri != NULL, NULL);
- if (type && strcmp (type, "text/directory") == 0 &&
- file_ext_is (filename, ".vcf") &&
- g_file_get_contents (filename, &content, NULL, NULL) &&
- content) {
- EVCard *vc = e_vcard_new_from_string (content);
+ file = g_file_new_for_uri (uri);
+ attachment = g_object_new (E_TYPE_ATTACHMENT, "file", file, NULL);
+ g_object_unref (file);
- if (vc) {
- g_free (type);
- g_object_unref (G_OBJECT (vc));
+ return attachment;
+}
- type = g_strdup ("text/x-vcard");
- }
+EAttachment *
+e_attachment_new_for_message (CamelMimeMessage *message)
+{
+ CamelDataWrapper *wrapper;
+ CamelMimePart *mime_part;
+ EAttachment *attachment;
+ GString *description;
+ const gchar *subject;
- }
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
- g_free (content);
+ mime_part = camel_mime_part_new ();
+ camel_mime_part_set_disposition (mime_part, "inline");
+ subject = camel_mime_message_get_subject (message);
- if (type) {
- /* Check if returned mime_type is valid */
- CamelContentType *ctype = camel_content_type_decode (type);
+ description = g_string_new (_("Attached message"));
+ if (subject != NULL)
+ g_string_append_printf (description, " - %s", subject);
+ camel_mime_part_set_description (mime_part, description->str);
+ g_string_free (description, TRUE);
- if (!ctype) {
- g_free (type);
- type = NULL;
- } else
- camel_content_type_unref (ctype);
- }
+ wrapper = CAMEL_DATA_WRAPPER (message);
+ camel_medium_set_content_object (CAMEL_MEDIUM (mime_part), wrapper);
+ camel_mime_part_set_content_type (mime_part, "message/rfc822");
- return type;
+ attachment = e_attachment_new ();
+ e_attachment_set_mime_part (attachment, mime_part);
+ camel_object_unref (mime_part);
+
+ return attachment;
}
-
-/**
- * e_attachment_new:
- * @filename: filename to attach
- * @disposition: Content-Disposition of the attachment
- * @ex: exception
- *
- * Return value: the new attachment, or %NULL on error
- **/
-EAttachment *
-e_attachment_new (const char *filename, const char *disposition, CamelException *ex)
+void
+e_attachment_add_to_multipart (EAttachment *attachment,
+ CamelMultipart *multipart,
+ const gchar *default_charset)
{
- EAttachment *new;
- CamelMimePart *part;
+ CamelContentType *content_type;
CamelDataWrapper *wrapper;
- CamelStream *stream;
- struct stat statbuf;
- gchar *mime_type;
- gchar *basename;
- CamelURL *url;
+ CamelMimePart *mime_part;
- g_return_val_if_fail (filename != NULL, NULL);
+ /* XXX EMsgComposer might be a better place for this function. */
- if (g_stat (filename, &statbuf) < 0) {
- camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
- _("Cannot attach file %s: %s"),
- filename, g_strerror (errno));
- return NULL;
- }
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ g_return_if_fail (CAMEL_IS_MULTIPART (multipart));
- /* return if it's not a regular file */
- if (!S_ISREG (statbuf.st_mode)) {
- camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
- _("Cannot attach file %s: not a regular file"),
- filename);
- return NULL;
- }
+ /* Still loading? Too bad. */
+ mime_part = e_attachment_get_mime_part (attachment);
+ if (mime_part == NULL)
+ return;
- if (!(stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0))) {
- camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
- _("Cannot attach file %s: %s"),
- filename, g_strerror (errno));
- return NULL;
- }
+ content_type = camel_mime_part_get_content_type (mime_part);
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
+
+ if (CAMEL_IS_MULTIPART (wrapper))
+ goto exit;
- if ((mime_type = attachment_guess_mime_type (filename))) {
- if (!g_ascii_strcasecmp (mime_type, "message/rfc822")) {
- wrapper = (CamelDataWrapper *) camel_mime_message_new ();
- } else {
- wrapper = camel_data_wrapper_new ();
+ /* For text content, determine the best encoding and character set. */
+ if (camel_content_type_is (content_type, "text", "*")) {
+ CamelTransferEncoding encoding;
+ CamelStreamFilter *filtered_stream;
+ CamelMimeFilterBestenc *filter;
+ CamelStream *stream;
+ const gchar *charset;
+
+ charset = camel_content_type_param (content_type, "charset");
+
+ /* Determine the best encoding by writing the MIME
+ * part to a NULL stream with a "bestenc" filter. */
+ stream = camel_stream_null_new ();
+ filtered_stream = camel_stream_filter_new_with_stream (stream);
+ filter = camel_mime_filter_bestenc_new (
+ CAMEL_BESTENC_GET_ENCODING);
+ camel_stream_filter_add (
+ filtered_stream, CAMEL_MIME_FILTER (filter));
+ camel_data_wrapper_decode_to_stream (
+ wrapper, CAMEL_STREAM (filtered_stream));
+ camel_object_unref (filtered_stream);
+ camel_object_unref (stream);
+
+ /* Retrieve the best encoding from the filter. */
+ encoding = camel_mime_filter_bestenc_get_best_encoding (
+ filter, CAMEL_BESTENC_8BIT);
+ camel_mime_part_set_encoding (mime_part, encoding);
+ camel_object_unref (filter);
+
+ if (encoding == CAMEL_TRANSFER_ENCODING_7BIT) {
+ /* The text fits within us-ascii, so this is safe.
+ * FIXME Check that this isn't iso-2022-jp? */
+ default_charset = "us-ascii";
+
+ } else if (charset == NULL && default_charset == NULL) {
+ default_charset = attachment_get_default_charset ();
+ /* FIXME Check that this fits within the
+ * default_charset and if not, find one
+ * that does and/or allow the user to
+ * specify. */
}
- camel_data_wrapper_construct_from_stream (wrapper, stream);
- camel_data_wrapper_set_mime_type (wrapper, mime_type);
- g_free (mime_type);
- } else {
- wrapper = camel_data_wrapper_new ();
- camel_data_wrapper_construct_from_stream (wrapper, stream);
- camel_data_wrapper_set_mime_type (wrapper, "application/octet-stream");
- }
+ if (charset == NULL) {
+ gchar *type;
- camel_object_unref (stream);
+ camel_content_type_set_param (
+ content_type, "charset", default_charset);
+ type = camel_content_type_format (content_type);
+ camel_mime_part_set_content_type (mime_part, type);
+ g_free (type);
+ }
- part = camel_mime_part_new ();
- camel_medium_set_content_object (CAMEL_MEDIUM (part), wrapper);
- camel_object_unref (wrapper);
+ /* Otherwise, unless it's a message/rfc822, Base64 encode it. */
+ } else if (!CAMEL_IS_MIME_MESSAGE (wrapper))
+ camel_mime_part_set_encoding (
+ mime_part, CAMEL_TRANSFER_ENCODING_BASE64);
- camel_mime_part_set_disposition (part, disposition);
- basename = g_path_get_basename (filename);
- camel_mime_part_set_filename (part, basename);
+exit:
+ camel_multipart_add_part (multipart, mime_part);
+}
-#if 0
- /* Note: Outlook 2002 is broken with respect to Content-Ids on
- non-multipart/related parts, so as an interoperability
- workaround, don't set a Content-Id on these parts. Fixes
- bug #10032 */
- /* set the Content-Id */
- content_id = camel_header_msgid_generate ();
- camel_mime_part_set_content_id (part, content_id);
- g_free (content_id);
-#endif
+const gchar *
+e_attachment_get_disposition (EAttachment *attachment)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
- new = g_object_new (E_TYPE_ATTACHMENT, "filename", basename, NULL);
- new->priv->mime_part = part;
- new->size = statbuf.st_size;
- new->guessed_type = TRUE;
- new->cancellable = NULL;
- new->is_available_local = TRUE;
+ return attachment->priv->disposition;
+}
+
+void
+e_attachment_set_disposition (EAttachment *attachment,
+ const gchar *disposition)
+{
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
- url = camel_url_new ("file://", NULL);
- camel_url_set_path (url, filename);
- new->store_uri = camel_url_to_string (url, 0);
- camel_url_free (url);
+ g_free (attachment->priv->disposition);
+ attachment->priv->disposition = g_strdup (disposition);
- return new;
+ g_object_notify (G_OBJECT (attachment), "disposition");
}
+GFile *
+e_attachment_get_file (EAttachment *attachment)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
-typedef struct {
- EAttachment *attachment;
- char *filename;
- char *uri;
- GtkWindow *parent; /* for error dialog */
-
- guint64 file_size; /* zero indicates unknown size */
- GInputStream *istream; /* read from here ... */
- GOutputStream *ostream; /* ...and write into this. */
- gboolean was_error;
+ return attachment->priv->file;
+}
+
+void
+e_attachment_set_file (EAttachment *attachment,
+ GFile *file)
+{
GCancellable *cancellable;
- void *buffer; /* read into this, not more than buffer_size bytes */
- gsize buffer_size;
-} DownloadInfo;
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
-static void
-download_info_free (DownloadInfo *download_info)
-{
- /* if there was an error, then free attachment too */
- if (download_info->was_error)
- g_object_unref (download_info->attachment);
+ g_object_freeze_notify (G_OBJECT (attachment));
+
+ if (file != NULL) {
+ g_return_if_fail (G_IS_FILE (file));
+ g_object_ref (file);
+ }
+
+ attachment_reset (attachment);
+ attachment->priv->file = file;
- if (download_info->ostream)
- g_object_unref (download_info->ostream);
+ cancellable = attachment->priv->cancellable;
- if (download_info->istream)
- g_object_unref (download_info->istream);
+ if (file != NULL)
+ g_file_query_info_async (
+ file, ATTACHMENT_QUERY,
+ G_FILE_QUERY_INFO_NONE,
+ G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback)
+ attachment_file_info_ready_cb,
+ attachment);
- if (download_info->cancellable)
- g_object_unref (download_info->cancellable);
+ g_object_notify (G_OBJECT (attachment), "file");
- g_free (download_info->filename);
- g_free (download_info->uri);
- g_free (download_info->buffer);
- g_free (download_info);
+ g_object_thaw_notify (G_OBJECT (attachment));
}
-static void
-data_ready_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+GFileInfo *
+e_attachment_get_file_info (EAttachment *attachment)
{
- DownloadInfo *download_info = (DownloadInfo *)user_data;
- GError *error = NULL;
- gssize read;
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
- g_return_if_fail (download_info != NULL);
+ return attachment->priv->file_info;
+}
- if (g_cancellable_is_cancelled (download_info->cancellable)) {
- /* finish the operation and close both streams */
- g_input_stream_read_finish (G_INPUT_STREAM (source_object), res, NULL);
+CamelMimePart *
+e_attachment_get_mime_part (EAttachment *attachment)
+{
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
- g_output_stream_close (download_info->ostream, NULL, NULL);
- g_input_stream_close (download_info->istream, NULL, NULL);
+ return attachment->priv->mime_part;
+}
- /* The only way how to get this canceled is in EAttachment's finalize method,
- and because the download_info_free free's the attachment on error,
- then do not consider cancellation as an error. */
- download_info_free (download_info);
- return;
+void
+e_attachment_set_mime_part (EAttachment *attachment,
+ CamelMimePart *mime_part)
+{
+ GFileInfo *file_info;
+
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+
+ g_object_freeze_notify (G_OBJECT (attachment));
+
+ if (mime_part != NULL) {
+ g_return_if_fail (CAMEL_IS_MIME_PART (mime_part));
+ camel_object_ref (mime_part);
}
- read = g_input_stream_read_finish (G_INPUT_STREAM (source_object), res, &error);
+ attachment_reset (attachment);
+ attachment->priv->mime_part = mime_part;
- if (!error)
- g_output_stream_write_all (download_info->ostream, download_info->buffer, read, NULL, download_info->cancellable, &error);
+ file_info = g_file_info_new ();
+ attachment_set_file_info (attachment, file_info);
+ attachment_mime_part_to_file_info (attachment);
+ g_object_unref (file_info);
- if (error) {
- download_info->was_error = error->domain != G_IO_ERROR || error->code != G_IO_ERROR_CANCELLED;
- if (download_info->was_error)
- e_error_run (download_info->parent, "mail-composer:no-attach", download_info->uri, error->message, NULL);
+ g_object_notify (G_OBJECT (attachment), "mime-part");
- g_error_free (error);
+ g_object_thaw_notify (G_OBJECT (attachment));
+}
- download_info->attachment->cancellable = NULL;
- download_info_free (download_info);
- return;
- }
+const gchar *
+e_attachment_get_content_type (EAttachment *attachment)
+{
+ GFileInfo *file_info;
+ const gchar *attribute;
- if (read == 0) {
- CamelException ex;
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
- /* done with reading */
- g_output_stream_close (download_info->ostream, NULL, NULL);
- g_input_stream_close (download_info->istream, NULL, NULL);
+ attribute = G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE;
+ file_info = e_attachment_get_file_info (attachment);
- download_info->attachment->cancellable = NULL;
+ if (file_info == NULL)
+ return NULL;
- camel_exception_init (&ex);
- e_attachment_build_remote_file (download_info->filename, download_info->attachment, &ex);
+ return g_file_info_get_attribute_string (file_info, attribute);
+}
- if (camel_exception_is_set (&ex)) {
- download_info->was_error = TRUE;
- e_error_run (download_info->parent, "mail-composer:no-attach", download_info->uri, camel_exception_get_description (&ex), NULL);
- camel_exception_clear (&ex);
- }
+const gchar *
+e_attachment_get_display_name (EAttachment *attachment)
+{
+ GFileInfo *file_info;
+ const gchar *attribute;
- download_info->attachment->percentage = -1;
- download_info->attachment->is_available_local = TRUE;
- g_signal_emit (download_info->attachment, signals[UPDATE], 0);
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
- download_info_free (download_info);
- return;
- } else if (download_info->file_size) {
- download_info->attachment->percentage = read * 100 / download_info->file_size;
- download_info->file_size -= MIN (download_info->file_size, read);
- g_signal_emit (download_info->attachment, signals[UPDATE], 0);
- } else {
- download_info->attachment->percentage = 0;
- g_signal_emit (download_info->attachment, signals[UPDATE], 0);
- }
+ attribute = G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME;
+ file_info = e_attachment_get_file_info (attachment);
- /* read next chunk */
- g_input_stream_read_async (download_info->istream, download_info->buffer, download_info->buffer_size, G_PRIORITY_DEFAULT, download_info->cancellable, data_ready_cb, download_info);
+ if (file_info == NULL)
+ return NULL;
+
+ return g_file_info_get_attribute_string (file_info, attribute);
}
-static gboolean
-download_to_local_path (DownloadInfo *download_info, CamelException *ex)
+const gchar *
+e_attachment_get_description (EAttachment *attachment)
{
- GError *error = NULL;
- GFile *src = g_file_new_for_uri (download_info->uri);
- GFile *des = g_file_new_for_path (download_info->filename);
- gboolean res = FALSE;
+ GFileInfo *file_info;
+ const gchar *attribute;
- g_return_val_if_fail (src != NULL && des != NULL, FALSE);
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
- download_info->ostream = G_OUTPUT_STREAM (g_file_replace (des, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error));
+ attribute = G_FILE_ATTRIBUTE_STANDARD_DESCRIPTION;
+ file_info = e_attachment_get_file_info (attachment);
- if (download_info->ostream && !error) {
- GFileInfo *fi;
+ if (file_info == NULL)
+ return NULL;
- fi = g_file_query_info (src, G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, NULL, NULL);
+ return g_file_info_get_attribute_string (file_info, attribute);
+}
- if (fi) {
- download_info->file_size = g_file_info_get_attribute_uint64 (fi, G_FILE_ATTRIBUTE_STANDARD_SIZE);
- g_object_unref (fi);
- } else {
- download_info->file_size = 0;
- }
+GIcon *
+e_attachment_get_icon (EAttachment *attachment)
+{
+ GFileInfo *file_info;
+ const gchar *attribute;
- download_info->istream = G_INPUT_STREAM (g_file_read (src, NULL, &error));
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
- if (download_info->istream && !error) {
- download_info->cancellable = g_cancellable_new ();
- download_info->attachment->cancellable = download_info->cancellable;
- download_info->buffer_size = 10240; /* max 10KB chunk */
- download_info->buffer = g_malloc (sizeof (char) * download_info->buffer_size);
+ attribute = G_FILE_ATTRIBUTE_STANDARD_ICON;
+ file_info = e_attachment_get_file_info (attachment);
- g_input_stream_read_async (download_info->istream, download_info->buffer, download_info->buffer_size, G_PRIORITY_DEFAULT, download_info->cancellable, data_ready_cb, download_info);
+ if (file_info == NULL)
+ return NULL;
- res = TRUE;
- }
- }
+ return (GIcon *)
+ g_file_info_get_attribute_object (file_info, attribute);
+}
+
+const gchar *
+e_attachment_get_thumbnail_path (EAttachment *attachment)
+{
+ GFileInfo *file_info;
+ const gchar *attribute;
- if (error) {
- /* propagate error */
- if (ex)
- camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, error->message);
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
- g_error_free (error);
- download_info->was_error = TRUE;
- download_info_free (download_info);
- }
+ attribute = G_FILE_ATTRIBUTE_THUMBNAIL_PATH;
+ file_info = e_attachment_get_file_info (attachment);
- g_object_unref (src);
- g_object_unref (des);
+ if (file_info == NULL)
+ return NULL;
- return res;
+ return g_file_info_get_attribute_byte_string (file_info, attribute);
}
-EAttachment *
-e_attachment_new_remote_file (GtkWindow *error_dlg_parent, const char *uri, const char *disposition, const char *path, CamelException *ex)
+guint64
+e_attachment_get_size (EAttachment *attachment)
{
- EAttachment *new;
- DownloadInfo *download_info;
- CamelURL *url;
- char *base;
- gchar *filename;
+ GFileInfo *file_info;
+ const gchar *attribute;
- g_return_val_if_fail (uri != NULL, NULL);
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), 0);
- url = camel_url_new (uri, NULL);
- base = g_path_get_basename (url->path);
- camel_url_free (url);
+ attribute = G_FILE_ATTRIBUTE_STANDARD_SIZE;
+ file_info = e_attachment_get_file_info (attachment);
- filename = g_build_filename (path, base, NULL);
+ if (file_info == NULL)
+ return 0;
- new = g_object_new (E_TYPE_ATTACHMENT, "filename", filename, NULL);
- new->size = 0;
- new->guessed_type = FALSE;
- new->cancellable = NULL;
- new->is_available_local = FALSE;
- new->percentage = 0;
+ return g_file_info_get_attribute_uint64 (file_info, attribute);
+}
- g_free (base);
+gboolean
+e_attachment_is_image (EAttachment *attachment)
+{
+ const gchar *content_type;
- download_info = g_new0 (DownloadInfo, 1);
- download_info->attachment = new;
- download_info->filename = g_strdup (filename);
- download_info->uri = g_strdup (uri);
- download_info->parent = error_dlg_parent;
- download_info->was_error = FALSE;
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
- g_free (filename);
+ content_type = e_attachment_get_content_type (attachment);
- /* it frees all on the error, so do not free it twice */
- if (!download_to_local_path (download_info, ex))
- return NULL;
+ if (content_type == NULL)
+ return FALSE;
- return new;
+ return g_content_type_is_a (content_type, "image");
}
-
-void
-e_attachment_build_remote_file (const gchar *filename,
- EAttachment *attachment,
- CamelException *ex)
+gboolean
+e_attachment_is_rfc822 (EAttachment *attachment)
{
- CamelMimePart *part;
- CamelDataWrapper *wrapper;
- CamelStream *stream;
- struct stat statbuf;
- const gchar *description;
- const gchar *disposition;
- gchar *mime_type;
- gchar *basename;
- CamelURL *url;
+ const gchar *content_type;
- g_return_if_fail (filename != NULL);
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
- if (g_stat (filename, &statbuf) == -1) {
- camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
- _("Cannot attach file %s: %s"),
- filename, g_strerror (errno));
- g_message ("Cannot attach file %s: %s\n", filename, g_strerror (errno));
- return;
- }
+ content_type = e_attachment_get_content_type (attachment);
- /* return if it's not a regular file */
- if (!S_ISREG (statbuf.st_mode)) {
- camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
- _("Cannot attach file %s: not a regular file"),
- filename);
- g_message ("Cannot attach file %s: not a regular file", filename);
- return;
- }
+ if (content_type == NULL)
+ return FALSE;
- if (!(stream = camel_stream_fs_new_with_name (filename, O_RDONLY, 0))) {
- camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
- _("Cannot attach file %s: %s"),
- filename, g_strerror (errno));
- return;
- }
+ return g_content_type_equals (content_type, "message/rfc822");
+}
- if ((mime_type = attachment_guess_mime_type (filename))) {
- if (!g_ascii_strcasecmp (mime_type, "message/rfc822")) {
- wrapper = (CamelDataWrapper *) camel_mime_message_new ();
- } else {
- wrapper = camel_data_wrapper_new ();
- }
+static void
+attachment_save_file_cb (GFile *source,
+ GAsyncResult *result,
+ EActivity *activity)
+{
+ GError *error = NULL;
- camel_data_wrapper_construct_from_stream (wrapper, stream);
- camel_data_wrapper_set_mime_type (wrapper, mime_type);
- g_free (mime_type);
- } else {
- wrapper = camel_data_wrapper_new ();
- camel_data_wrapper_construct_from_stream (wrapper, stream);
- camel_data_wrapper_set_mime_type (wrapper, "application/octet-stream");
+ if (!g_file_copy_finish (source, result, &error)) {
+ e_activity_set_error (activity, error);
+ g_error_free (error);
}
- camel_object_unref (stream);
+ e_activity_complete (activity);
+ g_object_unref (activity);
+}
- part = camel_mime_part_new ();
- camel_medium_set_content_object (CAMEL_MEDIUM (part), wrapper);
- camel_object_unref (wrapper);
+static gpointer
+attachment_save_part_thread (EActivity *activity)
+{
+ GObject *object;
+ EAttachment *attachment;
+ GCancellable *cancellable;
+ GOutputStream *output_stream;
+ EFileActivity *file_activity;
+ CamelDataWrapper *wrapper;
+ CamelMimePart *mime_part;
+ CamelStream *stream;
+ GError *error = NULL;
- disposition = e_attachment_get_disposition (attachment);
- camel_mime_part_set_disposition (part, disposition);
+ object = G_OBJECT (activity);
+ attachment = g_object_get_data (object, "attachment");
+ output_stream = g_object_get_data (object, "output-stream");
- if (e_attachment_get_filename (attachment) == NULL)
- basename = g_path_get_basename (filename);
- else
- basename = g_path_get_basename (e_attachment_get_filename (attachment));
+ /* Last chance to cancel. */
+ file_activity = E_FILE_ACTIVITY (activity);
+ cancellable = e_file_activity_get_cancellable (file_activity);
+ if (g_cancellable_set_error_if_cancelled (cancellable, &error))
+ goto exit;
- camel_mime_part_set_filename (part, filename);
+ object = g_object_ref (output_stream);
+ stream = camel_stream_vfs_new_with_stream (object);
+ mime_part = e_attachment_get_mime_part (attachment);
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part));
- description = e_attachment_get_description (attachment);
- if (description != NULL) {
- camel_mime_part_set_description (part, description);
- e_attachment_set_description (attachment, NULL);
- }
+ if (camel_data_wrapper_decode_to_stream (wrapper, stream) < 0)
+ g_set_error (
+ &error, G_IO_ERROR,
+ g_io_error_from_errno (errno),
+ g_strerror (errno));
- attachment->priv->mime_part = part;
- attachment->size = statbuf.st_size;
- attachment->guessed_type = TRUE;
+ else if (camel_stream_flush (stream) < 0)
+ g_set_error (
+ &error, G_IO_ERROR,
+ g_io_error_from_errno (errno),
+ g_strerror (errno));
- e_attachment_set_filename (attachment, basename);
+ camel_object_unref (stream);
- url = camel_url_new ("file://", NULL);
- camel_url_set_path (url, filename);
- attachment->store_uri = camel_url_to_string (url, 0);
- camel_url_free (url);
+exit:
+ if (error != NULL) {
+ e_activity_set_error (activity, error);
+ g_error_free (error);
+ }
- g_free (basename);
-}
+ e_activity_complete_in_idle (activity);
+ g_object_unref (activity);
+ return NULL;
+}
-/**
- * e_attachment_new_from_mime_part:
- * @part: a CamelMimePart
- *
- * Return value: a new EAttachment based on the mime part
- **/
-EAttachment *
-e_attachment_new_from_mime_part (CamelMimePart *part)
+static void
+attachment_save_part_cb (GFile *destination,
+ GAsyncResult *result,
+ EActivity *activity)
{
- EAttachment *new;
- const gchar *filename;
+ GFileOutputStream *output_stream;
+ GError *error = NULL;
- g_return_val_if_fail (CAMEL_IS_MIME_PART (part), NULL);
+ output_stream = g_file_replace_finish (destination, result, &error);
- filename = camel_mime_part_get_filename (part);
+ if (output_stream != NULL) {
+ g_object_set_data_full (
+ G_OBJECT (activity),
+ "output-stream", output_stream,
+ (GDestroyNotify) g_object_unref);
+ g_thread_create (
+ (GThreadFunc) attachment_save_part_thread,
+ activity, FALSE, &error);
+ }
- new = g_object_new (E_TYPE_ATTACHMENT, "filename", filename, NULL);
- camel_object_ref (part);
- new->priv->mime_part = part;
- new->guessed_type = FALSE;
- new->is_available_local = TRUE;
- new->size = camel_mime_part_get_content_size (part);
+ if (error != NULL) {
+ e_activity_set_error (activity, error);
+ e_activity_complete (activity);
+ g_object_unref (activity);
+ g_error_free (error);
+ }
- return new;
}
void
-e_attachment_edit (EAttachment *attachment,
- GtkWindow *parent)
+e_attachment_save_async (EAttachment *attachment,
+ EFileActivity *file_activity,
+ GFile *destination)
{
- GtkWidget *dialog;
+ GFileProgressCallback progress_callback;
+ GCancellable *cancellable;
+ CamelMimePart *mime_part;
+ GFile *source;
g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ g_return_if_fail (G_IS_FILE (destination));
+ g_return_if_fail (E_IS_FILE_ACTIVITY (file_activity));
+
+ /* The attachment content is either a GFile (on disk) or a
+ * CamelMimePart (in memory). Each is saved differently. */
- dialog = e_attachment_dialog_new (parent, attachment);
- gtk_dialog_run (GTK_DIALOG (dialog));
- gtk_widget_destroy (dialog);
+ source = e_attachment_get_file (attachment);
+ mime_part = e_attachment_get_mime_part (attachment);
+ g_return_if_fail (source != NULL || mime_part != NULL);
+
+ cancellable = e_file_activity_get_cancellable (file_activity);
+ progress_callback = e_file_activity_progress;
+
+ /* GFile is the easier, but probably less common case. The
+ * attachment already references an on-disk file, so we can
+ * just use GIO to copy it asynchronously.
+ *
+ * We use G_FILE_COPY_OVERWRITE because the user should have
+ * already confirmed the overwrite through the save dialog. */
+ if (G_IS_FILE (source))
+ g_file_copy_async (
+ source, destination,
+ G_FILE_COPY_OVERWRITE,
+ G_PRIORITY_DEFAULT, cancellable,
+ progress_callback, file_activity,
+ (GAsyncReadyCallback) attachment_save_file_cb,
+ g_object_ref (file_activity));
+
+ /* CamelMimePart can only be decoded to a file synchronously, so
+ * we do this in two stages. Stage one asynchronously opens the
+ * destination file for writing. Stage two spawns a thread that
+ * decodes the MIME part to the destination file. This stage is
+ * not cancellable, unfortunately. */
+ else if (CAMEL_IS_MIME_PART (mime_part)) {
+ g_object_set_data_full (
+ G_OBJECT (file_activity),
+ "attachment", g_object_ref (attachment),
+ (GDestroyNotify) g_object_unref);
+ g_file_replace_async (
+ destination, NULL, FALSE,
+ G_FILE_CREATE_REPLACE_DESTINATION,
+ G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) attachment_save_part_cb,
+ g_object_ref (file_activity));
+ }
}
-const gchar *
-e_attachment_get_description (EAttachment *attachment)
+#if 0
+typedef struct {
+ gint io_priority;
+ GCancellable *cancellable;
+ GSimpleAsyncResult *simple;
+ GFileInfo *file_info;
+} BuildMimePartData;
+
+static BuildMimePartData *
+attachment_build_mime_part_data_new (EAttachment *attachment,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data,
+ gpointer source_tag)
{
- CamelMimePart *mime_part;
- const gchar *description;
+ BuildMimePartData *data;
+ GSimpleAsyncResult *simple;
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+ simple = g_simple_async_result_new (
+ G_OBJECT (attachment), callback, user_data, source_tag);
- mime_part = e_attachment_get_mime_part (attachment);
- if (mime_part != NULL)
- description = camel_mime_part_get_description (mime_part);
- else
- description = attachment->priv->description;
+ if (G_IS_CANCELLABLE (cancellable))
+ g_object_ref (cancellable);
- return description;
+ data = g_slice_new0 (BuildMimePartData);
+ data->io_priority = io_priority;
+ data->cancellable = cancellable;
+ data->simple = simple;
+ return data;
}
-void
-e_attachment_set_description (EAttachment *attachment,
- const gchar *description)
+static void
+attachment_build_mime_part_data_free (BuildMimePartData *data)
{
- CamelMimePart *mime_part;
+ if (data->attachment != NULL)
+ g_object_unref (data->attachment);
- g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ if (data->cancellable != NULL)
+ g_object_unref (data->cancellable);
- g_free (attachment->priv->description);
- attachment->priv->description = g_strdup (description);
+ if (data->simple != NULL)
+ g_object_unref (data->simple);
- mime_part = e_attachment_get_mime_part (attachment);
- if (mime_part != NULL)
- camel_mime_part_set_description (mime_part, description);
+ if (data->file_info != NULL)
+ g_object_unref (data->file_info);
- g_object_notify (G_OBJECT (attachment), "description");
+ g_slice_free (BuildMimePartData, data);
}
-const gchar *
-e_attachment_get_disposition (EAttachment *attachment)
+static void
+attachment_build_mime_part_splice_cb (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
{
+ GSimpleAsyncResult *final_result;
+ GCancellable *cancellable;
+ EAttachment *attachment;
+ CamelDataWrapper *wrapper;
CamelMimePart *mime_part;
- const gchar *disposition;
+ CamelStream *stream;
+ const gchar *content_type;
+ gchar *mime_type;
+ gssize length;
+ gpointer data;
+ GError *error = NULL;
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+ final_result = G_SIMPLE_ASYNC_RESULT (user_data);
- mime_part = e_attachment_get_mime_part (attachment);
- if (mime_part != NULL)
- disposition = camel_mime_part_get_disposition (mime_part);
- else
- disposition = attachment->priv->disposition;
+ cancellable = g_cancellable_get_current ();
+ g_cancellable_pop_current (cancellable);
+ g_object_unref (cancellable);
- return disposition;
-}
+ length = g_output_stream_splice_finish (
+ G_OUTPUT_STREAM (source), result, &error);
+ if (error != NULL)
+ goto fail;
-void
-e_attachment_set_disposition (EAttachment *attachment,
- const gchar *disposition)
-{
- CamelMimePart *mime_part;
+ data = g_memory_output_stream_get_data (
+ G_MEMORY_OUTPUT_STREAM (source));
- g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ attachment = E_ATTACHMENT (
+ g_async_result_get_source_object (
+ G_ASYNC_RESULT (final_result)));
- g_free (attachment->priv->disposition);
- attachment->priv->disposition = g_strdup (disposition);
+ if (e_attachment_is_rfc822 (attachment))
+ wrapper = (CamelDataWrapper *) camel_mime_message_new ();
+ else
+ wrapper = camel_data_wrapper_new ();
- mime_part = e_attachment_get_mime_part (attachment);
- if (mime_part != NULL)
- camel_mime_part_set_disposition (mime_part, disposition);
+ content_type = e_attachment_get_content_type (attachment);
+ mime_type = g_content_type_get_mime_type (content_type);
- g_object_notify (G_OBJECT (attachment), "disposition");
-}
+ stream = camel_stream_mem_new_with_buffer (data, length);
+ camel_data_wrapper_construct_from_stream (wrapper, stream);
+ camel_data_wrapper_set_mime_type (wrapper, mime_type);
+ camel_object_unref (stream);
-const gchar *
-e_attachment_get_filename (EAttachment *attachment)
-{
- CamelMimePart *mime_part;
- const gchar *filename;
+ mime_part = camel_mime_part_new ();
+ camel_medium_set_content_object (CAMEL_MEDIUM (mime_part), wrapper);
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+ g_simple_async_result_set_op_res_gpointer (
+ final_result, mime_part, camel_object_unref);
- mime_part = e_attachment_get_mime_part (attachment);
- if (mime_part != NULL)
- filename = camel_mime_part_get_filename (mime_part);
- else
- filename = attachment->priv->filename;
+ g_simple_async_result_complete (final_result);
+
+ camel_object_unref (wrapper);
+ g_free (mime_type);
+
+ return;
- return filename;
+fail:
+ g_simple_async_result_set_from_error (final_result, error);
+ g_simple_async_result_complete (final_result);
+ g_error_free (error);
}
-void
-e_attachment_set_filename (EAttachment *attachment,
- const gchar *filename)
+static void
+attachment_build_mime_part_read_cb (GObject *source,
+ GAsyncResult *result,
+ BuildMimePartData *data)
{
- CamelMimePart *mime_part;
+ GFileInputStream *input_stream;
+ GOutputStream *output_stream;
+ GCancellable *cancellable;
+ GError *error = NULL;
- g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ input_stream = g_file_read_finish (G_FILE (source), result, &error);
+ if (error != NULL)
+ goto fail;
- g_free (attachment->priv->filename);
- attachment->priv->filename = g_strdup (filename);
+ output_stream = g_memory_output_stream_new (
+ NULL, 0, g_realloc, g_free);
- mime_part = e_attachment_get_mime_part (attachment);
- if (mime_part != NULL)
- camel_mime_part_set_filename (mime_part, filename);
+ g_output_stream_splice_async (
+ output_stream, G_INPUT_STREAM (input_stream),
+ G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
+ G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
+ G_PRIORITY_DEFAULT, cancellable,
+ attachment_build_mime_part_splice_cb, result);
- g_object_notify (G_OBJECT (attachment), "filename");
-}
+ g_cancellable_push_current (cancellable);
-CamelMimePart *
-e_attachment_get_mime_part (EAttachment *attachment)
-{
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+ g_object_unref (input_stream);
+ g_object_unref (output_stream);
- return attachment->priv->mime_part;
+ return;
+
+fail:
+ g_simple_async_result_set_from_error (final_result, error);
+ g_simple_async_result_complete (final_result);
+ g_error_free (error);
}
-const gchar *
-e_attachment_get_mime_type (EAttachment *attachment)
+static gboolean
+attachment_build_mime_part_idle_cb (BuildMimePartData *data)
{
- CamelContentType *content_type;
- CamelMimePart *mime_part;
- const gchar *filename;
- gchar *mime_type;
+ GObject *source;
+ GAsyncResult *result;
+ GFileInfo *file_info;
+ GFile *file;
+ GError *error = NULL;
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+ if (g_cancellable_set_error_if_cancelled (data->cancellable, &error))
+ goto cancelled;
- if (attachment->priv->mime_type != NULL)
- goto exit;
+ result = G_ASYNC_RESULT (data->simple);
+ source = g_async_result_get_source_object (result);
+ file_info = e_attachment_get_file_info (E_ATTACHMENT (source));
- mime_part = e_attachment_get_mime_part (attachment);
- filename = e_attachment_get_filename (attachment);
- content_type = camel_mime_part_get_content_type (mime_part);
+ /* Poll again on the next idle. */
+ if (!G_IS_FILE_INFO (file_info))
+ return TRUE;
- if (mime_part == NULL)
- mime_type = attachment_guess_mime_type (filename);
- else {
- content_type = camel_mime_part_get_content_type (mime_part);
- mime_type = camel_content_type_simple (content_type);
- }
+ /* We have a GFileInfo, so on to step 2. */
- attachment->priv->mime_type = mime_type;
+ data->file_info = g_file_info_dup (file_info);
+ file = e_attachment_get_file (E_ATTACHMENT (source));
-exit:
- return attachment->priv->mime_type;
-}
+ /* Because Camel's stream API is synchronous and not
+ * cancellable, we have to asynchronously read the file
+ * into memory and then encode it to a MIME part. That
+ * means double buffering the file contents in memory,
+ * unfortunately. */
+ g_file_read_async (
+ file, data->io_priority, data->cancellable,
+ attachment_build_mime_part_read_cb, data);
-GdkPixbuf *
-e_attachment_get_thumbnail (EAttachment *attachment)
-{
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+ return FALSE;
+
+cancelled:
+ g_simple_async_result_set_op_res_gboolean (data->simple, FALSE);
+ g_simple_async_result_set_from_error (data->simple, error);
+ g_simple_async_result_complete (data->simple);
- return attachment->priv->thumbnail;
+ build_mime_part_data_free (data);
+ g_error_free (error);
+
+ return FALSE;
}
void
-e_attachment_set_thumbnail (EAttachment *attachment,
- GdkPixbuf *thumbnail)
+e_attachment_build_mime_part_async (EAttachment *attachment,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ CamelMimePart *mime_part;
+ GSimpleAsyncResult *result;
+ GFile *file;
+
g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ g_return_if_fail (callback != NULL);
- if (thumbnail != NULL) {
- g_return_if_fail (GDK_IS_PIXBUF (thumbnail));
- g_object_ref (thumbnail);
+ file = e_attachment_get_file (attachment);
+ mime_part = e_attachment_get_mime_part (attachment);
+ g_return_if_fail (file != NULL || mime_part != NULL);
+
+ result = g_simple_async_result_new (
+ G_OBJECT (attachment), callback, user_data,
+ e_attachment_build_mime_part_async);
+
+ /* First try the easy way out. */
+ if (CAMEL_IS_MIME_PART (mime_part)) {
+ camel_object_ref (mime_part);
+ g_simple_async_result_set_op_res_gpointer (
+ result, mime_part, camel_object_unref);
+ g_simple_async_result_complete_in_idle (result);
+ return;
}
- if (attachment->priv->thumbnail != NULL)
- g_object_unref (attachment->priv->thumbnail);
+ /* XXX g_cancellable_push_current() documentation lies.
+ * The function rejects NULL pointers, so create a
+ * dummy GCancellable if necessary. */
+ if (cancellable == NULL)
+ cancellable = g_cancellable_new ();
+ else
+ g_object_ref (cancellable);
- attachment->priv->thumbnail = thumbnail;
+ /* Because Camel's stream API is synchronous and not
+ * cancellable, we have to asynchronously read the file
+ * into memory and then encode it to a MIME part. That
+ * means it's double buffered, unfortunately. */
+ g_file_read_async (
+ file, G_PRIORITY_DEFAULT, cancellable,
+ attachment_build_mime_part_read_cb, result);
- g_object_notify (G_OBJECT (attachment), "thumbnail");
+ g_cancellable_push_current (cancellable);
}
-gboolean
-e_attachment_is_image (EAttachment *attachment)
+CamelMimePart *
+e_attachment_build_mime_part_finish (EAttachment *attachment,
+ GAsyncResult *result,
+ GError **error)
{
- CamelContentType *content_type;
CamelMimePart *mime_part;
+ GSimpleAsyncResult *simple_result;
+ gboolean async_result_is_valid;
+ gpointer source_tag;
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+ g_return_val_if_fail (E_IS_ATTACHMENT (attachment), NULL);
+ g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
- mime_part = e_attachment_get_mime_part (attachment);
- if (mime_part == NULL)
- return FALSE;
+ source_tag = e_attachment_build_mime_part_async;
+ async_result_is_valid = g_simple_async_result_is_valid (
+ result, G_OBJECT (attachment), source_tag);
+ g_return_val_if_fail (async_result_is_valid, NULL);
- content_type = camel_mime_part_get_content_type (mime_part);
+ simple_result = G_SIMPLE_ASYNC_RESULT (result);
+ g_simple_async_result_propagate_error (simple_result, error);
+ mime_part = g_simple_async_result_get_op_res_gpointer (simple_result);
+ attachment_file_info_to_mime_part (attachment, mime_part);
- return camel_content_type_is (content_type, "image", "*");
-}
+ if (CAMEL_IS_MIME_PART (mime_part))
+ camel_object_ref (mime_part);
-gboolean
-e_attachment_is_inline (EAttachment *attachment)
-{
- const gchar *disposition;
+ g_object_unref (result);
- g_return_val_if_fail (E_IS_ATTACHMENT (attachment), FALSE);
+ return mime_part;
+}
+#endif
- disposition = e_attachment_get_disposition (attachment);
- g_return_val_if_fail (disposition != NULL, FALSE);
+void
+_e_attachment_set_reference (EAttachment *attachment,
+ GtkTreeRowReference *reference)
+{
+ g_return_if_fail (E_IS_ATTACHMENT (attachment));
+ g_return_if_fail (reference != NULL);
- return (g_ascii_strcasecmp (disposition, "inline") == 0);
+ gtk_tree_row_reference_free (attachment->priv->reference);
+ attachment->priv->reference = gtk_tree_row_reference_copy (reference);
}