diff options
Diffstat (limited to 'mail/em-composer-utils.c')
-rw-r--r-- | mail/em-composer-utils.c | 623 |
1 files changed, 623 insertions, 0 deletions
diff --git a/mail/em-composer-utils.c b/mail/em-composer-utils.c new file mode 100644 index 0000000000..e181c2cb33 --- /dev/null +++ b/mail/em-composer-utils.c @@ -0,0 +1,623 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast <fejj@ximian.com> + * + * Copyright 2003 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include "mail-mt.h" +#include "mail-ops.h" +#include "mail-tools.h" +#include "mail-config.h" +#include "mail-session.h" +#include "mail-send-recv.h" + +#include <e-util/e-dialog-utils.h> /* e_notice */ + +#include "em-utils.h" +#include "em-composer-utils.h" + +struct emcs_t { + unsigned int ref_count; + + CamelFolder *drafts_folder; + char *drafts_uid; + + CamelFolder *folder; + guint32 flags, set; + char *uid; +}; + +static struct emcs_t * +emcs_new (void) +{ + struct emcs_t *emcs; + + emcs = g_new (struct emcs_t, 1); + emcs->ref_count = 1; + emcs->drafts_folder = NULL; + emcs->drafts_uid = NULL; + emcs->folder = NULL; + emcs->flags = 0; + emcs->set = 0; + emcs->uid = NULL; + + return emcs; +} + +static void +free_emcs (struct emcs_t *emcs) +{ + if (emcs->drafts_folder) + camel_object_unref (emcs->drafts_folder); + g_free (emcs->drafts_uid); + + if (emcs->folder) + camel_object_unref (emcs->folder); + g_free (emcs->uid); + g_free (emcs); +} + +static void +emcs_ref (struct emcs_t *emcs) +{ + emcs->ref_count++; +} + +static void +emcs_unref (struct emcs_t *emcs) +{ + emcs->ref_count--; + if (emcs->ref_count == 0) + free_emcs (emcs); +} + +static void +composer_destroy_cb (gpointer user_data, GObject *deadbeef) +{ + emcs_unref (user_data); +} + +static gboolean +ask_confirm_for_unwanted_html_mail (EMsgComposer *composer, EDestination **recipients) +{ + gboolean show_again, res; + GConfClient *gconf; + GString *str; + int i; + + gconf = mail_config_get_gconf_client (); + + if (!gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/unwanted_html", NULL)) + return TRUE; + + /* FIXME: this wording sucks */ + str = g_string_new (_("You are sending an HTML-formatted message. Please make sure that\n" + "the following recipients are willing and able to receive HTML mail:\n")); + for (i = 0; recipients[i] != NULL; ++i) { + if (!e_destination_get_html_mail_pref (recipients[i])) { + const char *name; + + name = e_destination_get_textrep (recipients[i], FALSE); + + g_string_append_printf (str, " %s\n", name); + } + } + + g_string_append (str, _("Send anyway?")); + res = em_utils_prompt_user ((GtkWindow *) composer, GTK_RESPONSE_YES, &show_again, "%s", str->str); + g_string_free (str, TRUE); + + gconf_client_set_bool (gconf, "/apps/evolution/mail/prompts/unwanted_html", show_again, NULL); + + return res; +} + +static gboolean +ask_confirm_for_empty_subject (EMsgComposer *composer) +{ + gboolean show_again, res; + GConfClient *gconf; + + gconf = mail_config_get_gconf_client (); + + if (!gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/empty_subject", NULL)) + return TRUE; + + res = em_utils_prompt_user ((GtkWindow *) composer, GTK_RESPONSE_YES, &show_again, + _("This message has no subject.\nReally send?")); + + gconf_client_set_bool (gconf, "/apps/evolution/mail/prompts/empty_subject", show_again, NULL); + + return res; +} + +static gboolean +ask_confirm_for_only_bcc (EMsgComposer *composer, gboolean hidden_list_case) +{ + gboolean show_again, res; + const char *first_text; + GConfClient *gconf; + + gconf = mail_config_get_gconf_client (); + + if (!gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/only_bcc", NULL)) + return TRUE; + + /* If the user is mailing a hidden contact list, it is possible for + them to create a message with only Bcc recipients without really + realizing it. To try to avoid being totally confusing, I've changed + this dialog to provide slightly different text in that case, to + better explain what the hell is going on. */ + + if (hidden_list_case) { + first_text = _("Since the contact list you are sending to " + "is configured to hide the list's addresses, " + "this message will contain only Bcc recipients."); + } else { + first_text = _("This message contains only Bcc recipients."); + } + + res = em_utils_prompt_user ((GtkWindow *) composer, GTK_RESPONSE_YES, &show_again, + "%s\n%s", first_text, + _("It is possible that the mail server may reveal the recipients " + "by adding an Apparently-To header.\nSend anyway?")); + + gconf_client_set_bool (gconf, "/apps/evolution/mail/prompts/only_bcc", show_again, NULL); + + return res; +} + + +struct _send_data { + struct emcs_t *emcs; + EMsgComposer *composer; + gboolean send; +}; + +static void +composer_send_queued_cb (CamelFolder *folder, CamelMimeMessage *msg, CamelMessageInfo *info, + int queued, const char *appended_uid, void *data) +{ + struct emcs_t *emcs; + struct _send_data *send = data; + + emcs = send->emcs; + + if (queued) { + if (emcs && emcs->drafts_folder) { + /* delete the old draft message */ + camel_folder_set_message_flags (emcs->drafts_folder, emcs->drafts_uid, + CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN, + CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN); + camel_object_unref (emcs->drafts_folder); + emcs->drafts_folder = NULL; + g_free (emcs->drafts_uid); + emcs->drafts_uid = NULL; + } + + if (emcs && emcs->folder) { + /* set any replied flags etc */ + camel_folder_set_message_flags (emcs->folder, emcs->uid, emcs->flags, emcs->set); + camel_object_unref (emcs->folder); + emcs->folder = NULL; + g_free (emcs->uid); + emcs->uid = NULL; + } + + gtk_widget_destroy (GTK_WIDGET (send->composer)); + + if (send->send && camel_session_is_online (session)) { + /* queue a message send */ + mail_send (); + } + } else { + if (!emcs) { + /* disconnect the previous signal handlers */ + g_signal_handlers_disconnect_matched (send->composer, G_SIGNAL_MATCH_FUNC, 0, + 0, NULL, em_utils_composer_send_cb, NULL); + g_signal_handlers_disconnect_matched (send->composer, G_SIGNAL_MATCH_FUNC, 0, + 0, NULL, em_utils_composer_save_draft_cb, NULL); + + /* reconnect to the signals using a non-NULL emcs for the callback data */ + em_composer_utils_setup_default_callbacks (send->composer); + } + + e_msg_composer_set_enable_autosave (send->composer, TRUE); + gtk_widget_show (GTK_WIDGET (send->composer)); + } + + camel_message_info_free (info); + + if (send->emcs) + emcs_unref (send->emcs); + + g_object_unref (send->composer); + g_free (send); +} + +static CamelMimeMessage * +composer_get_message (EMsgComposer *composer, gboolean post, gboolean save_html_object_data) +{ + CamelMimeMessage *message = NULL; + EDestination **recipients, **recipients_bcc; + gboolean send_html, confirm_html; + CamelInternetAddress *cia; + int hidden = 0, shown = 0; + int num = 0, num_bcc = 0; + const char *subject; + GConfClient *gconf; + EAccount *account; + int i; + + gconf = mail_config_get_gconf_client (); + + /* We should do all of the validity checks based on the composer, and not on + the created message, as extra interaction may occur when we get the message + (e.g. to get a passphrase to sign a message) */ + + /* get the message recipients */ + recipients = e_msg_composer_get_recipients (composer); + + cia = camel_internet_address_new (); + + /* see which ones are visible/present, etc */ + if (recipients) { + for (i = 0; recipients[i] != NULL; i++) { + const char *addr = e_destination_get_address (recipients[i]); + + if (addr && addr[0]) { + camel_address_decode ((CamelAddress *) cia, addr); + if (camel_address_length ((CamelAddress *) cia) > 0) { + camel_address_remove ((CamelAddress *) cia, -1); + num++; + if (e_destination_is_evolution_list (recipients[i]) + && !e_destination_list_show_addresses (recipients[i])) { + hidden++; + } else { + shown++; + } + } + } + } + } + + recipients_bcc = e_msg_composer_get_bcc (composer); + if (recipients_bcc) { + for (i = 0; recipients_bcc[i] != NULL; i++) { + const char *addr = e_destination_get_address (recipients_bcc[i]); + + if (addr && addr[0]) { + camel_address_decode ((CamelAddress *) cia, addr); + if (camel_address_length ((CamelAddress *) cia) > 0) { + camel_address_remove ((CamelAddress *) cia, -1); + num_bcc++; + } + } + } + + e_destination_freev (recipients_bcc); + } + + camel_object_unref (cia); + + /* I'm sensing a lack of love, er, I mean recipients. */ + if (num == 0 && !post) { + e_notice ((GtkWindow *) composer, GTK_MESSAGE_WARNING, + _("You must specify recipients in order to send this message.")); + goto finished; + } + + if (num > 0 && (num == num_bcc || shown == 0)) { + /* this means that the only recipients are Bcc's */ + if (!ask_confirm_for_only_bcc (composer, shown == 0)) + goto finished; + } + + send_html = gconf_client_get_bool (gconf, "/apps/evolution/mail/composer/send_html", NULL); + confirm_html = gconf_client_get_bool (gconf, "/apps/evolution/mail/prompts/unwanted_html", NULL); + + /* Only show this warning if our default is to send html. If it isn't, we've + manually switched into html mode in the composer and (presumably) had a good + reason for doing this. */ + if (e_msg_composer_get_send_html (composer) && send_html && confirm_html) { + gboolean html_problem = FALSE; + + if (recipients) { + for (i = 0; recipients[i] != NULL && !html_problem; i++) { + if (!e_destination_get_html_mail_pref (recipients[i])) + html_problem = TRUE; + } + } + + if (html_problem) { + html_problem = !ask_confirm_for_unwanted_html_mail (composer, recipients); + if (html_problem) + goto finished; + } + } + + /* Check for no subject */ + subject = e_msg_composer_get_subject (composer); + if (subject == NULL || subject[0] == '\0') { + if (!ask_confirm_for_empty_subject (composer)) + goto finished; + } + + /* actually get the message now, this will sign/encrypt etc */ + message = e_msg_composer_get_message (composer, save_html_object_data); + if (message == NULL) + goto finished; + + /* Add info about the sending account */ + account = e_msg_composer_get_preferred_account (composer); + + if (account) { + camel_medium_set_header (CAMEL_MEDIUM (message), "X-Evolution-Account", account->name); + camel_medium_set_header (CAMEL_MEDIUM (message), "X-Evolution-Transport", account->transport->url); + camel_medium_set_header (CAMEL_MEDIUM (message), "X-Evolution-Fcc", account->sent_folder_uri); + if (account->id->organization && *account->id->organization) + camel_medium_set_header (CAMEL_MEDIUM (message), "Organization", account->id->organization); + } + + /* Get the message recipients and 'touch' them, boosting their use scores */ + if (recipients) + e_destination_touchv (recipients); + + finished: + + if (recipients) + e_destination_freev (recipients); + + return message; +} + +static void +got_post_folder (char *uri, CamelFolder *folder, void *data) +{ + CamelFolder **fp = data; + + *fp = folder; + + if (folder) + camel_object_ref (folder); +} + +void +em_utils_composer_send_cb (EMsgComposer *composer, gpointer user_data) +{ + extern CamelFolder *outbox_folder; + CamelMimeMessage *message; + CamelMessageInfo *info; + struct _send_data *send; + gboolean post = FALSE; + CamelFolder *folder; + XEvolution *xev; + char *url; + + url = e_msg_composer_hdrs_get_post_to ((EMsgComposerHdrs *) composer->hdrs); + if (url && *url) { + post = TRUE; + + mail_msg_wait (mail_get_folder (url, 0, got_post_folder, &folder, mail_thread_new)); + + if (!folder) { + g_free (url); + return; + } + } else { + folder = outbox_folder; + camel_object_ref (folder); + } + + g_free (url); + + message = composer_get_message (composer, post, FALSE); + if (!message) + return; + + if (post) { + /* Remove the X-Evolution* headers if we are in Post-To mode */ + xev = mail_tool_remove_xevolution_headers (message); + mail_tool_destroy_xevolution (xev); + } + + info = camel_message_info_new (); + info->flags = CAMEL_MESSAGE_SEEN; + + send = g_malloc (sizeof (*send)); + send->emcs = user_data; + if (send->emcs) + emcs_ref (send->emcs); + send->send = !post; + send->composer = composer; + g_object_ref (composer); + gtk_widget_hide (GTK_WIDGET (composer)); + + e_msg_composer_set_enable_autosave (composer, FALSE); + + mail_append_mail (folder, message, info, composer_send_queued_cb, send); + camel_object_unref (message); + camel_object_unref (folder); +} + +struct _save_draft_info { + struct emcs_t *emcs; + EMsgComposer *composer; + int quit; +}; + +static void +save_draft_done (CamelFolder *folder, CamelMimeMessage *msg, CamelMessageInfo *info, int ok, + const char *appended_uid, void *user_data) +{ + struct _save_draft_info *sdi = user_data; + struct emcs_t *emcs; + CORBA_Environment ev; + + if (!ok) + goto done; + CORBA_exception_init (&ev); + GNOME_GtkHTML_Editor_Engine_runCommand (sdi->composer->editor_engine, "saved", &ev); + CORBA_exception_free (&ev); + + if ((emcs = sdi->emcs) == NULL) { + emcs = emcs_new (); + + /* disconnect the previous signal handlers */ + g_signal_handlers_disconnect_by_func (sdi->composer, G_CALLBACK (em_utils_composer_send_cb), NULL); + g_signal_handlers_disconnect_by_func (sdi->composer, G_CALLBACK (em_utils_composer_save_draft_cb), NULL); + + /* reconnect to the signals using a non-NULL emcs for the callback data */ + em_composer_utils_setup_default_callbacks (sdi->composer); + } + + if (emcs->drafts_folder) { + /* delete the original draft message */ + camel_folder_set_message_flags (emcs->drafts_folder, emcs->drafts_uid, + CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN, + CAMEL_MESSAGE_DELETED | CAMEL_MESSAGE_SEEN); + camel_object_unref (emcs->drafts_folder); + emcs->drafts_folder = NULL; + g_free (emcs->drafts_uid); + emcs->drafts_uid = NULL; + } + + if (emcs->folder) { + /* set the replied flags etc */ + camel_folder_set_message_flags (emcs->folder, emcs->uid, emcs->flags, emcs->set); + camel_object_unref (emcs->folder); + emcs->folder = NULL; + g_free (emcs->uid); + emcs->uid = NULL; + } + + if (appended_uid) { + camel_object_ref (folder); + emcs->drafts_folder = folder; + emcs->drafts_uid = g_strdup (appended_uid); + } + + if (sdi->quit) + gtk_widget_destroy (GTK_WIDGET (sdi->composer)); + + done: + g_object_unref (sdi->composer); + if (sdi->emcs) + emcs_unref (sdi->emcs); + g_free (info); + g_free (sdi); +} + +static void +save_draft_folder (char *uri, CamelFolder *folder, gpointer data) +{ + CamelFolder **save = data; + + if (folder) { + *save = folder; + camel_object_ref (folder); + } +} + +void +em_utils_composer_save_draft_cb (EMsgComposer *composer, int quit, gpointer user_data) +{ + extern char *default_drafts_folder_uri; + extern CamelFolder *drafts_folder; + struct _save_draft_info *sdi; + CamelFolder *folder = NULL; + CamelMimeMessage *msg; + CamelMessageInfo *info; + EAccount *account; + + account = e_msg_composer_get_preferred_account (composer); + if (account && account->drafts_folder_uri && + strcmp (account->drafts_folder_uri, default_drafts_folder_uri) != 0) { + int id; + + id = mail_get_folder (account->drafts_folder_uri, 0, save_draft_folder, &folder, mail_thread_new); + mail_msg_wait (id); + + if (!folder) { + if (!em_utils_prompt_user ((GtkWindow *) composer, GTK_RESPONSE_YES, NULL, + _("Unable to open the drafts folder for this account.\n" + "Would you like to use the default drafts folder?"))) + return; + + folder = drafts_folder; + camel_object_ref (drafts_folder); + } + } else { + folder = drafts_folder; + camel_object_ref (folder); + } + + msg = e_msg_composer_get_message_draft (composer); + + info = g_new0 (CamelMessageInfo, 1); + info->flags = CAMEL_MESSAGE_DRAFT | CAMEL_MESSAGE_SEEN; + + sdi = g_malloc (sizeof (struct _save_draft_info)); + sdi->composer = composer; + g_object_ref (composer); + sdi->emcs = user_data; + if (sdi->emcs) + emcs_ref (sdi->emcs); + sdi->quit = quit; + + mail_append_mail (folder, msg, info, save_draft_done, sdi); + camel_object_unref (folder); + camel_object_unref (msg); +} + + +void +em_composer_utils_setup_callbacks (EMsgComposer *composer, CamelFolder *folder, const char *uid, + guint32 flags, guint32 set, CamelFolder *drafts, const char *drafts_uid) +{ + struct emcs_t *emcs; + + emcs = emcs_new (); + + if (folder && uid) { + camel_object_ref (folder); + emcs->folder = folder; + emcs->uid = g_strdup (uid); + emcs->flags = flags; + emcs->set = set; + } + + if (drafts && drafts_uid) { + camel_object_ref (drafts); + emcs->drafts_folder = drafts; + emcs->drafts_uid = g_strdup (drafts_uid); + } + + g_signal_connect (composer, "send", G_CALLBACK (em_utils_composer_send_cb), emcs); + g_signal_connect (composer, "save-draft", G_CALLBACK (em_utils_composer_save_draft_cb), emcs); + + g_object_weak_ref ((GObject *) composer, (GWeakNotify) composer_destroy_cb, emcs); +} |