aboutsummaryrefslogtreecommitdiffstats
path: root/composer
diff options
context:
space:
mode:
Diffstat (limited to 'composer')
-rw-r--r--composer/Makefile.am14
-rw-r--r--composer/e-composer-actions.c76
-rw-r--r--composer/e-composer-activity.c156
-rw-r--r--composer/e-composer-activity.h66
-rw-r--r--composer/e-composer-private.c42
-rw-r--r--composer/e-composer-private.h43
-rw-r--r--composer/e-msg-composer.c2036
-rw-r--r--composer/e-msg-composer.h70
-rw-r--r--composer/mail-composer.error.xml33
9 files changed, 1671 insertions, 865 deletions
diff --git a/composer/Makefile.am b/composer/Makefile.am
index 5caea41ee7..5cd2eab05c 100644
--- a/composer/Makefile.am
+++ b/composer/Makefile.am
@@ -9,15 +9,16 @@ privsolib_LTLIBRARIES = libcomposer.la
libcomposerincludedir = $(privincludedir)/composer
libcomposerinclude_HEADERS = \
- e-composer-header.h \
- e-composer-header-table.h \
+ e-composer-actions.h \
+ e-composer-activity.h \
+ e-composer-common.h \
e-composer-from-header.h \
+ e-composer-header-table.h \
+ e-composer-header.h \
e-composer-name-header.h \
e-composer-post-header.h \
e-composer-private.h \
e-composer-text-header.h \
- e-composer-common.h \
- e-composer-actions.h \
e-msg-composer.h
libcomposer_la_CPPFLAGS = \
@@ -42,9 +43,10 @@ libcomposer_la_CPPFLAGS = \
libcomposer_la_SOURCES = \
$(libcomposerinclude_HEADERS) \
e-composer-actions.c \
- e-composer-header.c \
- e-composer-header-table.c \
+ e-composer-activity.c \
e-composer-from-header.c \
+ e-composer-header-table.c \
+ e-composer-header.c \
e-composer-name-header.c \
e-composer-post-header.c \
e-composer-private.c \
diff --git a/composer/e-composer-actions.c b/composer/e-composer-actions.c
index a49567cea2..13fe48495e 100644
--- a/composer/e-composer-actions.c
+++ b/composer/e-composer-actions.c
@@ -130,8 +130,8 @@ action_save_cb (GtkAction *action,
if (response != GTK_RESPONSE_OK)
return;
} else {
- e_alert_run_dialog_for_args (
- GTK_WINDOW (composer),
+ e_alert_submit (
+ GTK_WIDGET (composer),
E_ALERT_NO_SAVE_FILE, filename,
g_strerror (errno_saved), NULL);
return;
@@ -140,8 +140,8 @@ action_save_cb (GtkAction *action,
close (fd);
if (!gtkhtml_editor_save (editor, filename, TRUE, &error)) {
- e_alert_run_dialog_for_args (
- GTK_WINDOW (composer),
+ e_alert_submit (
+ GTK_WIDGET (composer),
E_ALERT_NO_SAVE_FILE,
filename, error->message, NULL);
g_error_free (error);
@@ -252,20 +252,6 @@ static GtkActionEntry entries[] = {
N_("Close the current file"),
G_CALLBACK (action_close_cb) },
- { "print",
- GTK_STOCK_PRINT,
- N_("_Print..."),
- "<Control>p",
- NULL,
- G_CALLBACK (action_print_cb) },
-
- { "print-preview",
- GTK_STOCK_PRINT_PREVIEW,
- N_("Print Pre_view"),
- "<Shift><Control>p",
- NULL,
- G_CALLBACK (action_print_preview_cb) },
-
{ "save",
GTK_STOCK_SAVE,
N_("_Save"),
@@ -280,20 +266,6 @@ static GtkActionEntry entries[] = {
N_("Save the current file with a different name"),
G_CALLBACK (action_save_as_cb) },
- { "save-draft",
- GTK_STOCK_SAVE,
- N_("Save as _Draft"),
- "<Control>s",
- N_("Save as draft"),
- G_CALLBACK (action_save_draft_cb) },
-
- { "send",
- "mail-send",
- N_("S_end"),
- "<Control>Return",
- N_("Send this message"),
- G_CALLBACK (action_send_cb) },
-
{ "new-message",
"mail-message-new",
N_("New _Message"),
@@ -318,6 +290,37 @@ static GtkActionEntry entries[] = {
NULL }
};
+static GtkActionEntry async_entries[] = {
+
+ { "print",
+ GTK_STOCK_PRINT,
+ N_("_Print..."),
+ "<Control>p",
+ NULL,
+ G_CALLBACK (action_print_cb) },
+
+ { "print-preview",
+ GTK_STOCK_PRINT_PREVIEW,
+ N_("Print Pre_view"),
+ "<Shift><Control>p",
+ NULL,
+ G_CALLBACK (action_print_preview_cb) },
+
+ { "save-draft",
+ GTK_STOCK_SAVE,
+ N_("Save as _Draft"),
+ "<Control>s",
+ N_("Save as draft"),
+ G_CALLBACK (action_save_draft_cb) },
+
+ { "send",
+ "mail-send",
+ N_("S_end"),
+ "<Control>Return",
+ N_("Send this message"),
+ G_CALLBACK (action_send_cb) },
+};
+
static GtkToggleActionEntry toggle_entries[] = {
{ "pgp-encrypt",
@@ -416,6 +419,15 @@ e_composer_actions_init (EMsgComposer *composer)
G_N_ELEMENTS (toggle_entries), composer);
gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+ /* Asynchronous Actions */
+ action_group = composer->priv->async_actions;
+ gtk_action_group_set_translation_domain (
+ action_group, GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (
+ action_group, async_entries,
+ G_N_ELEMENTS (async_entries), composer);
+ gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+
/* Character Set Actions */
action_group = composer->priv->charset_actions;
gtk_action_group_set_translation_domain (
diff --git a/composer/e-composer-activity.c b/composer/e-composer-activity.c
new file mode 100644
index 0000000000..d565bbc1fb
--- /dev/null
+++ b/composer/e-composer-activity.c
@@ -0,0 +1,156 @@
+/*
+ * e-composer-activity.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/>
+ *
+ */
+
+#include "e-composer-private.h"
+
+#define E_COMPOSER_ACTIVITY_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), E_TYPE_COMPOSER_ACTIVITY, EComposerActivityPrivate))
+
+struct _EComposerActivityPrivate {
+ EMsgComposer *composer;
+};
+
+enum {
+ PROP_0,
+ PROP_COMPOSER
+};
+
+G_DEFINE_TYPE (
+ EComposerActivity,
+ e_composer_activity,
+ E_TYPE_ACTIVITY)
+
+static void
+composer_activity_set_sensitive (EMsgComposer *composer,
+ gboolean sensitive)
+{
+ GtkActionGroup *action_group;
+
+ action_group = composer->priv->async_actions;
+ gtk_action_group_set_sensitive (action_group, sensitive);
+}
+
+static void
+composer_activity_set_composer (EComposerActivity *activity,
+ EMsgComposer *composer)
+{
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+ g_return_if_fail (activity->priv->composer == NULL);
+
+ activity->priv->composer = g_object_ref (composer);
+
+ composer_activity_set_sensitive (composer, FALSE);
+}
+
+static void
+composer_activity_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_COMPOSER:
+ composer_activity_set_composer (
+ E_COMPOSER_ACTIVITY (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+composer_activity_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_COMPOSER:
+ g_value_set_object (
+ value, e_composer_activity_get_composer (
+ E_COMPOSER_ACTIVITY (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+composer_activity_dispose (GObject *object)
+{
+ EComposerActivityPrivate *priv;
+
+ priv = E_COMPOSER_ACTIVITY_GET_PRIVATE (object);
+
+ if (priv->composer != NULL) {
+ composer_activity_set_sensitive (priv->composer, TRUE);
+ g_object_unref (priv->composer);
+ priv->composer = NULL;
+ }
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_composer_activity_parent_class)->dispose (object);
+}
+
+static void
+e_composer_activity_class_init (EComposerActivityClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (EComposerActivityPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = composer_activity_set_property;
+ object_class->get_property = composer_activity_get_property;
+ object_class->dispose = composer_activity_dispose;
+
+ g_object_class_install_property (
+ object_class,
+ PROP_COMPOSER,
+ g_param_spec_object (
+ "composer",
+ NULL,
+ NULL,
+ E_TYPE_MSG_COMPOSER,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+e_composer_activity_init (EComposerActivity *activity)
+{
+ activity->priv = E_COMPOSER_ACTIVITY_GET_PRIVATE (activity);
+}
+
+EActivity *
+e_composer_activity_new (EMsgComposer *composer)
+{
+ return g_object_new (
+ E_TYPE_COMPOSER_ACTIVITY,
+ "composer", composer, NULL);
+}
+
+EMsgComposer *
+e_composer_activity_get_composer (EComposerActivity *activity)
+{
+ g_return_val_if_fail (E_IS_COMPOSER_ACTIVITY (activity), NULL);
+
+ return activity->priv->composer;
+}
diff --git a/composer/e-composer-activity.h b/composer/e-composer-activity.h
new file mode 100644
index 0000000000..431e390062
--- /dev/null
+++ b/composer/e-composer-activity.h
@@ -0,0 +1,66 @@
+/*
+ * e-composer-activity.h
+ *
+ * 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/>
+ *
+ */
+
+#ifndef E_COMPOSER_ACTIVITY_H
+#define E_COMPOSER_ACTIVITY_H
+
+#include <e-util/e-activity.h>
+#include <composer/e-msg-composer.h>
+
+/* Standard GObject macros */
+#define E_TYPE_COMPOSER_ACTIVITY \
+ (e_composer_activity_get_type ())
+#define E_COMPOSER_ACTIVITY(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_COMPOSER_ACTIVITY, EComposerActivity))
+#define E_COMPOSER_ACTIVITY_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_COMPOSER_ACTIVITY, EComposerActivityClass))
+#define E_IS_COMPOSER_ACTIVITY(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_COMPOSER_ACTIVITY))
+#define E_IS_COMPOSER_ACTIVITY_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_COMPOSER_ACTIVITY))
+#define E_COMPOSER_ACTIVITY_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_COMPOSER_ACTIVITY, EComposerActivityClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EComposerActivity EComposerActivity;
+typedef struct _EComposerActivityClass EComposerActivityClass;
+typedef struct _EComposerActivityPrivate EComposerActivityPrivate;
+
+struct _EComposerActivity {
+ EActivity parent;
+ EComposerActivityPrivate *priv;
+};
+
+struct _EComposerActivityClass {
+ EActivityClass parent_class;
+};
+
+GType e_composer_activity_get_type (void);
+EActivity * e_composer_activity_new (EMsgComposer *composer);
+EMsgComposer * e_composer_activity_get_composer
+ (EComposerActivity *activity);
+
+G_END_DECLS
+
+#endif /* E_COMPOSER_ACTIVITY_H */
diff --git a/composer/e-composer-private.c b/composer/e-composer-private.c
index 01ffda6501..d96832d887 100644
--- a/composer/e-composer-private.c
+++ b/composer/e-composer-private.c
@@ -176,6 +176,7 @@ e_composer_private_constructed (EMsgComposer *composer)
priv->window_group = gtk_window_group_new ();
gtk_window_group_add_window (priv->window_group, window);
+ priv->async_actions = gtk_action_group_new ("async");
priv->charset_actions = gtk_action_group_new ("charset");
priv->composer_actions = gtk_action_group_new ("composer");
@@ -253,17 +254,31 @@ e_composer_private_constructed (EMsgComposer *composer)
priv->focus_tracker = focus_tracker;
- /* Construct the header table. */
-
container = editor->vbox;
+ /* Construct the activity bar. */
+
+ widget = e_activity_bar_new ();
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ priv->activity_bar = g_object_ref (widget);
+ /* EActivityBar controls its own visibility. */
+
+ /* Construct the alert bar for errors. */
+
+ widget = e_alert_bar_new ();
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
+ priv->alert_bar = g_object_ref (widget);
+ /* EAlertBar controls its own visibility. */
+
+ /* Construct the header table. */
+
widget = e_composer_header_table_new (shell);
gtk_container_set_border_width (GTK_CONTAINER (widget), 6);
- gtk_box_pack_start (GTK_BOX (editor->vbox), widget, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
if (small_screen_mode)
- gtk_box_reorder_child (GTK_BOX (editor->vbox), widget, 1);
+ gtk_box_reorder_child (GTK_BOX (container), widget, 1);
else
- gtk_box_reorder_child (GTK_BOX (editor->vbox), widget, 2);
+ gtk_box_reorder_child (GTK_BOX (container), widget, 2);
priv->header_table = g_object_ref (widget);
gtk_widget_show (widget);
@@ -399,8 +414,6 @@ e_composer_private_constructed (EMsgComposer *composer)
g_signal_connect (
html, "url-requested",
G_CALLBACK (msg_composer_url_requested_cb), composer);
-
- priv->mail_sent = FALSE;
}
void
@@ -436,6 +449,16 @@ e_composer_private_dispose (EMsgComposer *composer)
composer->priv->header_table = NULL;
}
+ if (composer->priv->activity_bar != NULL) {
+ g_object_unref (composer->priv->activity_bar);
+ composer->priv->activity_bar = NULL;
+ }
+
+ if (composer->priv->alert_bar != NULL) {
+ g_object_unref (composer->priv->alert_bar);
+ composer->priv->alert_bar = NULL;
+ }
+
if (composer->priv->attachment_paned != NULL) {
g_object_unref (composer->priv->attachment_paned);
composer->priv->attachment_paned = NULL;
@@ -451,6 +474,11 @@ e_composer_private_dispose (EMsgComposer *composer)
composer->priv->window_group = NULL;
}
+ if (composer->priv->async_actions != NULL) {
+ g_object_unref (composer->priv->async_actions);
+ composer->priv->async_actions = NULL;
+ }
+
if (composer->priv->charset_actions != NULL) {
g_object_unref (composer->priv->charset_actions);
composer->priv->charset_actions = NULL;
diff --git a/composer/e-composer-private.h b/composer/e-composer-private.h
index 026ed96954..1e9130b196 100644
--- a/composer/e-composer-private.h
+++ b/composer/e-composer-private.h
@@ -24,20 +24,44 @@
#include <errno.h>
-#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
#include <glib/gstdio.h>
+#include <glib/gi18n-lib.h>
+
+#include <gconf/gconf.h>
+#include <gconf/gconf-client.h>
#include "e-composer-actions.h"
+#include "e-composer-activity.h"
#include "e-composer-header-table.h"
+#include "e-util/e-alert-sink.h"
#include "e-util/e-binding.h"
#include "e-util/e-charset.h"
+#include "e-util/e-extensible.h"
+#include "e-util/e-marshal.h"
#include "e-util/e-mktemp.h"
+#include "e-util/e-plugin-ui.h"
#include "e-util/e-selection.h"
#include "e-util/e-util.h"
#include "e-util/gconf-bridge.h"
+#include "widgets/misc/e-activity-bar.h"
+#include "widgets/misc/e-alert-bar.h"
+#include "widgets/misc/e-attachment.h"
#include "widgets/misc/e-attachment-icon-view.h"
#include "widgets/misc/e-attachment-paned.h"
#include "widgets/misc/e-attachment-store.h"
+#include "widgets/misc/e-signature-combo-box.h"
+#include "widgets/misc/e-web-view.h"
+#include "shell/e-shell.h"
+
+#ifdef HAVE_XFREE
+#include <X11/XF86keysym.h>
+#endif
+
+/* backward-compatibility cruft */
+#include "e-util/gtk-compat.h"
#define E_MSG_COMPOSER_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -92,15 +116,19 @@ struct _EMsgComposerPrivate {
GtkWidget *html_editor;
GtkWidget *header_table;
+ GtkWidget *activity_bar;
+ GtkWidget *alert_bar;
GtkWidget *attachment_paned;
EFocusTracker *focus_tracker;
GtkWindowGroup *window_group;
+ GtkActionGroup *async_actions;
GtkActionGroup *charset_actions;
GtkActionGroup *composer_actions;
- GPtrArray *extra_hdr_names, *extra_hdr_values;
+ GPtrArray *extra_hdr_names;
+ GPtrArray *extra_hdr_values;
GArray *gconf_bridge_binding_ids;
GtkWidget *focused_entry;
@@ -111,9 +139,10 @@ struct _EMsgComposerPrivate {
GHashTable *inline_images_by_url;
GList *current_images;
- gchar *mime_type, *mime_body, *charset;
+ gchar *mime_type;
+ gchar *mime_body;
+ gchar *charset;
- guint32 is_alternative : 1;
guint32 autosaved : 1;
guint32 mode_post : 1;
guint32 in_signature_insert : 1;
@@ -122,12 +151,6 @@ struct _EMsgComposerPrivate {
CamelMimeMessage *redirect;
gboolean is_from_message;
-
- /* The mail composed has been sent. This bit will be set when
- * the mail passed sanity checking and is sent out, which
- * indicates that the composer can be destroyed. This bit can
- * be set/get by using API e_msg_composer_{set,get}_mail_sent (). */
- gboolean mail_sent;
};
void e_composer_private_constructed (EMsgComposer *composer);
diff --git a/composer/e-msg-composer.c b/composer/e-msg-composer.c
index 0ea116f6dc..cc4b3c3b08 100644
--- a/composer/e-msg-composer.c
+++ b/composer/e-msg-composer.c
@@ -37,45 +37,53 @@
#include <ctype.h>
#include <fcntl.h>
-#include <glib.h>
-
-#include <gtk/gtk.h>
-#include <gdk/gdkkeysyms.h>
-
-#include <gconf/gconf.h>
-#include <gconf/gconf-client.h>
-
#include "e-util/e-account-utils.h"
#include "e-util/e-alert-dialog.h"
-#include "e-util/e-alert-sink.h"
#include "e-util/e-dialog-utils.h"
-#include "e-util/e-extensible.h"
-#include "e-util/e-plugin-ui.h"
#include "e-util/e-signature-utils.h"
#include "e-util/e-util-private.h"
-#include "e-signature-combo-box.h"
-#include "shell/e-shell.h"
#include "em-format/em-format.h"
#include "em-format/em-format-quote.h"
-#include "misc/e-web-view.h"
-#include "e-msg-composer.h"
-#include "e-attachment.h"
#include "e-composer-private.h"
-#include "e-composer-header-table.h"
-#ifdef HAVE_XFREE
-#include <X11/XF86keysym.h>
-#endif
+typedef struct _AsyncContext AsyncContext;
+
+struct _AsyncContext {
+ EActivity *activity;
+
+ CamelMimeMessage *message;
+ CamelDataWrapper *top_level_part;
+ CamelDataWrapper *text_plain_part;
+
+ EAccount *account;
+ CamelSession *session;
+ CamelInternetAddress *from;
-/* backward-compatibility cruft */
-#include "e-util/gtk-compat.h"
+ CamelTransferEncoding plain_encoding;
+ GtkPrintOperationAction print_action;
+
+ GPtrArray *recipients;
-#define d(x)
+ guint skip_content : 1;
+ guint need_thread : 1;
+ guint pgp_sign : 1;
+ guint pgp_encrypt : 1;
+ guint smime_sign : 1;
+ guint smime_encrypt : 1;
+};
-#define E_MSG_COMPOSER_GET_PRIVATE(obj) \
- (G_TYPE_INSTANCE_GET_PRIVATE \
- ((obj), E_TYPE_MSG_COMPOSER, EMsgComposerPrivate))
+/* Flags for building a message. */
+typedef enum {
+ COMPOSER_FLAG_HTML_CONTENT = 1 << 0,
+ COMPOSER_FLAG_SAVE_OBJECT_DATA = 1 << 1,
+ COMPOSER_FLAG_PRIORITIZE_MESSAGE = 1 << 2,
+ COMPOSER_FLAG_REQUEST_READ_RECEIPT = 1 << 3,
+ COMPOSER_FLAG_PGP_SIGN = 1 << 4,
+ COMPOSER_FLAG_PGP_ENCRYPT = 1 << 5,
+ COMPOSER_FLAG_SMIME_SIGN = 1 << 6,
+ COMPOSER_FLAG_SMIME_ENCRYPT = 1 << 7
+} ComposerFlags;
enum {
PROP_0,
@@ -84,6 +92,7 @@ enum {
};
enum {
+ PRESEND,
SEND,
SAVE_DRAFT,
PRINT,
@@ -92,11 +101,6 @@ enum {
static guint signals[LAST_SIGNAL];
-/* local prototypes */
-static GList *add_recipients (GList *list, const gchar *recips);
-
-static void handle_mailto (EMsgComposer *composer, const gchar *mailto);
-
/* used by e_msg_composer_add_message_attachments () */
static void add_attachments_from_multipart (EMsgComposer *composer,
CamelMultipart *multipart,
@@ -121,13 +125,46 @@ static void handle_multipart_signed (EMsgComposer *composer,
GCancellable *cancellable,
gint depth);
+static void e_msg_composer_alert_sink_init (EAlertSinkInterface *interface);
+
G_DEFINE_TYPE_WITH_CODE (
EMsgComposer,
e_msg_composer,
GTKHTML_TYPE_EDITOR,
- G_IMPLEMENT_INTERFACE (E_TYPE_ALERT_SINK, NULL)
+ G_IMPLEMENT_INTERFACE (
+ E_TYPE_ALERT_SINK, e_msg_composer_alert_sink_init)
G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
+static void
+async_context_free (AsyncContext *context)
+{
+ if (context->activity != NULL)
+ g_object_unref (context->activity);
+
+ if (context->message != NULL)
+ g_object_unref (context->message);
+
+ if (context->top_level_part != NULL)
+ g_object_unref (context->top_level_part);
+
+ if (context->text_plain_part != NULL)
+ g_object_unref (context->text_plain_part);
+
+ if (context->account != NULL)
+ g_object_unref (context->account);
+
+ if (context->session != NULL)
+ g_object_unref (context->session);
+
+ if (context->from != NULL)
+ g_object_unref (context->from);
+
+ if (context->recipients != NULL)
+ g_ptr_array_free (context->recipients, TRUE);
+
+ g_slice_free (AsyncContext, context);
+}
+
/**
* emcu_part_to_html:
* @part:
@@ -181,52 +218,6 @@ emcu_part_to_html (CamelMimePart *part,
return text;
}
-/* copy of em_utils_prompt_user from mailer */
-static gboolean
-emcu_prompt_user (GtkWindow *parent, const gchar *promptkey, const gchar *tag, ...)
-{
- GtkDialog *mbox;
- GtkWidget *check = NULL;
- GtkWidget *container;
- va_list ap;
- gint button;
- GConfClient *gconf = gconf_client_get_default ();
- EAlert *alert = NULL;
-
- if (promptkey
- && !gconf_client_get_bool (gconf, promptkey, NULL)) {
- g_object_unref (gconf);
- return TRUE;
- }
-
- va_start (ap, tag);
- alert = e_alert_new_valist (tag, ap);
- va_end (ap);
-
- mbox = (GtkDialog*) e_alert_dialog_new (parent, alert);
- g_object_unref (alert);
-
- if (promptkey) {
- check = gtk_check_button_new_with_mnemonic (_("_Do not show this message again."));
- gtk_container_set_border_width ((GtkContainer *)check, 12);
- container = gtk_dialog_get_content_area (mbox);
- gtk_box_pack_start (GTK_BOX (container), check, TRUE, TRUE, 0);
- gtk_widget_show (check);
- }
-
- button = gtk_dialog_run (mbox);
- if (promptkey)
- gconf_client_set_bool (
- gconf, promptkey,
- !gtk_toggle_button_get_active (
- GTK_TOGGLE_BUTTON (check)), NULL);
-
- gtk_widget_destroy ((GtkWidget*) mbox);
- g_object_unref (gconf);
-
- return button == GTK_RESPONSE_YES;
-}
-
/* copy of mail_tool_remove_xevolution_headers */
static struct _camel_header_raw *
emcu_remove_xevolution_headers (CamelMimeMessage *message)
@@ -607,42 +598,413 @@ account_hash_algo_to_camel_hash (const gchar *hash_algo)
return res;
}
-static CamelMimeMessage *
-build_message (EMsgComposer *composer,
- gboolean html_content,
- gboolean save_html_object_data,
- GCancellable *cancellable,
- GError **error)
+static void
+composer_add_charset_filter (CamelStream *stream,
+ const gchar *charset)
{
- GtkhtmlEditor *editor;
- EMsgComposerPrivate *p = composer->priv;
+ CamelMimeFilter *filter;
+
+ filter = camel_mime_filter_charset_new ("UTF-8", charset);
+ camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), filter);
+ g_object_unref (filter);
+}
+
+static void
+composer_add_quoted_printable_filter (CamelStream *stream)
+{
+ CamelMimeFilter *filter;
+
+ filter = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_QP_ENC);
+ camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), filter);
+ g_object_unref (filter);
+
+ filter = camel_mime_filter_canon_new (CAMEL_MIME_FILTER_CANON_FROM);
+ camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), filter);
+ g_object_unref (filter);
+}
+
+/* Helper for composer_build_message_thread() */
+static gboolean
+composer_build_message_pgp (AsyncContext *context,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelCipherContext *cipher;
+ CamelDataWrapper *content;
+ CamelMimePart *mime_part;
+ const gchar *pgp_userid;
+ gboolean have_pgp_key;
+
+ /* Return silently if we're not signing or encrypting with PGP. */
+ if (!context->pgp_sign && !context->pgp_encrypt)
+ return TRUE;
+
+ have_pgp_key =
+ (context->account != NULL) &&
+ (context->account->pgp_key != NULL) &&
+ (context->account->pgp_key[0] != '\0');
+
+ mime_part = camel_mime_part_new ();
+
+ camel_medium_set_content (
+ CAMEL_MEDIUM (mime_part),
+ context->top_level_part);
+
+ if (context->top_level_part == context->text_plain_part)
+ camel_mime_part_set_encoding (
+ mime_part, context->plain_encoding);
+
+ g_object_unref (context->top_level_part);
+ context->top_level_part = NULL;
+
+ if (have_pgp_key)
+ pgp_userid = context->account->pgp_key;
+ else
+ camel_internet_address_get (
+ context->from, 0, NULL, &pgp_userid);
+
+ if (context->pgp_sign) {
+ CamelMimePart *npart;
+ gboolean success;
+
+ npart = camel_mime_part_new ();
+
+ cipher = camel_gpg_context_new (context->session);
+ if (context->account != NULL)
+ camel_gpg_context_set_always_trust (
+ CAMEL_GPG_CONTEXT (cipher),
+ context->account->pgp_always_trust);
+
+ success = camel_cipher_context_sign_sync (
+ cipher, pgp_userid,
+ account_hash_algo_to_camel_hash (
+ (context->account != NULL) ?
+ e_account_get_string (context->account,
+ E_ACCOUNT_PGP_HASH_ALGORITHM) : NULL),
+ mime_part, npart, cancellable, error);
+
+ g_object_unref (cipher);
+
+ g_object_unref (mime_part);
+
+ if (!success) {
+ g_object_unref (npart);
+ return FALSE;
+ }
+
+ mime_part = npart;
+ }
+
+ if (context->pgp_encrypt) {
+ CamelMimePart *npart;
+ gboolean encrypt_to_self;
+ gboolean success;
+
+ encrypt_to_self =
+ (context->account != NULL) &&
+ (context->account->pgp_encrypt_to_self) &&
+ (pgp_userid != NULL);
+
+ npart = camel_mime_part_new ();
+
+ /* Check to see if we should encrypt to self.
+ * NB gets removed immediately after use */
+ if (encrypt_to_self)
+ g_ptr_array_add (
+ context->recipients,
+ g_strdup (pgp_userid));
+
+ cipher = camel_gpg_context_new (context->session);
+ if (context->account != NULL)
+ camel_gpg_context_set_always_trust (
+ CAMEL_GPG_CONTEXT (cipher),
+ context->account->pgp_always_trust);
+ success = camel_cipher_context_encrypt_sync (
+ cipher, pgp_userid, context->recipients,
+ mime_part, npart, cancellable, error);
+
+ g_object_unref (cipher);
+
+ if (encrypt_to_self)
+ g_ptr_array_set_size (
+ context->recipients,
+ context->recipients->len - 1);
+
+ g_object_unref (mime_part);
+
+ if (!success) {
+ g_object_unref (npart);
+ return FALSE;
+ }
+
+ mime_part = npart;
+ }
+
+ content = camel_medium_get_content (CAMEL_MEDIUM (mime_part));
+ context->top_level_part = g_object_ref (content);
+
+ g_object_unref (mime_part);
+
+ return TRUE;
+}
+
+#ifdef HAVE_SSL
+static gboolean
+composer_build_message_smime (AsyncContext *context,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CamelCipherContext *cipher;
+ CamelMimePart *mime_part;
+ gboolean have_signing_certificate;
+ gboolean have_encryption_certificate;
+
+ /* Return silently if we're not signing or encrypting with S/MIME. */
+ if (!context->smime_sign && !context->smime_encrypt)
+ return TRUE;
+
+ have_signing_certificate =
+ (context->account != NULL) &&
+ (context->account->smime_sign_key != NULL) &&
+ (context->account->smime_sign_key[0] != '\0');
+
+ have_encryption_certificate =
+ (context->account != NULL) &&
+ (context->account->smime_encrypt_key != NULL) &&
+ (context->account->smime_encrypt_key[0] != '\0');
+
+ if (context->smime_sign && !have_signing_certificate) {
+ g_set_error (
+ error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
+ _("Cannot sign outgoing message: "
+ "No signing certificate set for "
+ "this account"));
+ return FALSE;
+ }
+
+ if (context->smime_encrypt && !have_encryption_certificate) {
+ g_set_error (
+ error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
+ _("Cannot encrypt outgoing message: "
+ "No encryption certificate set for "
+ "this account"));
+ return FALSE;
+ }
+
+ mime_part = camel_mime_part_new ();
+
+ camel_medium_set_content (
+ CAMEL_MEDIUM (mime_part),
+ context->top_level_part);
+
+ if (context->top_level_part == context->text_plain_part)
+ camel_mime_part_set_encoding (
+ mime_part, context->plain_encoding);
+
+ g_object_unref (context->top_level_part);
+ context->top_level_part = NULL;
+
+ if (context->smime_sign) {
+ CamelMimePart *npart;
+ gboolean success;
+
+ npart = camel_mime_part_new ();
+
+ cipher = camel_smime_context_new (context->session);
+
+ /* if we're also encrypting, envelope-sign rather than clear-sign */
+ if (context->smime_encrypt) {
+ camel_smime_context_set_sign_mode (
+ (CamelSMIMEContext *) cipher,
+ CAMEL_SMIME_SIGN_ENVELOPED);
+ camel_smime_context_set_encrypt_key (
+ (CamelSMIMEContext *) cipher, TRUE,
+ context->account->smime_encrypt_key);
+ } else if (have_encryption_certificate) {
+ camel_smime_context_set_encrypt_key (
+ (CamelSMIMEContext *) cipher, TRUE,
+ context->account->smime_encrypt_key);
+ }
+
+ success = camel_cipher_context_sign_sync (
+ cipher, context->account->smime_sign_key,
+ account_hash_algo_to_camel_hash (
+ (context->account != NULL) ?
+ e_account_get_string (context->account,
+ E_ACCOUNT_SMIME_HASH_ALGORITHM) : NULL),
+ mime_part, npart, cancellable, error);
+
+ g_object_unref (cipher);
+
+ g_object_unref (mime_part);
+
+ if (!success) {
+ g_object_unref (npart);
+ return FALSE;
+ }
+
+ mime_part = npart;
+ }
+
+ if (context->smime_encrypt) {
+ gboolean success;
+
+ /* check to see if we should encrypt to self, NB removed after use */
+ if (context->account->smime_encrypt_to_self)
+ g_ptr_array_add (
+ context->recipients, g_strdup (
+ context->account->smime_encrypt_key));
+
+ cipher = camel_smime_context_new (context->session);
+ camel_smime_context_set_encrypt_key (
+ (CamelSMIMEContext *) cipher, TRUE,
+ context->account->smime_encrypt_key);
+
+ success = camel_cipher_context_encrypt_sync (
+ cipher, NULL,
+ context->recipients, mime_part,
+ CAMEL_MIME_PART (context->message),
+ cancellable, error);
+
+ g_object_unref (cipher);
+
+ if (!success)
+ return FALSE;
+
+ if (context->account->smime_encrypt_to_self)
+ g_ptr_array_set_size (
+ context->recipients,
+ context->recipients->len - 1);
+ }
+
+ /* we replaced the message directly, we don't want to do reparenting foo */
+ if (context->smime_encrypt) {
+ context->skip_content = TRUE;
+ } else {
+ CamelDataWrapper *content;
+
+ content = camel_medium_get_content (
+ CAMEL_MEDIUM (mime_part));
+ context->top_level_part = g_object_ref (content);
+ }
+
+ g_object_unref (mime_part);
+
+ return TRUE;
+}
+#endif
+
+static void
+composer_build_message_thread (GSimpleAsyncResult *simple,
+ EMsgComposer *composer,
+ GCancellable *cancellable)
+{
+ AsyncContext *context;
+ GError *error = NULL;
+
+ context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ /* Setup working recipient list if we're encrypting. */
+ if (context->pgp_encrypt || context->smime_encrypt) {
+ gint ii, jj;
+
+ const gchar *types[] = {
+ CAMEL_RECIPIENT_TYPE_TO,
+ CAMEL_RECIPIENT_TYPE_CC,
+ CAMEL_RECIPIENT_TYPE_BCC
+ };
+
+ context->recipients = g_ptr_array_new_with_free_func (
+ (GDestroyNotify) g_free);
+ for (ii = 0; ii < G_N_ELEMENTS (types); ii++) {
+ CamelInternetAddress *addr;
+ const gchar *address;
+
+ addr = camel_mime_message_get_recipients (
+ context->message, types[ii]);
+ for (jj = 0; camel_internet_address_get (addr, jj, NULL, &address); jj++)
+ g_ptr_array_add (
+ context->recipients,
+ g_strdup (address));
+ }
+ }
+
+ if (!composer_build_message_pgp (context, cancellable, &error)) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ return;
+ }
+
+#if defined (HAVE_NSS)
+ if (!composer_build_message_smime (context, cancellable, &error)) {
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ return;
+ }
+#endif /* HAVE_NSS */
+}
+
+static void
+composer_add_evolution_format_header (CamelMedium *medium,
+ ComposerFlags flags)
+{
+ GString *string;
+
+ string = g_string_sized_new (128);
+
+ if (flags & COMPOSER_FLAG_HTML_CONTENT)
+ g_string_append (string, "text/html");
+ else
+ g_string_append (string, "text/plain");
+
+ if (flags & COMPOSER_FLAG_PGP_SIGN)
+ g_string_append (string, ", pgp-sign");
+
+ if (flags & COMPOSER_FLAG_PGP_ENCRYPT)
+ g_string_append (string, ", pgp-encrypt");
+
+ if (flags & COMPOSER_FLAG_SMIME_SIGN)
+ g_string_append (string, ", smime-sign");
+
+ if (flags & COMPOSER_FLAG_SMIME_ENCRYPT)
+ g_string_append (string, ", smime-encrypt");
+
+ camel_medium_add_header (
+ medium, "X-Evolution-Format", string->str);
+
+ g_string_free (string, TRUE);
+}
+
+static void
+composer_build_message (EMsgComposer *composer,
+ ComposerFlags flags,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ EMsgComposerPrivate *priv;
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
+ GtkhtmlEditor *editor;
EAttachmentView *view;
EAttachmentStore *store;
EComposerHeaderTable *table;
- GtkToggleAction *action;
- CamelDataWrapper *plain, *html, *current;
- CamelTransferEncoding plain_encoding;
+ CamelDataWrapper *html;
const gchar *iconv_charset = NULL;
- GPtrArray *recipients = NULL;
CamelMultipart *body = NULL;
CamelContentType *type;
- CamelMimeMessage *new;
CamelSession *session;
CamelStream *stream;
+ CamelStream *mem_stream;
CamelMimePart *part;
GByteArray *data;
EAccount *account;
gchar *charset;
- gboolean pgp_sign;
- gboolean pgp_encrypt;
- gboolean smime_sign;
- gboolean smime_encrypt;
gint i;
- GError *local_error = NULL;
-
- g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
+ priv = composer->priv;
editor = GTKHTML_EDITOR (composer);
table = e_msg_composer_get_header_table (composer);
account = e_composer_header_table_get_account (table);
@@ -650,55 +1012,129 @@ build_message (EMsgComposer *composer,
store = e_attachment_view_get_store (view);
session = e_msg_composer_get_session (composer);
- /* evil kludgy hack for Redirect */
- if (p->redirect) {
- build_message_headers (composer, p->redirect, TRUE);
- g_object_ref (p->redirect);
- return p->redirect;
+ /* Do all the non-blocking work here, and defer
+ * any blocking operations to a separate thread. */
+
+ context = g_slice_new0 (AsyncContext);
+ context->account = g_object_ref (account);
+ context->session = g_object_ref (session);
+ context->from = e_msg_composer_get_from (composer);
+
+ if (flags & COMPOSER_FLAG_PGP_SIGN)
+ context->pgp_sign = TRUE;
+
+ if (flags & COMPOSER_FLAG_PGP_ENCRYPT)
+ context->pgp_encrypt = TRUE;
+
+ if (flags & COMPOSER_FLAG_SMIME_SIGN)
+ context->smime_sign = TRUE;
+
+ if (flags & COMPOSER_FLAG_SMIME_ENCRYPT)
+ context->smime_encrypt = TRUE;
+
+ context->need_thread =
+ context->pgp_sign || context->pgp_encrypt ||
+ context->smime_sign || context->smime_encrypt;
+
+ simple = g_simple_async_result_new (
+ G_OBJECT (composer), callback,
+ user_data, composer_build_message);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, context, (GDestroyNotify) async_context_free);
+
+ /* If this is a redirected message, just tweak the headers. */
+ if (priv->redirect) {
+ context->message = g_object_ref (priv->redirect);
+ build_message_headers (composer, context->message, TRUE);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+ return;
}
- new = camel_mime_message_new ();
- build_message_headers (composer, new, FALSE);
- for (i = 0; i < p->extra_hdr_names->len; i++) {
+ context->message = camel_mime_message_new ();
+
+ build_message_headers (composer, context->message, FALSE);
+ for (i = 0; i < priv->extra_hdr_names->len; i++) {
camel_medium_add_header (
- CAMEL_MEDIUM (new),
- p->extra_hdr_names->pdata[i],
- p->extra_hdr_values->pdata[i]);
+ CAMEL_MEDIUM (context->message),
+ priv->extra_hdr_names->pdata[i],
+ priv->extra_hdr_values->pdata[i]);
}
- /* Message Disposition Notification */
- action = GTK_TOGGLE_ACTION (ACTION (REQUEST_READ_RECEIPT));
- if (gtk_toggle_action_get_active (action)) {
+ /* Disposition-Notification-To */
+ if (flags & COMPOSER_FLAG_REQUEST_READ_RECEIPT) {
gchar *mdn_address = account->id->reply_to;
if (!mdn_address || !*mdn_address)
mdn_address = account->id->address;
camel_medium_add_header (
- CAMEL_MEDIUM (new),
+ CAMEL_MEDIUM (context->message),
"Disposition-Notification-To", mdn_address);
}
- /* Message Priority */
- action = GTK_TOGGLE_ACTION (ACTION (PRIORITIZE_MESSAGE));
- if (gtk_toggle_action_get_active (action))
+ /* X-Priority */
+ if (flags & COMPOSER_FLAG_PRIORITIZE_MESSAGE)
camel_medium_add_header (
- CAMEL_MEDIUM (new), "X-Priority", "1");
+ CAMEL_MEDIUM (context->message),
+ "X-Priority", "1");
- if (p->mime_body) {
- if (text_requires_quoted_printable (p->mime_body, -1)) {
- plain_encoding = CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
+ if (account != NULL) {
+ /* X-Evolution-Account */
+ camel_medium_set_header (
+ CAMEL_MEDIUM (context->message),
+ "X-Evolution-Account", account->uid);
+
+ /* X-Evolution-Fcc */
+ camel_medium_set_header (
+ CAMEL_MEDIUM (context->message),
+ "X-Evolution-Fcc", account->sent_folder_uri);
+
+ /* X-Evolution-Transport */
+ camel_medium_set_header (
+ CAMEL_MEDIUM (context->message),
+ "X-Evolution-Transport", account->transport->url);
+
+ /* Organization */
+ if (account->id->organization != NULL) {
+ gchar *organization;
+
+ organization = camel_header_encode_string (
+ (const guchar *) account->id->organization);
+ camel_medium_set_header (
+ CAMEL_MEDIUM (context->message),
+ "Organization", organization);
+ g_free (organization);
+ }
+ }
+
+ /* X-Evolution-Format */
+ composer_add_evolution_format_header (
+ CAMEL_MEDIUM (context->message), flags);
+
+ /* Build the text/plain part. */
+
+ if (priv->mime_body) {
+ if (text_requires_quoted_printable (priv->mime_body, -1)) {
+ context->plain_encoding =
+ CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
} else {
- plain_encoding = CAMEL_TRANSFER_ENCODING_7BIT;
- for (i = 0; p->mime_body[i]; i++) {
- if ((guchar) p->mime_body[i] > 127) {
- plain_encoding = CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
+ context->plain_encoding = CAMEL_TRANSFER_ENCODING_7BIT;
+ for (i = 0; priv->mime_body[i]; i++) {
+ if ((guchar) priv->mime_body[i] > 127) {
+ context->plain_encoding =
+ CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
break;
}
}
}
+
data = g_byte_array_new ();
- g_byte_array_append (data, (const guint8 *)p->mime_body, strlen (p->mime_body));
- type = camel_content_type_decode (p->mime_type);
+ g_byte_array_append (
+ data, (const guint8 *) priv->mime_body,
+ strlen (priv->mime_body));
+ type = camel_content_type_decode (priv->mime_type);
+
} else {
gchar *text;
gsize length;
@@ -708,78 +1144,70 @@ build_message (EMsgComposer *composer,
g_byte_array_append (data, (guint8 *) text, (guint) length);
g_free (text);
- /* FIXME: we may want to do better than this... */
-
type = camel_content_type_new ("text", "plain");
- if ((charset = best_charset (data, p->charset, &plain_encoding))) {
+ charset = best_charset (
+ data, priv->charset, &context->plain_encoding);
+ if (charset != NULL) {
camel_content_type_set_param (type, "charset", charset);
iconv_charset = camel_iconv_charset_name (charset);
g_free (charset);
}
}
- stream = camel_stream_mem_new_with_byte_array (data);
-
- /* convert the stream to the appropriate charset */
- if (iconv_charset && g_ascii_strcasecmp (iconv_charset, "UTF-8") != 0) {
- CamelStream *filter_stream;
- CamelMimeFilter *filter;
+ mem_stream = camel_stream_mem_new_with_byte_array (data);
+ stream = camel_stream_filter_new (mem_stream);
+ g_object_unref (mem_stream);
- filter_stream = camel_stream_filter_new (stream);
- g_object_unref (stream);
-
- stream = filter_stream;
- filter = camel_mime_filter_charset_new ("UTF-8", iconv_charset);
- camel_stream_filter_add (
- CAMEL_STREAM_FILTER (filter_stream), filter);
- g_object_unref (filter);
- }
+ /* Convert the stream to the appropriate charset. */
+ if (iconv_charset && g_ascii_strcasecmp (iconv_charset, "UTF-8") != 0)
+ composer_add_charset_filter (stream, iconv_charset);
- if (plain_encoding == CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE) {
- /* encode to quoted-printable by ourself, together with
- * taking care of "\nFrom " text */
- CamelStream *filter_stream;
- CamelMimeFilter *mf, *qp;
+ /* Encode the stream to quoted-printable if necessary. */
+ if (context->plain_encoding == CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE)
+ composer_add_quoted_printable_filter (stream);
- if (!CAMEL_IS_STREAM_FILTER (stream)) {
- filter_stream = camel_stream_filter_new (stream);
- g_object_unref (stream);
-
- stream = filter_stream;
- }
-
- qp = camel_mime_filter_basic_new (
- CAMEL_MIME_FILTER_BASIC_QP_ENC);
- camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), qp);
- g_object_unref (qp);
-
- mf = camel_mime_filter_canon_new (CAMEL_MIME_FILTER_CANON_FROM);
- camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), mf);
- g_object_unref (mf);
- }
-
- /* construct the content object */
- plain = camel_data_wrapper_new ();
+ /* Construct the content object. This does not block since
+ * we're constructing the data wrapper from a memory stream. */
+ context->top_level_part = camel_data_wrapper_new ();
camel_data_wrapper_construct_from_stream_sync (
- plain, stream, NULL, NULL);
+ context->top_level_part, stream, NULL, NULL);
g_object_unref (stream);
- if (plain_encoding == CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE) {
- /* to not re-encode the data when pushing it to a part */
- plain->encoding = plain_encoding;
- }
+ context->text_plain_part = g_object_ref (context->top_level_part);
+
+ /* Avoid re-encoding the data when adding it to a MIME part. */
+ if (context->plain_encoding == CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE)
+ context->top_level_part->encoding = context->plain_encoding;
+
+ camel_data_wrapper_set_mime_type_field (
+ context->top_level_part, type);
- camel_data_wrapper_set_mime_type_field (plain, type);
camel_content_type_unref (type);
- if (html_content) {
+ /* Build the text/html part, and wrap it and the text/plain part
+ * in a multipart/alternative part. Additionally, if there are
+ * inline images then wrap the multipart/alternative part along
+ * with the images in a multipart/related part.
+ *
+ * So the structure of all this will be:
+ *
+ * multipart/related
+ * multipart/alternative
+ * text/plain
+ * text/html
+ * image/<<whatever>>
+ * image/<<whatever>>
+ * ...
+ */
+
+ if (flags & COMPOSER_FLAG_HTML_CONTENT) {
gchar *text;
gsize length;
gboolean pre_encode;
clear_current_images (composer);
- if (save_html_object_data)
+ if (flags & COMPOSER_FLAG_SAVE_OBJECT_DATA)
gtkhtml_editor_run_command (editor, "save-data-on");
data = g_byte_array_new ();
@@ -788,45 +1216,30 @@ build_message (EMsgComposer *composer,
pre_encode = text_requires_quoted_printable (text, length);
g_free (text);
- if (save_html_object_data)
+ if (flags & COMPOSER_FLAG_SAVE_OBJECT_DATA)
gtkhtml_editor_run_command (editor, "save-data-off");
- html = camel_data_wrapper_new ();
-
- stream = camel_stream_mem_new_with_byte_array (data);
-
- if (pre_encode) {
- /* encode to quoted-printable by ourself, together with
- * taking care of "\nFrom " text */
- CamelStream *filter_stream;
- CamelMimeFilter *mf, *qp;
+ mem_stream = camel_stream_mem_new_with_byte_array (data);
+ stream = camel_stream_filter_new (mem_stream);
+ g_object_unref (mem_stream);
- if (!CAMEL_IS_STREAM_FILTER (stream)) {
- filter_stream = camel_stream_filter_new (stream);
- g_object_unref (stream);
-
- stream = filter_stream;
- }
-
- qp = camel_mime_filter_basic_new (
- CAMEL_MIME_FILTER_BASIC_QP_ENC);
- camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), qp);
- g_object_unref (qp);
-
- mf = camel_mime_filter_canon_new (CAMEL_MIME_FILTER_CANON_FROM);
- camel_stream_filter_add (CAMEL_STREAM_FILTER (stream), mf);
- g_object_unref (mf);
- }
+ if (pre_encode)
+ composer_add_quoted_printable_filter (stream);
+ /* Construct the content object. This does not block since
+ * we're constructing the data wrapper from a memory stream. */
+ html = camel_data_wrapper_new ();
camel_data_wrapper_construct_from_stream_sync (
html, stream, NULL, NULL);
g_object_unref (stream);
- camel_data_wrapper_set_mime_type (html, "text/html; charset=utf-8");
- if (pre_encode) {
- /* to not re-encode the data when pushing it to a part */
- html->encoding = CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
- }
+ camel_data_wrapper_set_mime_type (
+ html, "text/html; charset=utf-8");
+
+ /* Avoid re-encoding the data when adding it to a MIME part. */
+ if (pre_encode)
+ html->encoding =
+ CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE;
/* Build the multipart/alternative */
body = camel_multipart_new ();
@@ -834,357 +1247,124 @@ build_message (EMsgComposer *composer,
CAMEL_DATA_WRAPPER (body), "multipart/alternative");
camel_multipart_set_boundary (body, NULL);
+ /* Add the text/plain part. */
part = camel_mime_part_new ();
- camel_medium_set_content (CAMEL_MEDIUM (part), plain);
- g_object_unref (plain);
- camel_mime_part_set_encoding (part, plain_encoding);
+ camel_medium_set_content (
+ CAMEL_MEDIUM (part), context->top_level_part);
+ camel_mime_part_set_encoding (part, context->plain_encoding);
camel_multipart_add_part (body, part);
g_object_unref (part);
+ /* Add the text/html part. */
part = camel_mime_part_new ();
camel_medium_set_content (CAMEL_MEDIUM (part), html);
- g_object_unref (html);
- camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE);
+ camel_mime_part_set_encoding (
+ part, CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE);
camel_multipart_add_part (body, part);
g_object_unref (part);
- /* If there are inlined images, construct a
- * multipart/related containing the
- * multipart/alternative and the images.
- */
- if (p->current_images) {
+ g_object_unref (context->top_level_part);
+ g_object_unref (html);
+
+ /* If there are inlined images, construct a multipart/related
+ * containing the multipart/alternative and the images. */
+ if (priv->current_images) {
CamelMultipart *html_with_images;
html_with_images = camel_multipart_new ();
camel_data_wrapper_set_mime_type (
CAMEL_DATA_WRAPPER (html_with_images),
- "multipart/related; type=\"multipart/alternative\"");
+ "multipart/related; "
+ "type=\"multipart/alternative\"");
camel_multipart_set_boundary (html_with_images, NULL);
part = camel_mime_part_new ();
- camel_medium_set_content (CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (body));
- g_object_unref (body);
+ camel_medium_set_content (
+ CAMEL_MEDIUM (part),
+ CAMEL_DATA_WRAPPER (body));
camel_multipart_add_part (html_with_images, part);
g_object_unref (part);
+ g_object_unref (body);
+
add_inlined_images (composer, html_with_images);
clear_current_images (composer);
- current = CAMEL_DATA_WRAPPER (html_with_images);
+ context->top_level_part =
+ CAMEL_DATA_WRAPPER (html_with_images);
} else
- current = CAMEL_DATA_WRAPPER (body);
- } else
- current = plain;
+ context->top_level_part =
+ CAMEL_DATA_WRAPPER (body);
+ }
+ /* If there are attachments, wrap what we've built so far
+ * along with the attachments in a multipart/mixed part. */
if (e_attachment_store_get_num_attachments (store) > 0) {
CamelMultipart *multipart = camel_multipart_new ();
- if (p->is_alternative) {
- camel_data_wrapper_set_mime_type (
- CAMEL_DATA_WRAPPER (multipart),
- "multipart/alternative");
- }
-
/* Generate a random boundary. */
camel_multipart_set_boundary (multipart, NULL);
part = camel_mime_part_new ();
- camel_medium_set_content (CAMEL_MEDIUM (part), current);
- if (current == plain)
- camel_mime_part_set_encoding (part, plain_encoding);
- g_object_unref (current);
+ camel_medium_set_content (
+ CAMEL_MEDIUM (part),
+ context->top_level_part);
+ if (context->top_level_part == context->text_plain_part)
+ camel_mime_part_set_encoding (
+ part, context->plain_encoding);
camel_multipart_add_part (multipart, part);
g_object_unref (part);
e_attachment_store_add_to_multipart (
- store, multipart, p->charset);
+ store, multipart, priv->charset);
- if (p->is_alternative) {
- for (i = camel_multipart_get_number (multipart); i > 1; i--) {
- part = camel_multipart_get_part (multipart, i - 1);
- camel_medium_remove_header (CAMEL_MEDIUM (part), "Content-Disposition");
- }
- }
-
- current = CAMEL_DATA_WRAPPER (multipart);
+ g_object_unref (context->top_level_part);
+ context->top_level_part = CAMEL_DATA_WRAPPER (multipart);
}
- action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
- pgp_sign = gtk_toggle_action_get_active (action);
-
- action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT));
- pgp_encrypt = gtk_toggle_action_get_active (action);
-
-#if defined (HAVE_NSS)
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN));
- smime_sign = gtk_toggle_action_get_active (action);
-
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT));
- smime_encrypt = gtk_toggle_action_get_active (action);
-#else
- smime_sign = FALSE;
- smime_encrypt = FALSE;
-#endif
-
- /* Setup working recipient list if we're encrypting */
- if (pgp_encrypt || smime_encrypt) {
- gint j;
- const gchar *types[] = {
- CAMEL_RECIPIENT_TYPE_TO,
- CAMEL_RECIPIENT_TYPE_CC,
- CAMEL_RECIPIENT_TYPE_BCC
- };
-
- recipients = g_ptr_array_new ();
- for (i = 0; i < G_N_ELEMENTS (types); i++) {
- CamelInternetAddress *addr;
- const gchar *address;
-
- addr = camel_mime_message_get_recipients (new, types[i]);
- for (j=0;camel_internet_address_get (addr, j, NULL, &address); j++)
- g_ptr_array_add (recipients, g_strdup (address));
-
- }
- }
-
- if (pgp_sign || pgp_encrypt) {
- const gchar *pgp_userid;
- CamelInternetAddress *from = NULL;
- CamelCipherContext *cipher;
-
- part = camel_mime_part_new ();
- camel_medium_set_content (CAMEL_MEDIUM (part), current);
- if (current == plain)
- camel_mime_part_set_encoding (part, plain_encoding);
- g_object_unref (current);
-
- if (account && account->pgp_key && *account->pgp_key) {
- pgp_userid = account->pgp_key;
- } else {
- from = e_msg_composer_get_from (composer);
- camel_internet_address_get (from, 0, NULL, &pgp_userid);
- }
-
- if (pgp_sign) {
- CamelMimePart *npart = camel_mime_part_new ();
-
- cipher = camel_gpg_context_new (session);
- if (account != NULL)
- camel_gpg_context_set_always_trust (
- CAMEL_GPG_CONTEXT (cipher),
- account->pgp_always_trust);
-
- camel_cipher_context_sign_sync (
- cipher, pgp_userid,
- account_hash_algo_to_camel_hash (
- account ? e_account_get_string (account, E_ACCOUNT_PGP_HASH_ALGORITHM) : NULL),
- part, npart, cancellable, &local_error);
-
- g_object_unref (cipher);
-
- if (local_error != NULL) {
- g_object_unref (npart);
- goto exception;
- }
-
- g_object_unref (part);
- part = npart;
- }
-
- if (pgp_encrypt) {
- CamelMimePart *npart = camel_mime_part_new ();
-
- /* Check to see if we should encrypt to self.
- * NB gets removed immediately after use */
- if (account && account->pgp_encrypt_to_self && pgp_userid)
- g_ptr_array_add (recipients, g_strdup (pgp_userid));
-
- cipher = camel_gpg_context_new (session);
- if (account != NULL)
- camel_gpg_context_set_always_trust (
- CAMEL_GPG_CONTEXT (cipher),
- account->pgp_always_trust);
-
- camel_cipher_context_encrypt_sync (
- cipher, pgp_userid, recipients,
- part, npart, cancellable, &local_error);
-
- g_object_unref (cipher);
-
- if (account && account->pgp_encrypt_to_self && pgp_userid)
- g_ptr_array_set_size (recipients, recipients->len - 1);
-
- if (local_error != NULL) {
- g_object_unref (npart);
- goto exception;
- }
-
- g_object_unref (part);
- part = npart;
- }
-
- if (from)
- g_object_unref (from);
-
- current = camel_medium_get_content (CAMEL_MEDIUM (part));
- g_object_ref (current);
- g_object_unref (part);
- }
-
-#if defined (HAVE_NSS)
- if (smime_sign || smime_encrypt) {
- CamelInternetAddress *from = NULL;
- CamelCipherContext *cipher;
-
- part = camel_mime_part_new ();
- camel_medium_set_content ((CamelMedium *)part, current);
- if (current == plain)
- camel_mime_part_set_encoding (part, plain_encoding);
- g_object_unref (current);
-
- if (smime_sign && (account == NULL ||
- account->smime_sign_key == NULL ||
- account->smime_sign_key[0] == 0)) {
- g_set_error (
- &local_error,
- CAMEL_ERROR, CAMEL_ERROR_GENERIC,
- _("Cannot sign outgoing message: "
- "No signing certificate set for "
- "this account"));
- goto exception;
- }
-
- if (smime_encrypt && (account == NULL ||
- account->smime_sign_key == NULL ||
- account->smime_sign_key[0] == 0)) {
- g_set_error (
- &local_error,
- CAMEL_ERROR, CAMEL_ERROR_GENERIC,
- _("Cannot encrypt outgoing message: "
- "No encryption certificate set for "
- "this account"));
- goto exception;
- }
-
- if (smime_sign) {
- CamelMimePart *npart = camel_mime_part_new ();
-
- cipher = camel_smime_context_new (session);
-
- /* if we're also encrypting, envelope-sign rather than clear-sign */
- if (smime_encrypt) {
- camel_smime_context_set_sign_mode (
- (CamelSMIMEContext *) cipher,
- CAMEL_SMIME_SIGN_ENVELOPED);
- camel_smime_context_set_encrypt_key (
- (CamelSMIMEContext *) cipher,
- TRUE, account->smime_encrypt_key);
- } else if (account &&
- account->smime_encrypt_key &&
- *account->smime_encrypt_key) {
- camel_smime_context_set_encrypt_key (
- (CamelSMIMEContext *) cipher,
- TRUE, account->smime_encrypt_key);
- }
-
- camel_cipher_context_sign_sync (
- cipher, account->smime_sign_key,
- account_hash_algo_to_camel_hash (
- (account != NULL) ?
- e_account_get_string (account,
- E_ACCOUNT_SMIME_HASH_ALGORITHM) : NULL),
- part, npart, cancellable, &local_error);
-
- g_object_unref (cipher);
-
- if (local_error != NULL) {
- g_object_unref (npart);
- goto exception;
- }
-
- g_object_unref (part);
- part = npart;
- }
-
- if (smime_encrypt) {
-
- /* check to see if we should encrypt to self, NB removed after use */
- if (account->smime_encrypt_to_self)
- g_ptr_array_add (
- recipients, g_strdup (
- account->smime_encrypt_key));
-
- cipher = camel_smime_context_new (session);
- camel_smime_context_set_encrypt_key (
- (CamelSMIMEContext *) cipher, TRUE,
- account->smime_encrypt_key);
-
- camel_cipher_context_encrypt_sync (
- cipher, NULL, recipients, part,
- (CamelMimePart *) new, cancellable,
- &local_error);
-
- g_object_unref (cipher);
-
- if (local_error != NULL)
- goto exception;
-
- if (account->smime_encrypt_to_self)
- g_ptr_array_set_size (recipients, recipients->len - 1);
- }
-
- if (from)
- g_object_unref (from);
-
- /* we replaced the message directly, we don't want to do reparenting foo */
- if (smime_encrypt) {
- g_object_unref (part);
- goto skip_content;
- } else {
- current = camel_medium_get_content ((CamelMedium *)part);
- g_object_ref (current);
- g_object_unref (part);
- }
- }
-#endif /* HAVE_NSS */
-
- camel_medium_set_content (CAMEL_MEDIUM (new), current);
- if (current == plain)
- camel_mime_part_set_encoding (CAMEL_MIME_PART (new), plain_encoding);
- g_object_unref (current);
+ /* Run any blocking operations in a separate thread. */
+ if (context->need_thread)
+ g_simple_async_result_run_in_thread (
+ simple, (GSimpleAsyncThreadFunc)
+ composer_build_message_thread,
+ io_priority, cancellable);
+ else
+ g_simple_async_result_complete (simple);
-#if defined (HAVE_NSS)
-skip_content:
-#endif
- if (recipients) {
- for (i=0; i<recipients->len; i++)
- g_free (recipients->pdata[i]);
- g_ptr_array_free (recipients, TRUE);
- }
+ g_object_unref (simple);
+}
- /* Attach whether this message was written in HTML */
- camel_medium_set_header (
- CAMEL_MEDIUM (new), "X-Evolution-Format",
- html_content ? "text/html" : "text/plain");
+static CamelMimeMessage *
+composer_build_message_finish (EMsgComposer *composer,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *context;
- return new;
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (composer), composer_build_message), NULL);
-exception:
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ context = g_simple_async_result_get_op_res_gpointer (simple);
- if (part != CAMEL_MIME_PART (new))
- g_object_unref (part);
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
- g_object_unref (new);
+ /* Finalize some details before returning. */
- if (recipients) {
- for (i=0; i<recipients->len; i++)
- g_free (recipients->pdata[i]);
- g_ptr_array_free (recipients, TRUE);
- }
+ if (!context->skip_content)
+ camel_medium_set_content (
+ CAMEL_MEDIUM (context->message),
+ context->top_level_part);
- g_propagate_error (error, local_error);
+ if (context->top_level_part == context->text_plain_part)
+ camel_mime_part_set_encoding (
+ CAMEL_MIME_PART (context->message),
+ context->plain_encoding);
- return NULL;
+ return g_object_ref (context->message);
}
/* Signatures */
@@ -1678,6 +1858,11 @@ msg_composer_delete_event_cb (EMsgComposer *composer)
shell = e_msg_composer_get_shell (composer);
+ /* If the "async" action group is insensitive, it means an
+ * asynchronous operation is in progress. Block the event. */
+ if (!gtk_action_group_get_sensitive (composer->priv->async_actions))
+ return TRUE;
+
if (g_list_length (e_shell_get_watched_windows (shell)) == 1) {
/* This is the last watched window, use the quit
* mechanism to have a draft saved properly */
@@ -2219,6 +2404,56 @@ msg_composer_object_deleted (GtkhtmlEditor *editor)
gtkhtml_editor_set_paragraph_data (editor, "signature", "0");
}
+static gboolean
+msg_composer_presend (EMsgComposer *composer)
+{
+ /* This keeps the signal accumulator at TRUE. */
+ return TRUE;
+}
+
+static void
+msg_composer_submit_alert (EAlertSink *alert_sink,
+ EAlert *alert)
+{
+ EMsgComposerPrivate *priv;
+ EAlertBar *alert_bar;
+ GtkWidget *dialog;
+ GtkWindow *parent;
+
+ priv = E_MSG_COMPOSER_GET_PRIVATE (alert_sink);
+
+ switch (e_alert_get_message_type (alert)) {
+ case GTK_MESSAGE_INFO:
+ case GTK_MESSAGE_WARNING:
+ case GTK_MESSAGE_ERROR:
+ alert_bar = E_ALERT_BAR (priv->alert_bar);
+ e_alert_bar_add_alert (alert_bar, alert);
+ break;
+
+ default:
+ parent = GTK_WINDOW (alert_sink);
+ dialog = e_alert_dialog_new (parent, alert);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ break;
+ }
+}
+
+static gboolean
+msg_composer_accumulator_false_abort (GSignalInvocationHint *ihint,
+ GValue *return_accu,
+ const GValue *handler_return,
+ gpointer dummy)
+{
+ gboolean v_boolean;
+
+ v_boolean = g_value_get_boolean (handler_return);
+ g_value_set_boolean (return_accu, v_boolean);
+
+ /* FALSE means abort the signal emission. */
+ return v_boolean;
+}
+
static void
e_msg_composer_class_init (EMsgComposerClass *class)
{
@@ -2250,6 +2485,8 @@ e_msg_composer_class_init (EMsgComposerClass *class)
editor_class->link_clicked = msg_composer_link_clicked;
editor_class->object_deleted = msg_composer_object_deleted;
+ class->presend = msg_composer_presend;
+
g_object_class_install_property (
object_class,
PROP_FOCUS_TRACKER,
@@ -2271,30 +2508,54 @@ e_msg_composer_class_init (EMsgComposerClass *class)
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
+ signals[PRESEND] = g_signal_new (
+ "presend",
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (EMsgComposerClass, presend),
+ msg_composer_accumulator_false_abort,
+ NULL,
+ e_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN, 0);
+
signals[SEND] = g_signal_new (
"send",
G_OBJECT_CLASS_TYPE (class),
G_SIGNAL_RUN_LAST,
- 0, NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ G_STRUCT_OFFSET (EMsgComposerClass, send),
+ NULL, NULL,
+ e_marshal_VOID__OBJECT_OBJECT,
+ G_TYPE_NONE, 2,
+ CAMEL_TYPE_MIME_MESSAGE,
+ E_TYPE_ACTIVITY);
signals[SAVE_DRAFT] = g_signal_new (
"save-draft",
G_OBJECT_CLASS_TYPE (class),
G_SIGNAL_RUN_LAST,
- 0, NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ G_STRUCT_OFFSET (EMsgComposerClass, save_draft),
+ NULL, NULL,
+ e_marshal_VOID__OBJECT_OBJECT,
+ G_TYPE_NONE, 2,
+ CAMEL_TYPE_MIME_MESSAGE,
+ E_TYPE_ACTIVITY);
signals[PRINT] = g_signal_new (
"print",
G_OBJECT_CLASS_TYPE (class),
G_SIGNAL_RUN_LAST,
0, NULL, NULL,
- g_cclosure_marshal_VOID__ENUM,
- G_TYPE_NONE, 1,
- GTK_TYPE_PRINT_OPERATION_ACTION);
+ e_marshal_VOID__ENUM_OBJECT_OBJECT,
+ G_TYPE_NONE, 3,
+ GTK_TYPE_PRINT_OPERATION_ACTION,
+ CAMEL_TYPE_MIME_MESSAGE,
+ E_TYPE_ACTIVITY);
+}
+
+static void
+e_msg_composer_alert_sink_init (EAlertSinkInterface *interface)
+{
+ interface->submit_alert = msg_composer_submit_alert;
}
static void
@@ -3214,6 +3475,51 @@ e_msg_composer_get_shell (EMsgComposer *composer)
return E_SHELL (composer->priv->shell);
}
+static void
+msg_composer_send_cb (EMsgComposer *composer,
+ GAsyncResult *result,
+ AsyncContext *context)
+{
+ CamelMimeMessage *message;
+ GtkhtmlEditor *editor;
+ GError *error = NULL;
+
+ message = e_msg_composer_get_message_finish (composer, result, &error);
+
+ /* Ignore cancellations. */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warn_if_fail (message == NULL);
+ async_context_free (context);
+ g_error_free (error);
+ return;
+ }
+
+ if (error != NULL) {
+ g_warn_if_fail (message == NULL);
+ async_context_free (context);
+ e_alert_submit (
+ GTK_WIDGET (composer),
+ "mail-composer:no-build-message",
+ error->message, NULL);
+ g_error_free (error);
+ return;
+ }
+
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ g_signal_emit (
+ composer, signals[SEND], 0,
+ message, context->activity);
+
+ g_object_unref (message);
+
+ async_context_free (context);
+
+ /* XXX This should be elsewhere. */
+ editor = GTKHTML_EDITOR (composer);
+ gtkhtml_editor_set_changed (editor, FALSE);
+}
+
/**
* e_msg_composer_send:
* @composer: an #EMsgComposer
@@ -3223,15 +3529,84 @@ e_msg_composer_get_shell (EMsgComposer *composer)
void
e_msg_composer_send (EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
+ AsyncContext *context;
+ GtkAction *action;
+ EActivityBar *activity_bar;
+ GCancellable *cancellable;
+ gboolean proceed_with_send = TRUE;
+ const gchar *icon_name;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- editor = GTKHTML_EDITOR (composer);
+ /* This gives the user a chance to abort the send. */
+ g_signal_emit (composer, signals[PRESEND], 0, &proceed_with_send);
+
+ if (!proceed_with_send)
+ return;
+
+ context = g_slice_new0 (AsyncContext);
+ context->activity = e_composer_activity_new (composer);
+
+ cancellable = camel_operation_new ();
+ e_activity_set_cancellable (context->activity, cancellable);
+ g_object_unref (cancellable);
+
+ action = ACTION (SEND);
+ icon_name = gtk_action_get_icon_name (action);
+ e_activity_set_icon_name (context->activity, icon_name);
+
+ activity_bar = E_ACTIVITY_BAR (composer->priv->activity_bar);
+ e_activity_bar_set_activity (activity_bar, context->activity);
+
+ e_msg_composer_get_message (
+ composer, G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) msg_composer_send_cb,
+ context);
+}
+
+static void
+msg_composer_save_draft_cb (EMsgComposer *composer,
+ GAsyncResult *result,
+ AsyncContext *context)
+{
+ CamelMimeMessage *message;
+ GtkhtmlEditor *editor;
+ GError *error = NULL;
+
+ message = e_msg_composer_get_message_draft_finish (
+ composer, result, &error);
+
+ /* Ignore cancellations. */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warn_if_fail (message == NULL);
+ async_context_free (context);
+ g_error_free (error);
+ return;
+ }
+
+ if (error != NULL) {
+ g_warn_if_fail (message == NULL);
+ async_context_free (context);
+ e_alert_submit (
+ GTK_WIDGET (composer),
+ "mail-composer:no-build-message",
+ error->message, NULL);
+ g_error_free (error);
+ return;
+ }
+
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ g_signal_emit (
+ composer, signals[SAVE_DRAFT], 0,
+ message, context->activity);
+
+ g_object_unref (message);
- g_signal_emit (composer, signals[SEND], 0);
+ async_context_free (context);
/* XXX This should be elsewhere. */
+ editor = GTKHTML_EDITOR (composer);
gtkhtml_editor_set_changed (editor, FALSE);
}
@@ -3244,32 +3619,113 @@ e_msg_composer_send (EMsgComposer *composer)
void
e_msg_composer_save_draft (EMsgComposer *composer)
{
- GtkhtmlEditor *editor;
+ AsyncContext *context;
+ GtkAction *action;
+ EActivityBar *activity_bar;
+ GCancellable *cancellable;
+ const gchar *icon_name;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- editor = GTKHTML_EDITOR (composer);
+ context = g_slice_new0 (AsyncContext);
+ context->activity = e_composer_activity_new (composer);
- g_signal_emit (composer, signals[SAVE_DRAFT], 0);
+ cancellable = camel_operation_new ();
+ e_activity_set_cancellable (context->activity, cancellable);
+ g_object_unref (cancellable);
- /* XXX This should be elsewhere. */
- gtkhtml_editor_set_changed (editor, FALSE);
+ action = ACTION (SAVE_DRAFT);
+ icon_name = gtk_action_get_icon_name (action);
+ e_activity_set_icon_name (context->activity, icon_name);
+
+ activity_bar = E_ACTIVITY_BAR (composer->priv->activity_bar);
+ e_activity_bar_set_activity (activity_bar, context->activity);
+
+ e_msg_composer_get_message_draft (
+ composer, G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) msg_composer_save_draft_cb,
+ context);
+}
+
+static void
+msg_composer_print_cb (EMsgComposer *composer,
+ GAsyncResult *result,
+ AsyncContext *context)
+{
+ CamelMimeMessage *message;
+ GError *error = NULL;
+
+ message = e_msg_composer_get_message_print_finish (
+ composer, result, &error);
+
+ /* Ignore cancellations. */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warn_if_fail (message == NULL);
+ async_context_free (context);
+ g_error_free (error);
+ return;
+ }
+
+ if (error != NULL) {
+ g_warn_if_fail (message == NULL);
+ async_context_free (context);
+ e_alert_submit (
+ GTK_WIDGET (composer),
+ "mail-composer:no-build-message",
+ error->message, NULL);
+ g_error_free (error);
+ return;
+ }
+
+ g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
+
+ g_signal_emit (
+ composer, signals[PRINT], 0,
+ context->print_action, message, context->activity);
+
+ g_object_unref (message);
+
+ async_context_free (context);
}
/**
* e_msg_composer_print:
* @composer: an #EMsgComposer
- * @action: the print action to start
+ * @print_action: the print action to start
*
* Print the message in @composer.
**/
void
e_msg_composer_print (EMsgComposer *composer,
- GtkPrintOperationAction action)
+ GtkPrintOperationAction print_action)
{
+ AsyncContext *context;
+ GtkAction *action;
+ EActivityBar *activity_bar;
+ GCancellable *cancellable;
+ const gchar *icon_name;
+
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- g_signal_emit (composer, signals[PRINT], 0, action);
+ context = g_slice_new0 (AsyncContext);
+ context->activity = e_composer_activity_new (composer);
+ context->print_action = print_action;
+
+ cancellable = camel_operation_new ();
+ e_activity_set_cancellable (context->activity, cancellable);
+ g_object_unref (cancellable);
+
+ action = ACTION (PRINT);
+ icon_name = gtk_action_get_icon_name (action);
+ e_activity_set_icon_name (context->activity, icon_name);
+
+ activity_bar = E_ACTIVITY_BAR (composer->priv->activity_bar);
+ e_activity_bar_set_activity (activity_bar, context->activity);
+
+ e_msg_composer_get_message_print (
+ composer, G_PRIORITY_DEFAULT, cancellable,
+ (GAsyncReadyCallback) msg_composer_print_cb,
+ context);
}
static GList *
@@ -3542,81 +3998,164 @@ e_msg_composer_set_body (EMsgComposer *composer,
/**
* e_msg_composer_add_header:
- * @composer: a composer object
- * @name: the header name
- * @value: the header value
+ * @composer: an #EMsgComposer
+ * @name: the header's name
+ * @value: the header's value
*
- * Adds a header with @name and @value to the message. This header
- * may not be displayed by the composer, but will be included in
- * the message it outputs.
+ * Adds a new custom header created from @name and @value. The header
+ * is not shown in the user interface but will be added to the resulting
+ * MIME message when sending or saving.
**/
void
e_msg_composer_add_header (EMsgComposer *composer,
const gchar *name,
const gchar *value)
{
- EMsgComposerPrivate *p = composer->priv;
+ EMsgComposerPrivate *priv;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
g_return_if_fail (name != NULL);
g_return_if_fail (value != NULL);
- g_ptr_array_add (p->extra_hdr_names, g_strdup (name));
- g_ptr_array_add (p->extra_hdr_values, g_strdup (value));
+ priv = composer->priv;
+
+ g_ptr_array_add (priv->extra_hdr_names, g_strdup (name));
+ g_ptr_array_add (priv->extra_hdr_values, g_strdup (value));
}
/**
- * e_msg_composer_modify_header :
- * @composer : a composer object
- * @name: the header name
- * @change_value: the header value to put in place of the previous
- * value
+ * e_msg_composer_set_header:
+ * @composer: an #EMsgComposer
+ * @name: the header's name
+ * @value: the header's value
*
- * Searches for a header with name=@name ,if found it removes
- * that header and adds a new header with the @name and @change_value .
- * If not found then it creates a new header with @name and @change_value .
+ * Replaces all custom headers matching @name that were added with
+ * e_msg_composer_add_header() or e_msg_composer_set_header(), with
+ * a new custom header created from @name and @value. The header is
+ * not shown in the user interface but will be added to the resulting
+ * MIME message when sending or saving.
**/
void
-e_msg_composer_modify_header (EMsgComposer *composer,
- const gchar *name,
- const gchar *change_value)
+e_msg_composer_set_header (EMsgComposer *composer,
+ const gchar *name,
+ const gchar *value)
{
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
g_return_if_fail (name != NULL);
- g_return_if_fail (change_value != NULL);
+ g_return_if_fail (value != NULL);
e_msg_composer_remove_header (composer, name);
- e_msg_composer_add_header (composer, name, change_value);
+ e_msg_composer_add_header (composer, name, value);
}
/**
- * e_msg_composer_modify_header :
- * @composer : a composer object
- * @name: the header name
+ * e_msg_composer_remove_header:
+ * @composer: an #EMsgComposer
+ * @name: the header's name
*
- * Searches for the header and if found it removes it .
+ * Removes all custom headers matching @name that were added with
+ * e_msg_composer_add_header() or e_msg_composer_set_header().
**/
void
e_msg_composer_remove_header (EMsgComposer *composer,
const gchar *name)
{
- EMsgComposerPrivate *p;
- gint i;
+ EMsgComposerPrivate *priv;
+ guint ii;
g_return_if_fail (E_IS_MSG_COMPOSER (composer));
g_return_if_fail (name != NULL);
- p = composer->priv;
+ priv = composer->priv;
- for (i = 0; i < p->extra_hdr_names->len; i++) {
- if (strcmp (p->extra_hdr_names->pdata[i], name) == 0) {
- g_ptr_array_remove_index (p->extra_hdr_names, i);
- g_ptr_array_remove_index (p->extra_hdr_values, i);
+ for (ii = 0; ii < priv->extra_hdr_names->len; ii++) {
+ if (g_strcmp0 (priv->extra_hdr_names->pdata[ii], name) == 0) {
+ g_free (priv->extra_hdr_names->pdata[ii]);
+ g_free (priv->extra_hdr_values->pdata[ii]);
+ g_ptr_array_remove_index (priv->extra_hdr_names, ii);
+ g_ptr_array_remove_index (priv->extra_hdr_values, ii);
}
}
}
/**
+ * e_msg_composer_set_draft_headers:
+ * @composer: an #EMsgComposer
+ * @folder_uri: folder URI of the last saved draft
+ * @message_uid: message UID of the last saved draft
+ *
+ * Add special X-Evolution-Draft headers to remember the most recently
+ * saved draft message, even across Evolution sessions. These headers
+ * can be used to mark the draft message for deletion after saving a
+ * newer draft or sending the composed message.
+ **/
+void
+e_msg_composer_set_draft_headers (EMsgComposer *composer,
+ const gchar *folder_uri,
+ const gchar *message_uid)
+{
+ const gchar *header_name;
+
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+ g_return_if_fail (folder_uri != NULL);
+ g_return_if_fail (message_uid != NULL);
+
+ header_name = "X-Evolution-Draft-Folder";
+ e_msg_composer_set_header (composer, header_name, folder_uri);
+
+ header_name = "X-Evolution-Draft-Message";
+ e_msg_composer_set_header (composer, header_name, message_uid);
+}
+
+/**
+ * e_msg_composer_set_source_headers:
+ * @composer: an #EMsgComposer
+ * @folder_uri: folder URI of the source message
+ * @message_uid: message UID of the source message
+ * @flags: flags to set on the source message after sending
+ *
+ * Add special X-Evolution-Source headers to remember the message being
+ * forwarded or replied to, even across Evolution sessions. These headers
+ * can be used to set appropriate flags on the source message after sending
+ * the composed message.
+ **/
+void
+e_msg_composer_set_source_headers (EMsgComposer *composer,
+ const gchar *folder_uri,
+ const gchar *message_uid,
+ CamelMessageFlags flags)
+{
+ GString *buffer;
+ const gchar *header_name;
+
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
+ g_return_if_fail (folder_uri != NULL);
+ g_return_if_fail (message_uid != NULL);
+
+ buffer = g_string_sized_new (32);
+
+ if (flags & CAMEL_MESSAGE_ANSWERED)
+ g_string_append (buffer, "ANSWERED ");
+ if (flags & CAMEL_MESSAGE_ANSWERED_ALL)
+ g_string_append (buffer, "ANSWERED_ALL ");
+ if (flags & CAMEL_MESSAGE_FORWARDED)
+ g_string_append (buffer, "FORWARDED ");
+ if (flags & CAMEL_MESSAGE_SEEN)
+ g_string_append (buffer, "SEEN ");
+
+ header_name = "X-Evolution-Source-Folder";
+ e_msg_composer_set_header (composer, header_name, folder_uri);
+
+ header_name = "X-Evolution-Source-Message";
+ e_msg_composer_set_header (composer, header_name, message_uid);
+
+ header_name = "X-Evolution-Source-Flags";
+ e_msg_composer_set_header (composer, header_name, buffer->str);
+
+ g_string_free (buffer, TRUE);
+}
+
+/**
* e_msg_composer_attach:
* @composer: a composer object
* @mime_part: the #CamelMimePart to attach
@@ -3745,207 +4284,211 @@ e_msg_composer_add_inline_image_from_mime_part (EMsgComposer *composer,
g_strdup (location), part);
}
+static void
+composer_get_message_ready (EMsgComposer *composer,
+ GAsyncResult *result,
+ GSimpleAsyncResult *simple)
+{
+ CamelMimeMessage *message;
+ GError *error = NULL;
+
+ message = composer_build_message_finish (composer, result, &error);
+
+ if (message != NULL)
+ g_simple_async_result_set_op_res_gpointer (
+ simple, message, (GDestroyNotify) g_object_unref);
+
+ if (error != NULL) {
+ g_warn_if_fail (message == NULL);
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ }
+
+ g_simple_async_result_complete (simple);
+
+ g_object_unref (simple);
+}
+
/**
* e_msg_composer_get_message:
- * @composer: A message composer widget
+ * @composer: an #EMsgComposer
*
- * Retrieve the message edited by the user as a CamelMimeMessage. The
- * CamelMimeMessage object is created on the fly; subsequent calls to this
+ * Retrieve the message edited by the user as a #CamelMimeMessage. The
+ * #CamelMimeMessage object is created on the fly; subsequent calls to this
* function will always create new objects from scratch.
- *
- * Returns: A pointer to the new CamelMimeMessage object
**/
-CamelMimeMessage *
+void
e_msg_composer_get_message (EMsgComposer *composer,
- gboolean save_html_object_data,
+ gint io_priority,
GCancellable *cancellable,
- GError **error)
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- EAttachmentView *view;
- EAttachmentStore *store;
- GtkhtmlEditor *editor;
- gboolean html_content;
+ GSimpleAsyncResult *simple;
+ GtkAction *action;
+ ComposerFlags flags = 0;
- g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- view = e_msg_composer_get_attachment_view (composer);
- store = e_attachment_view_get_store (view);
+ simple = g_simple_async_result_new (
+ G_OBJECT (composer), callback,
+ user_data, e_msg_composer_get_message);
- if (e_attachment_store_get_num_loading (store) > 0) {
- if (!emcu_prompt_user (GTK_WINDOW (composer), NULL,
- "mail-composer:ask-send-message-pending-download", NULL)) {
- return NULL;
- }
- }
+ if (gtkhtml_editor_get_html_mode (GTKHTML_EDITOR (composer)))
+ flags |= COMPOSER_FLAG_HTML_CONTENT;
- editor = GTKHTML_EDITOR (composer);
- html_content = gtkhtml_editor_get_html_mode (editor);
+ action = ACTION (PRIORITIZE_MESSAGE);
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ flags |= COMPOSER_FLAG_PRIORITIZE_MESSAGE;
- return build_message (
- composer, html_content,
- save_html_object_data, cancellable, error);
-}
+ action = ACTION (REQUEST_READ_RECEIPT);
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ flags |= COMPOSER_FLAG_REQUEST_READ_RECEIPT;
-static gchar *
-msg_composer_get_message_print_helper (EMsgComposer *composer,
- gboolean html_content)
-{
- GtkToggleAction *action;
- GString *string;
+ action = ACTION (PGP_SIGN);
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ flags |= COMPOSER_FLAG_PGP_SIGN;
- string = g_string_sized_new (128);
+ action = ACTION (PGP_ENCRYPT);
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ flags |= COMPOSER_FLAG_PGP_ENCRYPT;
- if (html_content)
- g_string_append (string, "text/html");
- else
- g_string_append (string, "text/plain");
-
- action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
- if (gtk_toggle_action_get_active (action))
- g_string_append (string, ", pgp-sign");
- gtk_toggle_action_set_active (action, FALSE);
-
- action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT));
- if (gtk_toggle_action_get_active (action))
- g_string_append (string, ", pgp-encrypt");
- gtk_toggle_action_set_active (action, FALSE);
+#ifdef HAVE_NSS
+ action = ACTION (SMIME_SIGN);
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ flags |= COMPOSER_FLAG_SMIME_SIGN;
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN));
- if (gtk_toggle_action_get_active (action))
- g_string_append (string, ", smime-sign");
- gtk_toggle_action_set_active (action, FALSE);
-
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT));
- if (gtk_toggle_action_get_active (action))
- g_string_append (string, ", smime-encrypt");
- gtk_toggle_action_set_active (action, FALSE);
+ action = ACTION (SMIME_ENCRYPT);
+ if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)))
+ flags |= COMPOSER_FLAG_SMIME_ENCRYPT;
+#endif
- return g_string_free (string, FALSE);
+ composer_build_message (
+ composer, flags, io_priority,
+ cancellable, (GAsyncReadyCallback)
+ composer_get_message_ready, simple);
}
CamelMimeMessage *
-e_msg_composer_get_message_print (EMsgComposer *composer,
- gboolean save_html_object_data,
- GCancellable *cancellable)
+e_msg_composer_get_message_finish (EMsgComposer *composer,
+ GAsyncResult *result,
+ GError **error)
{
- EShell *shell;
- GtkhtmlEditor *editor;
- EMsgComposer *temp_composer;
- CamelMimeMessage *msg;
- gboolean html_content;
- gchar *flags;
+ GSimpleAsyncResult *simple;
+ CamelMimeMessage *message;
- g_return_val_if_fail (E_IS_MSG_COMPOSER (composer), NULL);
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (composer),
+ e_msg_composer_get_message), NULL);
- editor = GTKHTML_EDITOR (composer);
- html_content = gtkhtml_editor_get_html_mode (editor);
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ message = g_simple_async_result_get_op_res_gpointer (simple);
- msg = build_message (
- composer, html_content, save_html_object_data,
- cancellable, NULL);
- if (msg == NULL)
+ if (g_simple_async_result_propagate_error (simple, error))
return NULL;
- shell = e_msg_composer_get_shell (composer);
- temp_composer = e_msg_composer_new_with_message (
- shell, msg, cancellable);
- g_object_unref (msg);
-
- /* Override composer flags. */
- flags = msg_composer_get_message_print_helper (
- temp_composer, html_content);
-
- msg = build_message (
- temp_composer, TRUE, save_html_object_data,
- cancellable, NULL);
- if (msg != NULL)
- camel_medium_set_header (
- CAMEL_MEDIUM (msg), "X-Evolution-Format", flags);
-
- gtk_widget_destroy (GTK_WIDGET (temp_composer));
- g_free (flags);
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
- return msg;
+ return g_object_ref (message);
}
-CamelMimeMessage *
-e_msg_composer_get_message_draft (EMsgComposer *composer,
+void
+e_msg_composer_get_message_print (EMsgComposer *composer,
+ gint io_priority,
GCancellable *cancellable,
- GError **error)
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GtkhtmlEditor *editor;
- EComposerHeaderTable *table;
- GtkToggleAction *action;
- CamelMimeMessage *msg;
- EAccount *account;
- gboolean html_content;
- gboolean pgp_encrypt;
- gboolean pgp_sign;
- gboolean smime_encrypt;
- gboolean smime_sign;
- GString *flags;
+ GSimpleAsyncResult *simple;
+ ComposerFlags flags = 0;
- editor = GTKHTML_EDITOR (composer);
- table = e_msg_composer_get_header_table (composer);
- html_content = gtkhtml_editor_get_html_mode (editor);
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
- pgp_sign = gtk_toggle_action_get_active (action);
- gtk_toggle_action_set_active (action, FALSE);
+ simple = g_simple_async_result_new (
+ G_OBJECT (composer), callback,
+ user_data, e_msg_composer_get_message_print);
- action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT));
- pgp_encrypt = gtk_toggle_action_get_active (action);
- gtk_toggle_action_set_active (action, FALSE);
+ flags |= COMPOSER_FLAG_HTML_CONTENT;
+ flags |= COMPOSER_FLAG_SAVE_OBJECT_DATA;
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN));
- smime_sign = gtk_toggle_action_get_active (action);
- gtk_toggle_action_set_active (action, FALSE);
+ composer_build_message (
+ composer, flags, io_priority,
+ cancellable, (GAsyncReadyCallback)
+ composer_get_message_ready, simple);
+}
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT));
- smime_encrypt = gtk_toggle_action_get_active (action);
- gtk_toggle_action_set_active (action, FALSE);
+CamelMimeMessage *
+e_msg_composer_get_message_print_finish (EMsgComposer *composer,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ CamelMimeMessage *message;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (composer),
+ e_msg_composer_get_message_print), NULL);
- msg = build_message (composer, TRUE, TRUE, cancellable, error);
- if (msg == NULL)
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ message = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
return NULL;
- action = GTK_TOGGLE_ACTION (ACTION (PGP_SIGN));
- gtk_toggle_action_set_active (action, pgp_sign);
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
- action = GTK_TOGGLE_ACTION (ACTION (PGP_ENCRYPT));
- gtk_toggle_action_set_active (action, pgp_encrypt);
+ return g_object_ref (message);
+}
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_SIGN));
- gtk_toggle_action_set_active (action, smime_sign);
+void
+e_msg_composer_get_message_draft (EMsgComposer *composer,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ ComposerFlags flags = 0;
- action = GTK_TOGGLE_ACTION (ACTION (SMIME_ENCRYPT));
- gtk_toggle_action_set_active (action, smime_encrypt);
+ g_return_if_fail (E_IS_MSG_COMPOSER (composer));
- /* Attach account info to the draft. */
- account = e_composer_header_table_get_account (table);
- if (account && account->name)
- camel_medium_set_header (
- CAMEL_MEDIUM (msg),
- "X-Evolution-Account", account->uid);
+ simple = g_simple_async_result_new (
+ G_OBJECT (composer), callback,
+ user_data, e_msg_composer_get_message_draft);
+
+ if (gtkhtml_editor_get_html_mode (GTKHTML_EDITOR (composer)))
+ flags |= COMPOSER_FLAG_HTML_CONTENT;
- flags = g_string_new (html_content ? "text/html" : "text/plain");
+ composer_build_message (
+ composer, flags, io_priority,
+ cancellable, (GAsyncReadyCallback)
+ composer_get_message_ready, simple);
+}
- /* This should probably only save the setting if it is
- * different from the from-account default? */
- if (pgp_sign)
- g_string_append (flags, ", pgp-sign");
- if (pgp_encrypt)
- g_string_append (flags, ", pgp-encrypt");
- if (smime_sign)
- g_string_append (flags, ", smime-sign");
- if (smime_encrypt)
- g_string_append (flags, ", smime-encrypt");
+CamelMimeMessage *
+e_msg_composer_get_message_draft_finish (EMsgComposer *composer,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ CamelMimeMessage *message;
- camel_medium_set_header (
- CAMEL_MEDIUM (msg), "X-Evolution-Format", flags->str);
- g_string_free (flags, TRUE);
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (composer),
+ e_msg_composer_get_message_draft), NULL);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ message = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return NULL;
+
+ g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
- return msg;
+ return g_object_ref (message);
}
/**
@@ -4153,50 +4696,6 @@ e_msg_composer_can_close (EMsgComposer *composer,
return res;
}
-EMsgComposer *
-e_msg_composer_load_from_file (EShell *shell,
- const gchar *filename,
- GCancellable *cancellable)
-{
- CamelStream *stream;
- CamelMimeMessage *message;
- EMsgComposer *composer;
-
- g_return_val_if_fail (E_IS_SHELL (shell), NULL);
- g_return_val_if_fail (filename != NULL, NULL);
-
- stream = camel_stream_fs_new_with_name (
- filename, O_RDONLY, 0, NULL);
- if (stream == NULL)
- return NULL;
-
- message = camel_mime_message_new ();
- camel_data_wrapper_construct_from_stream_sync (
- CAMEL_DATA_WRAPPER (message), stream, NULL, NULL);
- g_object_unref (stream);
-
- composer = e_msg_composer_new_with_message (
- shell, message, cancellable);
- if (composer != NULL)
- gtk_widget_show (GTK_WIDGET (composer));
-
- return composer;
-}
-
-void
-e_msg_composer_set_alternative (EMsgComposer *composer,
- gboolean alt)
-{
- GtkhtmlEditor *editor;
-
- g_return_if_fail (E_IS_MSG_COMPOSER (composer));
-
- editor = GTKHTML_EDITOR (composer);
-
- composer->priv->is_alternative = alt;
- gtkhtml_editor_set_html_mode (editor, !alt);
-}
-
void
e_msg_composer_reply_indent (EMsgComposer *composer)
{
@@ -4330,20 +4829,3 @@ e_save_spell_languages (GList *spell_languages)
g_error_free (error);
}
}
-
-void
-e_msg_composer_set_mail_sent (EMsgComposer *composer,
- gboolean mail_sent)
-{
- g_return_if_fail (composer != NULL);
-
- composer->priv->mail_sent = mail_sent;
-}
-
-gboolean
-e_msg_composer_get_mail_sent (EMsgComposer *composer)
-{
- g_return_val_if_fail (composer != NULL, FALSE);
-
- return composer->priv->mail_sent;
-}
diff --git a/composer/e-msg-composer.h b/composer/e-msg-composer.h
index 866774ed9c..5cf781022e 100644
--- a/composer/e-msg-composer.h
+++ b/composer/e-msg-composer.h
@@ -66,6 +66,19 @@ struct _EMsgComposer {
struct _EMsgComposerClass {
GtkhtmlEditorClass parent_class;
+
+ /* Signals */
+ gboolean (*presend) (EMsgComposer *composer);
+ void (*print) (EMsgComposer *composer,
+ GtkPrintOperationAction print_action,
+ CamelMimeMessage *message,
+ EActivity *activity);
+ void (*save_draft) (EMsgComposer *composer,
+ CamelMimeMessage *message,
+ EActivity *activity);
+ void (*send) (EMsgComposer *composer,
+ CamelMimeMessage *message,
+ EActivity *activity);
};
GType e_msg_composer_get_type (void);
@@ -87,10 +100,7 @@ EShell * e_msg_composer_get_shell (EMsgComposer *composer);
void e_msg_composer_send (EMsgComposer *composer);
void e_msg_composer_save_draft (EMsgComposer *composer);
void e_msg_composer_print (EMsgComposer *composer,
- GtkPrintOperationAction action);
-
-void e_msg_composer_set_alternative (EMsgComposer *composer,
- gboolean alt);
+ GtkPrintOperationAction print_action);
void e_msg_composer_set_body_text (EMsgComposer *composer,
const gchar *text,
@@ -101,11 +111,20 @@ void e_msg_composer_set_body (EMsgComposer *composer,
void e_msg_composer_add_header (EMsgComposer *composer,
const gchar *name,
const gchar *value);
-void e_msg_composer_modify_header (EMsgComposer *composer,
+void e_msg_composer_set_header (EMsgComposer *composer,
const gchar *name,
const gchar *value);
void e_msg_composer_remove_header (EMsgComposer *composer,
const gchar *name);
+void e_msg_composer_set_draft_headers
+ (EMsgComposer *composer,
+ const gchar *folder_uri,
+ const gchar *message_uid);
+void e_msg_composer_set_source_headers
+ (EMsgComposer *composer,
+ const gchar *folder_uri,
+ const gchar *message_uid,
+ CamelMessageFlags flags);
void e_msg_composer_attach (EMsgComposer *composer,
CamelMimePart *mime_part);
CamelMimePart * e_msg_composer_add_inline_image_from_file
@@ -114,20 +133,37 @@ CamelMimePart * e_msg_composer_add_inline_image_from_file
void e_msg_composer_add_inline_image_from_mime_part
(EMsgComposer *composer,
CamelMimePart *part);
-CamelMimeMessage *
- e_msg_composer_get_message (EMsgComposer *composer,
- gboolean save_html_object_data,
+void e_msg_composer_get_message (EMsgComposer *composer,
+ gint io_priority,
GCancellable *cancellable,
- GError **error);
+ GAsyncReadyCallback callback,
+ gpointer user_data);
CamelMimeMessage *
- e_msg_composer_get_message_print
+ e_msg_composer_get_message_finish
(EMsgComposer *composer,
- gboolean save_html_object_data,
- GCancellable *cancellable);
+ GAsyncResult *result,
+ GError **error);
+void e_msg_composer_get_message_print
+ (EMsgComposer *composer,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
CamelMimeMessage *
- e_msg_composer_get_message_draft
+ e_msg_composer_get_message_print_finish
+ (EMsgComposer *composer,
+ GAsyncResult *result,
+ GError **error);
+void e_msg_composer_get_message_draft
(EMsgComposer *composer,
+ gint io_priority,
GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+CamelMimeMessage *
+ e_msg_composer_get_message_draft_finish
+ (EMsgComposer *composer,
+ GAsyncResult *result,
GError **error);
void e_msg_composer_show_sig_file (EMsgComposer *composer);
@@ -147,10 +183,6 @@ void e_msg_composer_request_close (EMsgComposer *composer);
gboolean e_msg_composer_can_close (EMsgComposer *composer,
gboolean can_save_draft);
-EMsgComposer * e_msg_composer_load_from_file (EShell *shell,
- const gchar *filename,
- GCancellable *cancellable);
-
void e_msg_composer_reply_indent (EMsgComposer *composer);
EComposerHeaderTable *
@@ -166,10 +198,6 @@ gboolean e_msg_composer_is_exiting (EMsgComposer *composer);
GList * e_load_spell_languages (void);
void e_save_spell_languages (GList *spell_languages);
-gboolean e_msg_composer_get_mail_sent (EMsgComposer *composer);
-void e_msg_composer_set_mail_sent (EMsgComposer *composer,
- gboolean mail_sent);
-
G_END_DECLS
#endif /* E_MSG_COMPOSER_H */
diff --git a/composer/mail-composer.error.xml b/composer/mail-composer.error.xml
index 262eee459c..90f0187b0e 100644
--- a/composer/mail-composer.error.xml
+++ b/composer/mail-composer.error.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<error-list domain="mail-composer">
- <error id="no-attach" type="error" modal="true">
+ <error id="no-attach" type="error">
<_primary>You cannot attach the file &quot;{0}&quot; to this message.</_primary>
<!--For Translators: '{1}' is the exception description,describing why the file could not be attached to the message -->
<secondary>{1}</secondary>
@@ -35,7 +35,7 @@
<button _label="_Send" response="GTK_RESPONSE_YES"/>
</error>
- <error id="exit-unsaved" modal="true" type="warning" default="GTK_RESPONSE_YES">
+ <error id="exit-unsaved" type="warning" default="GTK_RESPONSE_YES">
<_primary>Are you sure you want to discard the message, titled '{0}', you are composing?</_primary>
<_secondary>Closing this composer window will discard the message permanently, unless you choose to save the message in your Drafts folder. This will allow you to continue the message at a later date.</_secondary>
<button _label="_Discard Changes" response="GTK_RESPONSE_NO"/>
@@ -43,30 +43,39 @@
<button _label="_Save Draft" response="GTK_RESPONSE_YES"/>
</error>
- <error id="no-build-message" type="error" modal="true">
+ <error id="no-build-message" type="error">
<_primary>Could not create message.</_primary>
<_secondary>Because &quot;{0}&quot;, you may need to select different mail options.</_secondary>
</error>
- <error id="no-sig-file" type="warning" modal="true">
+ <error id="no-sig-file" type="warning">
<_primary>Could not read signature file &quot;{0}&quot;.</_primary>
<_secondary>Because &quot;{1}&quot;.</_secondary>
</error>
- <error id="all-accounts-deleted" type="warning" modal="true">
+ <error id="all-accounts-deleted" type="warning">
<_primary>All accounts have been removed.</_primary>
<_secondary>You need to configure an account before you can compose mail.</_secondary>
</error>
- <error id="no-address-control" type="error" modal="true">
- <_primary>Could not create composer window.</_primary>
- <_secondary>Unable to activate the address selector control.</_secondary>
+ <error id="append-to-outbox-error" type="error">
+ <_primary>An error occurred while saving to your Outbox folder.</_primary>
+ <_secondary>The reported error was &quot;{0}&quot;. The message has not been sent.</_secondary>
</error>
- <error id="no-editor-control" type="error" modal="true">
- <_primary>Could not create composer window.</_primary>
- <_secondary xml:space="preserve">Unable to activate the HTML editor control.
+ <error id="save-draft-error" type="error">
+ <_primary>An error occurred while saving to your Drafts folder.</_primary>
+ <_secondary>The reported error was &quot;{0}&quot;. The message has most likely not been saved.</_secondary>
+ </error>
+
+ <error id="send-error" type="error">
+ <_primary>An error occurred while sending.</_primary>
+ <_secondary>The reported error was &quot;{0}&quot;.</_secondary>
+ </error>
-Please make sure that you have the correct version of gtkhtml and libgtkhtml installed.</_secondary>
+ <error id="saved-to-outbox" type="info">
+ <_primary>Message saved to Outbox.</_primary>
+ <_secondary>Because you are working offline, the message has been saved to your local Outbox folder. When you are back online you can send the message by clicking the Send/Receive button in Evolution's toolbar.</_secondary>
</error>
+
</error-list>