diff options
Diffstat (limited to 'mail')
-rw-r--r-- | mail/em-composer-utils.c | 1090 | ||||
-rw-r--r-- | mail/em-composer-utils.h | 45 | ||||
-rw-r--r-- | mail/em-utils.c | 1082 | ||||
-rw-r--r-- | mail/em-utils.h | 36 |
4 files changed, 1133 insertions, 1120 deletions
diff --git a/mail/em-composer-utils.c b/mail/em-composer-utils.c index a0f05692e0..5d365a8c33 100644 --- a/mail/em-composer-utils.c +++ b/mail/em-composer-utils.c @@ -25,9 +25,10 @@ #endif #include <string.h> - #include <gtk/gtkdialog.h> +#include <gal/util/e-util.h> + #include "mail-mt.h" #include "mail-ops.h" #include "mail-tools.h" @@ -40,6 +41,15 @@ #include "em-utils.h" #include "em-composer-utils.h" +#include "composer/e-msg-composer.h" +#include "em-format-html.h" +#include "em-format-quote.h" + +#include "e-util/e-account-list.h" + +#include <camel/camel-string-utils.h> + +static EAccount *guess_account (CamelMimeMessage *message, CamelFolder *folder); struct emcs_t { unsigned int ref_count; @@ -582,7 +592,6 @@ em_utils_composer_save_draft_cb (EMsgComposer *composer, int quit, gpointer user 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) @@ -610,3 +619,1080 @@ em_composer_utils_setup_callbacks (EMsgComposer *composer, CamelFolder *folder, g_object_weak_ref ((GObject *) composer, (GWeakNotify) composer_destroy_cb, emcs); } + +/* Composing messages... */ + +static EMsgComposer * +create_new_composer (void) +{ + EMsgComposer *composer; + + composer = e_msg_composer_new (); + + em_composer_utils_setup_default_callbacks (composer); + + return composer; +} + +/** + * em_utils_compose_new_message: + * + * Opens a new composer window as a child window of @parent's toplevel + * window. + **/ +void +em_utils_compose_new_message (void) +{ + GtkWidget *composer; + + composer = (GtkWidget *) create_new_composer (); + + gtk_widget_show (composer); +} + +/** + * em_utils_compose_new_message_with_mailto: + * @url: mailto url + * + * Opens a new composer window as a child window of @parent's toplevel + * window. If @url is non-NULL, the composer fields will be filled in + * according to the values in the mailto url. + **/ +void +em_utils_compose_new_message_with_mailto (const char *url) +{ + EMsgComposer *composer; + + if (url != NULL) + composer = e_msg_composer_new_from_url (url); + else + composer = e_msg_composer_new (); + + em_composer_utils_setup_default_callbacks (composer); + + gtk_widget_show ((GtkWidget *) composer); +} + +/** + * em_utils_post_to_folder: + * @folder: folder + * + * Opens a new composer window as a child window of @parent's toplevel + * window. If @folder is non-NULL, the composer will default to posting + * mail to the folder specified by @folder. + **/ +void +em_utils_post_to_folder (CamelFolder *folder) +{ + EMsgComposer *composer; + EAccount *account; + + composer = e_msg_composer_new_with_type (E_MSG_COMPOSER_POST); + + if (folder != NULL) { + char *url = mail_tools_folder_to_url (folder); + + e_msg_composer_hdrs_set_post_to ((EMsgComposerHdrs *) ((EMsgComposer *) composer)->hdrs, url); + g_free (url); + + url = camel_url_to_string (CAMEL_SERVICE (folder->parent_store)->url, CAMEL_URL_HIDE_ALL); + account = mail_config_get_account_by_source_url (url); + g_free (url); + + if (account) + e_msg_composer_set_headers (composer, account->name, NULL, NULL, NULL, ""); + } + + em_composer_utils_setup_default_callbacks (composer); + + e_msg_composer_unset_changed (composer); + e_msg_composer_drop_editor_undo (composer); + + gtk_widget_show ((GtkWidget *) composer); +} + +/** + * em_utils_post_to_url: + * @url: mailto url + * + * Opens a new composer window as a child window of @parent's toplevel + * window. If @url is non-NULL, the composer will default to posting + * mail to the folder specified by @url. + **/ +void +em_utils_post_to_url (const char *url) +{ + EMsgComposer *composer; + + composer = e_msg_composer_new_with_type (E_MSG_COMPOSER_POST); + + if (url != NULL) + e_msg_composer_hdrs_set_post_to ((EMsgComposerHdrs *) ((EMsgComposer *) composer)->hdrs, url); + + em_composer_utils_setup_default_callbacks (composer); + + e_msg_composer_unset_changed (composer); + e_msg_composer_drop_editor_undo (composer); + + gtk_widget_show ((GtkWidget *) composer); +} + +/* Editing messages... */ + +static void +edit_message (CamelMimeMessage *message, CamelFolder *drafts, const char *uid) +{ + EMsgComposer *composer; + + composer = e_msg_composer_new_with_message (message); + em_composer_utils_setup_callbacks (composer, NULL, NULL, 0, 0, drafts, uid); + e_msg_composer_unset_changed (composer); + e_msg_composer_drop_editor_undo (composer); + + gtk_widget_show (GTK_WIDGET (composer)); +} + +/** + * em_utils_edit_message: + * @message: message to edit + * + * Opens a composer filled in with the headers/mime-parts/etc of + * @message. + **/ +void +em_utils_edit_message (CamelMimeMessage *message) +{ + g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); + + edit_message (message, NULL, NULL); +} + +static void +edit_messages (CamelFolder *folder, GPtrArray *uids, GPtrArray *msgs, void *user_data) +{ + int i; + + if (msgs == NULL) + return; + + for (i = 0; i < msgs->len; i++) { + camel_medium_remove_header (CAMEL_MEDIUM (msgs->pdata[i]), "X-Mailer"); + + edit_message (msgs->pdata[i], folder, uids->pdata[i]); + } +} + +/** + * em_utils_edit_messages: + * @folder: folder containing messages to edit + * @uids: uids of messages to edit + * + * Opens a composer for each message to be edited. + **/ +void +em_utils_edit_messages (CamelFolder *folder, GPtrArray *uids) +{ + g_return_if_fail (CAMEL_IS_FOLDER (folder)); + g_return_if_fail (uids != NULL); + + mail_get_messages (folder, uids, edit_messages, NULL); +} + +/* Forwarding messages... */ + +static void +forward_attached (CamelFolder *folder, GPtrArray *messages, CamelMimePart *part, char *subject, void *user_data) +{ + EMsgComposer *composer; + + if (part == NULL) + return; + + composer = create_new_composer (); + e_msg_composer_set_headers (composer, NULL, NULL, NULL, NULL, subject); + e_msg_composer_attach (composer, part); + + e_msg_composer_unset_changed (composer); + e_msg_composer_drop_editor_undo (composer); + + gtk_widget_show (GTK_WIDGET (composer)); +} + +/** + * em_utils_forward_attached: + * @folder: folder containing messages to forward + * @uids: uids of messages to forward + * + * If there is more than a single message in @uids, a multipart/digest + * will be constructed and attached to a new composer window preset + * with the appropriate header defaults for forwarding the first + * message in the list. If only one message is to be forwarded, it is + * forwarded as a simple message/rfc822 attachment. + **/ +void +em_utils_forward_attached (CamelFolder *folder, GPtrArray *uids) +{ + g_return_if_fail (CAMEL_IS_FOLDER (folder)); + g_return_if_fail (uids != NULL); + + mail_build_attachment (folder, uids, forward_attached, NULL); +} + +static void +forward_non_attached (GPtrArray *messages, int style) +{ + CamelMimeMessage *message; + CamelDataWrapper *wrapper; + EMsgComposer *composer; + char *subject, *text; + int i; + guint32 flags; + + if (messages->len == 0) + return; + + flags = EM_FORMAT_QUOTE_HEADERS; + if (style == MAIL_CONFIG_FORWARD_QUOTED) + flags |= EM_FORMAT_QUOTE_CITE; + + for (i = 0; i < messages->len; i++) { + message = messages->pdata[i]; + subject = mail_tool_generate_forward_subject (message); + + text = em_utils_message_to_html (message, _("-------- Forwarded Message --------"), flags); + + if (text) { + composer = create_new_composer (); + e_msg_composer_set_headers (composer, NULL, NULL, NULL, NULL, subject); + + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (message)); + if (CAMEL_IS_MULTIPART (wrapper)) + e_msg_composer_add_message_attachments (composer, message, FALSE); + + e_msg_composer_set_body_text (composer, text); + + e_msg_composer_unset_changed (composer); + e_msg_composer_drop_editor_undo (composer); + + gtk_widget_show (GTK_WIDGET (composer)); + + g_free (text); + } + + g_free (subject); + } +} + +static void +forward_inline (CamelFolder *folder, GPtrArray *uids, GPtrArray *messages, void *user_data) +{ + forward_non_attached (messages, MAIL_CONFIG_FORWARD_INLINE); +} + +/** + * em_utils_forward_inline: + * @folder: folder containing messages to forward + * @uids: uids of messages to forward + * + * Forwards each message in the 'inline' form, each in its own composer window. + **/ +void +em_utils_forward_inline (CamelFolder *folder, GPtrArray *uids) +{ + g_return_if_fail (CAMEL_IS_FOLDER (folder)); + g_return_if_fail (uids != NULL); + + mail_get_messages (folder, uids, forward_inline, NULL); +} + +static void +forward_quoted (CamelFolder *folder, GPtrArray *uids, GPtrArray *messages, void *user_data) +{ + forward_non_attached (messages, MAIL_CONFIG_FORWARD_QUOTED); +} + +/** + * em_utils_forward_quoted: + * @folder: folder containing messages to forward + * @uids: uids of messages to forward + * + * Forwards each message in the 'quoted' form (each line starting with + * a "> "), each in its own composer window. + **/ +void +em_utils_forward_quoted (CamelFolder *folder, GPtrArray *uids) +{ + g_return_if_fail (CAMEL_IS_FOLDER (folder)); + g_return_if_fail (uids != NULL); + + mail_get_messages (folder, uids, forward_quoted, NULL); +} + +/** + * em_utils_forward_message: + * @parent: parent window + * @message: message to be forwarded + * + * Forwards a message in the user's configured default style. + **/ +void +em_utils_forward_message (CamelMimeMessage *message) +{ + GPtrArray *messages; + CamelMimePart *part; + GConfClient *gconf; + char *subject; + int mode; + + messages = g_ptr_array_new (); + g_ptr_array_add (messages, message); + + gconf = mail_config_get_gconf_client (); + mode = gconf_client_get_int (gconf, "/apps/evolution/mail/format/forward_style", NULL); + + switch (mode) { + case MAIL_CONFIG_FORWARD_ATTACHED: + default: + part = mail_tool_make_message_attachment (message); + + subject = mail_tool_generate_forward_subject (message); + + forward_attached (NULL, messages, part, subject, NULL); + camel_object_unref (part); + g_free (subject); + break; + case MAIL_CONFIG_FORWARD_INLINE: + forward_non_attached (messages, MAIL_CONFIG_FORWARD_INLINE); + break; + case MAIL_CONFIG_FORWARD_QUOTED: + forward_non_attached (messages, MAIL_CONFIG_FORWARD_QUOTED); + break; + } + + g_ptr_array_free (messages, TRUE); +} + +/** + * em_utils_forward_messages: + * @folder: folder containing messages to forward + * @uids: uids of messages to forward + * + * Forwards a group of messages in the user's configured default + * style. + **/ +void +em_utils_forward_messages (CamelFolder *folder, GPtrArray *uids) +{ + GConfClient *gconf; + int mode; + + gconf = mail_config_get_gconf_client (); + mode = gconf_client_get_int (gconf, "/apps/evolution/mail/format/forward_style", NULL); + + switch (mode) { + case MAIL_CONFIG_FORWARD_ATTACHED: + default: + em_utils_forward_attached (folder, uids); + break; + case MAIL_CONFIG_FORWARD_INLINE: + em_utils_forward_inline (folder, uids); + break; + case MAIL_CONFIG_FORWARD_QUOTED: + em_utils_forward_quoted (folder, uids); + break; + } +} + +/* Redirecting messages... */ + +static EMsgComposer * +redirect_get_composer (CamelMimeMessage *message) +{ + EMsgComposer *composer; + EAccount *account; + + /* QMail will refuse to send a message if it finds one of + it's Delivered-To headers in the message, so remove all + Delivered-To headers. Fixes bug #23635. */ + while (camel_medium_get_header (CAMEL_MEDIUM (message), "Delivered-To")) + camel_medium_remove_header (CAMEL_MEDIUM (message), "Delivered-To"); + + account = guess_account (message, NULL); + + composer = e_msg_composer_new_redirect (message, account ? account->name : NULL); + + em_composer_utils_setup_default_callbacks (composer); + + return composer; +} + +/** + * em_utils_redirect_message: + * @message: message to redirect + * + * Opens a composer to redirect @message (Note: only headers will be + * editable). Adds Resent-From/Resent-To/etc headers. + **/ +void +em_utils_redirect_message (CamelMimeMessage *message) +{ + EMsgComposer *composer; + CamelDataWrapper *wrapper; + + g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); + + composer = redirect_get_composer (message); + + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (message)); + + gtk_widget_show (GTK_WIDGET (composer)); + e_msg_composer_unset_changed (composer); + e_msg_composer_drop_editor_undo (composer); +} + +static void +redirect_msg (CamelFolder *folder, const char *uid, CamelMimeMessage *message, void *user_data) +{ + if (message == NULL) + return; + + em_utils_redirect_message (message); +} + +/** + * em_utils_redirect_message_by_uid: + * @folder: folder containing message to be redirected + * @uid: uid of message to be redirected + * + * Opens a composer to redirect the message (Note: only headers will + * be editable). Adds Resent-From/Resent-To/etc headers. + **/ +void +em_utils_redirect_message_by_uid (CamelFolder *folder, const char *uid) +{ + g_return_if_fail (CAMEL_IS_FOLDER (folder)); + g_return_if_fail (uid != NULL); + + mail_get_message (folder, uid, redirect_msg, NULL, mail_thread_new); +} + +/* Replying to messages... */ + +static GHashTable * +generate_account_hash (void) +{ + GHashTable *account_hash; + EAccount *account, *def; + EAccountList *accounts; + EIterator *iter; + + accounts = mail_config_get_accounts (); + account_hash = g_hash_table_new (camel_strcase_hash, camel_strcase_equal); + + /* add the default account to the hash first */ + if ((def = mail_config_get_default_account ())) { + if (def->id->address) + g_hash_table_insert (account_hash, (char *) def->id->address, (void *) def); + } + + iter = e_list_get_iterator ((EList *) accounts); + while (e_iterator_is_valid (iter)) { + account = (EAccount *) e_iterator_get (iter); + + if (account->id->address) { + EAccount *acnt; + + /* Accounts with identical email addresses that are enabled + * take precedence over the accounts that aren't. If all + * accounts with matching email addresses are disabled, then + * the first one in the list takes precedence. The default + * account always takes precedence no matter what. + */ + acnt = g_hash_table_lookup (account_hash, account->id->address); + if (acnt && acnt != def && !acnt->enabled && account->enabled) { + g_hash_table_remove (account_hash, acnt->id->address); + acnt = NULL; + } + + if (!acnt) + g_hash_table_insert (account_hash, (char *) account->id->address, (void *) account); + } + + e_iterator_next (iter); + } + + g_object_unref (iter); + + return account_hash; +} + +static EDestination ** +em_utils_camel_address_to_destination (CamelInternetAddress *iaddr) +{ + EDestination *dest, **destv; + int n, i, j; + + if (iaddr == NULL) + return NULL; + + if ((n = camel_address_length ((CamelAddress *) iaddr)) == 0) + return NULL; + + destv = g_malloc (sizeof (EDestination *) * (n + 1)); + for (i = 0, j = 0; i < n; i++) { + const char *name, *addr; + + if (camel_internet_address_get (iaddr, i, &name, &addr)) { + dest = e_destination_new (); + e_destination_set_name (dest, name); + e_destination_set_email (dest, addr); + + destv[j++] = dest; + } + } + + if (j == 0) { + g_free (destv); + return NULL; + } + + destv[j] = NULL; + + return destv; +} + +static EMsgComposer * +reply_get_composer (CamelMimeMessage *message, EAccount *account, + CamelInternetAddress *to, CamelInternetAddress *cc, + CamelFolder *folder, const char *postto) +{ + const char *message_id, *references; + EDestination **tov, **ccv; + EMsgComposer *composer; + char *subject; + + g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); + g_return_val_if_fail (to == NULL || CAMEL_IS_INTERNET_ADDRESS (to), NULL); + g_return_val_if_fail (cc == NULL || CAMEL_IS_INTERNET_ADDRESS (cc), NULL); + + /* construct the tov/ccv */ + tov = em_utils_camel_address_to_destination (to); + ccv = em_utils_camel_address_to_destination (cc); + + if (tov || ccv) { + if (postto) + composer = e_msg_composer_new_with_type (E_MSG_COMPOSER_MAIL_POST); + else + composer = e_msg_composer_new_with_type (E_MSG_COMPOSER_MAIL); + } else + composer = e_msg_composer_new_with_type (E_MSG_COMPOSER_POST); + + /* Set the subject of the new message. */ + if ((subject = (char *) camel_mime_message_get_subject (message))) { + if (strncasecmp (subject, "Re: ", 4) != 0) + subject = g_strdup_printf ("Re: %s", subject); + else + subject = g_strdup (subject); + } else { + subject = g_strdup (""); + } + + e_msg_composer_set_headers (composer, account ? account->name : NULL, tov, ccv, NULL, subject); + + g_free (subject); + + /* add post-to, if nessecary */ + if (postto) { + char *store_url = NULL; + + if (folder) { + store_url = camel_url_to_string (CAMEL_SERVICE (folder->parent_store)->url, CAMEL_URL_HIDE_ALL); + if (store_url[strlen (store_url) - 1] == '/') + store_url[strlen (store_url)-1] = '\0'; + } + + e_msg_composer_hdrs_set_post_to_base (E_MSG_COMPOSER_HDRS (composer->hdrs), store_url ? store_url : "", postto); + g_free (store_url); + } + + /* Add In-Reply-To and References. */ + message_id = camel_medium_get_header (CAMEL_MEDIUM (message), "Message-Id"); + references = camel_medium_get_header (CAMEL_MEDIUM (message), "References"); + if (message_id) { + char *reply_refs; + + e_msg_composer_add_header (composer, "In-Reply-To", message_id); + + if (references) + reply_refs = g_strdup_printf ("%s %s", references, message_id); + else + reply_refs = g_strdup (message_id); + + e_msg_composer_add_header (composer, "References", reply_refs); + g_free (reply_refs); + } else if (references) { + e_msg_composer_add_header (composer, "References", references); + } + + e_msg_composer_drop_editor_undo (composer); + + return composer; +} + +static EAccount * +guess_account_folder(CamelFolder *folder) +{ + EAccount *account; + char *tmp; + + tmp = camel_url_to_string(CAMEL_SERVICE(folder->parent_store)->url, CAMEL_URL_HIDE_ALL); + account = mail_config_get_account_by_source_url(tmp); + g_free(tmp); + + return account; +} + +static EAccount * +guess_account (CamelMimeMessage *message, CamelFolder *folder) +{ + GHashTable *account_hash = NULL; + EAccount *account = NULL; + const char *tmp; + int i, j; + char *types[2] = { CAMEL_RECIPIENT_TYPE_TO, CAMEL_RECIPIENT_TYPE_CC }; + + /* check for newsgroup header */ + if (folder + && camel_medium_get_header((CamelMedium *)message, "Newsgroups") + && (account = guess_account_folder(folder))) + return account; + + /* then recipient (to/cc) in account table */ + account_hash = generate_account_hash (); + for (j=0;account == NULL && j<2;j++) { + const CamelInternetAddress *to; + + to = camel_mime_message_get_recipients(message, types[j]); + if (to) { + for (i = 0; camel_internet_address_get(to, i, NULL, &tmp); i++) { + account = g_hash_table_lookup(account_hash, tmp); + if (account) + break; + } + } + } + g_hash_table_destroy(account_hash); + + /* then message source */ + if (account == NULL + && (tmp = camel_mime_message_get_source(message))) + account = mail_config_get_account_by_source_url(tmp); + + /* and finally, source folder */ + if (account == NULL + && folder) + account = guess_account_folder(folder); + + return account; +} + +static void +get_reply_sender (CamelMimeMessage *message, CamelInternetAddress **to, const char **postto) +{ + const CamelInternetAddress *reply_to; + const char *name, *addr, *posthdr; + int i; + + /* check whether there is a 'Newsgroups: ' header in there */ + posthdr = camel_medium_get_header (CAMEL_MEDIUM (message), "Newsgroups"); + if (posthdr && postto) { + *postto = posthdr; + while (**postto == ' ') + (*postto)++; + return; + } + + reply_to = camel_mime_message_get_reply_to (message); + if (!reply_to) + reply_to = camel_mime_message_get_from (message); + + if (reply_to) { + *to = camel_internet_address_new (); + + for (i = 0; camel_internet_address_get (reply_to, i, &name, &addr); i++) + camel_internet_address_add (*to, name, addr); + } +} + +static gboolean +get_reply_list (CamelMimeMessage *message, CamelInternetAddress **to) +{ + const char *header, *p; + char *addr; + + /* Examples: + * + * List-Post: <mailto:list@host.com> + * List-Post: <mailto:moderator@host.com?subject=list%20posting> + * List-Post: NO (posting not allowed on this list) + */ + if (!(header = camel_medium_get_header ((CamelMedium *) message, "List-Post"))) + return FALSE; + + while (*header == ' ' || *header == '\t') + header++; + + /* check for NO */ + if (!strncasecmp (header, "NO", 2)) + return FALSE; + + /* Search for the first mailto angle-bracket enclosed URL. + * (See rfc2369, Section 2, paragraph 3 for details) */ + if (!(header = camel_strstrcase (header, "<mailto:"))) + return FALSE; + + header += 8; + + p = header; + while (*p && !strchr ("?>", *p)) + p++; + + addr = g_strndup (header, p - header); + + *to = camel_internet_address_new (); + camel_internet_address_add (*to, NULL, addr); + + g_free (addr); + + return TRUE; +} + +static void +concat_unique_addrs (CamelInternetAddress *dest, const CamelInternetAddress *src, GHashTable *rcpt_hash) +{ + const char *name, *addr; + int i; + + for (i = 0; camel_internet_address_get (src, i, &name, &addr); i++) { + if (!g_hash_table_lookup (rcpt_hash, addr)) { + camel_internet_address_add (dest, name, addr); + g_hash_table_insert (rcpt_hash, (char *) addr, GINT_TO_POINTER (1)); + } + } +} + +static void +get_reply_all (CamelMimeMessage *message, CamelInternetAddress **to, CamelInternetAddress **cc, const char **postto) +{ + const CamelInternetAddress *reply_to, *to_addrs, *cc_addrs; + const char *name, *addr, *posthdr; + GHashTable *rcpt_hash; + int i; + + /* check whether there is a 'Newsgroups: ' header in there */ + posthdr = camel_medium_get_header (CAMEL_MEDIUM(message), "Newsgroups"); + if (posthdr && postto) { + *postto = posthdr; + while (**postto == ' ') + (*postto)++; + } + + rcpt_hash = generate_account_hash (); + + reply_to = camel_mime_message_get_reply_to (message); + if (!reply_to) + reply_to = camel_mime_message_get_from (message); + + to_addrs = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO); + cc_addrs = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC); + + *to = camel_internet_address_new (); + *cc = camel_internet_address_new (); + + if (reply_to) { + for (i = 0; camel_internet_address_get (reply_to, i, &name, &addr); i++) { + /* ignore references to the Reply-To address in the To and Cc lists */ + if (addr && !g_hash_table_lookup (rcpt_hash, addr)) { + /* In the case that we are doing a Reply-To-All, we do not want + to include the user's email address because replying to oneself + is kinda silly. */ + + camel_internet_address_add (*to, name, addr); + g_hash_table_insert (rcpt_hash, (char *) addr, GINT_TO_POINTER (1)); + } + } + } + + concat_unique_addrs (*cc, to_addrs, rcpt_hash); + concat_unique_addrs (*cc, cc_addrs, rcpt_hash); + + /* promote the first Cc: address to To: if To: is empty */ + if (camel_address_length ((CamelAddress *) *to) == 0 && camel_address_length ((CamelAddress *) *cc) > 0) { + camel_internet_address_get (*cc, 0, &name, &addr); + camel_internet_address_add (*to, name, addr); + camel_address_remove ((CamelAddress *) *cc, 0); + } + + g_hash_table_destroy (rcpt_hash); +} + +static void +composer_set_body (EMsgComposer *composer, CamelMimeMessage *message) +{ + const CamelInternetAddress *sender; + char *text, *credits, format[256]; + const char *name, *addr; + CamelMimePart *part; + GConfClient *gconf; + time_t date; + int date_offset; + + gconf = mail_config_get_gconf_client (); + + switch (gconf_client_get_int (gconf, "/apps/evolution/mail/format/reply_style", NULL)) { + case MAIL_CONFIG_REPLY_DO_NOT_QUOTE: + /* do nothing */ + break; + case MAIL_CONFIG_REPLY_ATTACH: + /* attach the original message as an attachment */ + part = mail_tool_make_message_attachment (message); + e_msg_composer_attach (composer, part); + camel_object_unref (part); + break; + case MAIL_CONFIG_REPLY_QUOTED: + default: + /* do what any sane user would want when replying... */ + sender = camel_mime_message_get_from (message); + if (sender != NULL && camel_address_length (CAMEL_ADDRESS (sender)) > 0) { + camel_internet_address_get (sender, 0, &name, &addr); + } else { + name = _("an unknown sender"); + } + + date = camel_mime_message_get_date(message, &date_offset); + /* Convert to UTC */ + date += (date_offset / 100) * 60 * 60; + date += (date_offset % 100) * 60; + + /* translators: attribution string used when quoting messages, + it must contain a single single %%+05d followed by a single '%%s' */ + e_utf8_strftime(format, sizeof(format), _("On %a, %Y-%m-%d at %H:%M %%+05d, %%s wrote:"), gmtime(&date)); + credits = g_strdup_printf(format, date_offset, name && *name ? name : addr); + text = em_utils_message_to_html(message, credits, EM_FORMAT_QUOTE_CITE); + g_free (credits); + e_msg_composer_set_body_text(composer, text); + g_free (text); + break; + } + + e_msg_composer_drop_editor_undo (composer); +} + +/** + * em_utils_reply_to_message: + * @message: message to reply to + * @mode: reply mode + * + * Creates a new composer ready to reply to @message. + **/ +void +em_utils_reply_to_message (CamelMimeMessage *message, int mode) +{ + CamelInternetAddress *to = NULL, *cc = NULL; + EMsgComposer *composer; + EAccount *account; + + account = guess_account (message, NULL); + + switch (mode) { + case REPLY_MODE_SENDER: + get_reply_sender (message, &to, NULL); + break; + case REPLY_MODE_LIST: + if (get_reply_list (message, &to)) + break; + case REPLY_MODE_ALL: + get_reply_all (message, &to, &cc, NULL); + break; + } + + composer = reply_get_composer (message, account, to, cc, NULL, NULL); + e_msg_composer_add_message_attachments (composer, message, TRUE); + + if (to != NULL) + camel_object_unref (to); + + if (cc != NULL) + camel_object_unref (cc); + + composer_set_body (composer, message); + + em_composer_utils_setup_default_callbacks (composer); + + gtk_widget_show (GTK_WIDGET (composer)); + e_msg_composer_unset_changed (composer); +} + +static void +reply_to_message (CamelFolder *folder, const char *uid, CamelMimeMessage *message, void *user_data) +{ + CamelInternetAddress *to = NULL, *cc = NULL; + const char *postto = NULL; + EMsgComposer *composer; + EAccount *account; + guint32 flags; + int mode; + + if (message == NULL) + return; + + mode = GPOINTER_TO_INT (user_data); + + account = guess_account (message, folder); + flags = CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_SEEN; + + switch (mode) { + case REPLY_MODE_SENDER: + get_reply_sender (message, &to, &postto); + break; + case REPLY_MODE_LIST: + flags |= CAMEL_MESSAGE_ANSWERED_ALL; + if (get_reply_list (message, &to)) + break; + case REPLY_MODE_ALL: + flags |= CAMEL_MESSAGE_ANSWERED_ALL; + get_reply_all (message, &to, &cc, &postto); + break; + } + + composer = reply_get_composer (message, account, to, cc, folder, postto); + e_msg_composer_add_message_attachments (composer, message, TRUE); + + if (to != NULL) + camel_object_unref (to); + + if (cc != NULL) + camel_object_unref (cc); + + composer_set_body (composer, message); + + em_composer_utils_setup_callbacks (composer, folder, uid, flags, flags, NULL, NULL); + + gtk_widget_show (GTK_WIDGET (composer)); + e_msg_composer_unset_changed (composer); +} + +/** + * em_utils_reply_to_message_by_uid: + * @folder: folder containing message to reply to + * @uid: message uid + * @mode: reply mode + * + * Creates a new composer ready to reply to the message referenced by + * @folder and @uid. + **/ +void +em_utils_reply_to_message_by_uid (CamelFolder *folder, const char *uid, int mode) +{ + g_return_if_fail (CAMEL_IS_FOLDER (folder)); + g_return_if_fail (uid != NULL); + + mail_get_message (folder, uid, reply_to_message, GINT_TO_POINTER (mode), mail_thread_new); +} + +/* Posting replies... */ + +static void +post_reply_to_message (CamelFolder *folder, const char *uid, CamelMimeMessage *message, void *user_data) +{ + /* FIXME: would be nice if this shared more code with reply_get_composer() */ + const char *message_id, *references; + CamelInternetAddress *to = NULL; + EDestination **tov = NULL; + EMsgComposer *composer; + char *subject, *url; + EAccount *account; + guint32 flags; + + if (message == NULL) + return; + + account = guess_account (message, folder); + flags = CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_SEEN; + + get_reply_sender (message, &to, NULL); + + composer = e_msg_composer_new_with_type (E_MSG_COMPOSER_MAIL_POST); + + /* construct the tov/ccv */ + tov = em_utils_camel_address_to_destination (to); + + /* Set the subject of the new message. */ + if ((subject = (char *) camel_mime_message_get_subject (message))) { + if (strncasecmp (subject, "Re: ", 4) != 0) + subject = g_strdup_printf ("Re: %s", subject); + else + subject = g_strdup (subject); + } else { + subject = g_strdup (""); + } + + e_msg_composer_set_headers (composer, account ? account->name : NULL, tov, NULL, NULL, subject); + + g_free (subject); + + url = mail_tools_folder_to_url (folder); + e_msg_composer_hdrs_set_post_to ((EMsgComposerHdrs *) composer->hdrs, url); + g_free (url); + + /* Add In-Reply-To and References. */ + message_id = camel_medium_get_header (CAMEL_MEDIUM (message), "Message-Id"); + references = camel_medium_get_header (CAMEL_MEDIUM (message), "References"); + if (message_id) { + char *reply_refs; + + e_msg_composer_add_header (composer, "In-Reply-To", message_id); + + if (references) + reply_refs = g_strdup_printf ("%s %s", references, message_id); + else + reply_refs = g_strdup (message_id); + + e_msg_composer_add_header (composer, "References", reply_refs); + g_free (reply_refs); + } else if (references) { + e_msg_composer_add_header (composer, "References", references); + } + + e_msg_composer_drop_editor_undo (composer); + + e_msg_composer_add_message_attachments (composer, message, TRUE); + + if (to != NULL) + camel_object_unref (to); + + composer_set_body (composer, message); + + em_composer_utils_setup_callbacks (composer, folder, uid, flags, flags, NULL, NULL); + + gtk_widget_show (GTK_WIDGET (composer)); + e_msg_composer_unset_changed (composer); +} + +/** + * em_utils_post_reply_to_message_by_uid: + * @folder: folder containing message to reply to + * @uid: message uid + * @mode: reply mode + * + * Creates a new composer (post mode) ready to reply to the message + * referenced by @folder and @uid. + **/ +void +em_utils_post_reply_to_message_by_uid (CamelFolder *folder, const char *uid) +{ + g_return_if_fail (CAMEL_IS_FOLDER (folder)); + g_return_if_fail (uid != NULL); + + mail_get_message (folder, uid, post_reply_to_message, NULL, mail_thread_new); +} diff --git a/mail/em-composer-utils.h b/mail/em-composer-utils.h index 7e4aeb0274..01972f1fa2 100644 --- a/mail/em-composer-utils.h +++ b/mail/em-composer-utils.h @@ -20,22 +20,59 @@ * */ - #ifndef __EM_COMPOSER_UTILS_H__ #define __EM_COMPOSER_UTILS_H__ -#include <composer/e-msg-composer.h> +#include <glib.h> #ifdef __cplusplus extern "C" { #pragma } #endif /* __cplusplus */ -void em_composer_utils_setup_callbacks (EMsgComposer *composer, CamelFolder *folder, const char *uid, - guint32 flags, guint32 set, CamelFolder *drafts, const char *drafts_uid); +struct _CamelFolder; +struct _CamelMimeMessage; +struct _EMsgComposer; + +void em_composer_utils_setup_callbacks (struct _EMsgComposer *composer, struct _CamelFolder *folder, const char *uid, + guint32 flags, guint32 set, struct _CamelFolder *drafts, const char *drafts_uid); #define em_composer_utils_setup_default_callbacks(composer) em_composer_utils_setup_callbacks (composer, NULL, NULL, 0, 0, NULL, NULL) +void em_utils_composer_send_cb(struct _EMsgComposer *composer, gpointer user_data); +void em_utils_composer_save_draft_cb(struct _EMsgComposer *composer, int quit, gpointer user_data); + +void em_utils_compose_new_message (void); + +/* FIXME: mailto? url? should make up its mind what its called. imho use 'uri' */ +void em_utils_compose_new_message_with_mailto (const char *url); +void em_utils_post_to_folder (struct _CamelFolder *folder); +void em_utils_post_to_url (const char *url); + +void em_utils_edit_message (struct _CamelMimeMessage *message); +void em_utils_edit_messages (struct _CamelFolder *folder, GPtrArray *uids); + +void em_utils_forward_attached (struct _CamelFolder *folder, GPtrArray *uids); +void em_utils_forward_inline (struct _CamelFolder *folder, GPtrArray *uids); +void em_utils_forward_quoted (struct _CamelFolder *folder, GPtrArray *uids); + +void em_utils_forward_message (struct _CamelMimeMessage *msg); +void em_utils_forward_messages (struct _CamelFolder *folder, GPtrArray *uids); + +void em_utils_redirect_message (struct _CamelMimeMessage *message); +void em_utils_redirect_message_by_uid (struct _CamelFolder *folder, const char *uid); + +enum { + REPLY_MODE_SENDER, + REPLY_MODE_ALL, + REPLY_MODE_LIST +}; + +void em_utils_reply_to_message (struct _CamelMimeMessage *message, int mode); +void em_utils_reply_to_message_by_uid (struct _CamelFolder *folder, const char *uid, int mode); + +void em_utils_post_reply_to_message_by_uid (struct _CamelFolder *folder, const char *uid); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/mail/em-utils.c b/mail/em-utils.c index a5b927e7c2..87b0d9b899 100644 --- a/mail/em-utils.c +++ b/mail/em-utils.c @@ -37,6 +37,10 @@ #include <filter/filter-editor.h> +#include <bonobo/bonobo-listener.h> +#include <bonobo/bonobo-widget.h> +#include <bonobo/bonobo-event-source.h> + #include "mail-component.h" #include "mail-mt.h" #include "mail-ops.h" @@ -56,7 +60,6 @@ #include "em-composer-utils.h" #include "em-format-quote.h" -static EAccount *guess_account (CamelMimeMessage *message, CamelFolder *folder); static void emu_save_part_done (CamelMimePart *part, char *name, int done, void *data); #define d(x) @@ -288,1083 +291,6 @@ em_utils_edit_filters (GtkWidget *parent) gtk_widget_show (GTK_WIDGET (filter_editor)); } -/* Composing messages... */ - -static EMsgComposer * -create_new_composer (void) -{ - EMsgComposer *composer; - - composer = e_msg_composer_new (); - - em_composer_utils_setup_default_callbacks (composer); - - return composer; -} - -/** - * em_utils_compose_new_message: - * - * Opens a new composer window as a child window of @parent's toplevel - * window. - **/ -void -em_utils_compose_new_message (void) -{ - GtkWidget *composer; - - composer = (GtkWidget *) create_new_composer (); - - gtk_widget_show (composer); -} - -/** - * em_utils_compose_new_message_with_mailto: - * @url: mailto url - * - * Opens a new composer window as a child window of @parent's toplevel - * window. If @url is non-NULL, the composer fields will be filled in - * according to the values in the mailto url. - **/ -void -em_utils_compose_new_message_with_mailto (const char *url) -{ - EMsgComposer *composer; - - if (url != NULL) - composer = e_msg_composer_new_from_url (url); - else - composer = e_msg_composer_new (); - - em_composer_utils_setup_default_callbacks (composer); - - gtk_widget_show ((GtkWidget *) composer); -} - -/** - * em_utils_post_to_folder: - * @folder: folder - * - * Opens a new composer window as a child window of @parent's toplevel - * window. If @folder is non-NULL, the composer will default to posting - * mail to the folder specified by @folder. - **/ -void -em_utils_post_to_folder (CamelFolder *folder) -{ - EMsgComposer *composer; - EAccount *account; - - composer = e_msg_composer_new_with_type (E_MSG_COMPOSER_POST); - - if (folder != NULL) { - char *url = mail_tools_folder_to_url (folder); - - e_msg_composer_hdrs_set_post_to ((EMsgComposerHdrs *) ((EMsgComposer *) composer)->hdrs, url); - g_free (url); - - url = camel_url_to_string (CAMEL_SERVICE (folder->parent_store)->url, CAMEL_URL_HIDE_ALL); - account = mail_config_get_account_by_source_url (url); - g_free (url); - - if (account) - e_msg_composer_set_headers (composer, account->name, NULL, NULL, NULL, ""); - } - - em_composer_utils_setup_default_callbacks (composer); - - e_msg_composer_unset_changed (composer); - e_msg_composer_drop_editor_undo (composer); - - gtk_widget_show ((GtkWidget *) composer); -} - -/** - * em_utils_post_to_url: - * @url: mailto url - * - * Opens a new composer window as a child window of @parent's toplevel - * window. If @url is non-NULL, the composer will default to posting - * mail to the folder specified by @url. - **/ -void -em_utils_post_to_url (const char *url) -{ - EMsgComposer *composer; - - composer = e_msg_composer_new_with_type (E_MSG_COMPOSER_POST); - - if (url != NULL) - e_msg_composer_hdrs_set_post_to ((EMsgComposerHdrs *) ((EMsgComposer *) composer)->hdrs, url); - - em_composer_utils_setup_default_callbacks (composer); - - e_msg_composer_unset_changed (composer); - e_msg_composer_drop_editor_undo (composer); - - gtk_widget_show ((GtkWidget *) composer); -} - -/* Editing messages... */ - -static void -edit_message (CamelMimeMessage *message, CamelFolder *drafts, const char *uid) -{ - EMsgComposer *composer; - - composer = e_msg_composer_new_with_message (message); - em_composer_utils_setup_callbacks (composer, NULL, NULL, 0, 0, drafts, uid); - e_msg_composer_unset_changed (composer); - e_msg_composer_drop_editor_undo (composer); - - gtk_widget_show (GTK_WIDGET (composer)); -} - -/** - * em_utils_edit_message: - * @message: message to edit - * - * Opens a composer filled in with the headers/mime-parts/etc of - * @message. - **/ -void -em_utils_edit_message (CamelMimeMessage *message) -{ - g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); - - edit_message (message, NULL, NULL); -} - -static void -edit_messages (CamelFolder *folder, GPtrArray *uids, GPtrArray *msgs, void *user_data) -{ - int i; - - if (msgs == NULL) - return; - - for (i = 0; i < msgs->len; i++) { - camel_medium_remove_header (CAMEL_MEDIUM (msgs->pdata[i]), "X-Mailer"); - - edit_message (msgs->pdata[i], folder, uids->pdata[i]); - } -} - -/** - * em_utils_edit_messages: - * @folder: folder containing messages to edit - * @uids: uids of messages to edit - * - * Opens a composer for each message to be edited. - **/ -void -em_utils_edit_messages (CamelFolder *folder, GPtrArray *uids) -{ - g_return_if_fail (CAMEL_IS_FOLDER (folder)); - g_return_if_fail (uids != NULL); - - mail_get_messages (folder, uids, edit_messages, NULL); -} - -/* Forwarding messages... */ - -static void -forward_attached (CamelFolder *folder, GPtrArray *messages, CamelMimePart *part, char *subject, void *user_data) -{ - EMsgComposer *composer; - - if (part == NULL) - return; - - composer = create_new_composer (); - e_msg_composer_set_headers (composer, NULL, NULL, NULL, NULL, subject); - e_msg_composer_attach (composer, part); - - e_msg_composer_unset_changed (composer); - e_msg_composer_drop_editor_undo (composer); - - gtk_widget_show (GTK_WIDGET (composer)); -} - -/** - * em_utils_forward_attached: - * @folder: folder containing messages to forward - * @uids: uids of messages to forward - * - * If there is more than a single message in @uids, a multipart/digest - * will be constructed and attached to a new composer window preset - * with the appropriate header defaults for forwarding the first - * message in the list. If only one message is to be forwarded, it is - * forwarded as a simple message/rfc822 attachment. - **/ -void -em_utils_forward_attached (CamelFolder *folder, GPtrArray *uids) -{ - g_return_if_fail (CAMEL_IS_FOLDER (folder)); - g_return_if_fail (uids != NULL); - - mail_build_attachment (folder, uids, forward_attached, NULL); -} - -static void -forward_non_attached (GPtrArray *messages, int style) -{ - CamelMimeMessage *message; - CamelDataWrapper *wrapper; - EMsgComposer *composer; - char *subject, *text; - int i; - guint32 flags; - - if (messages->len == 0) - return; - - flags = EM_FORMAT_QUOTE_HEADERS; - if (style == MAIL_CONFIG_FORWARD_QUOTED) - flags |= EM_FORMAT_QUOTE_CITE; - - for (i = 0; i < messages->len; i++) { - message = messages->pdata[i]; - subject = mail_tool_generate_forward_subject (message); - - text = em_utils_message_to_html (message, _("-------- Forwarded Message --------"), flags); - - if (text) { - composer = create_new_composer (); - e_msg_composer_set_headers (composer, NULL, NULL, NULL, NULL, subject); - - wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (message)); - if (CAMEL_IS_MULTIPART (wrapper)) - e_msg_composer_add_message_attachments (composer, message, FALSE); - - e_msg_composer_set_body_text (composer, text); - - e_msg_composer_unset_changed (composer); - e_msg_composer_drop_editor_undo (composer); - - gtk_widget_show (GTK_WIDGET (composer)); - - g_free (text); - } - - g_free (subject); - } -} - -static void -forward_inline (CamelFolder *folder, GPtrArray *uids, GPtrArray *messages, void *user_data) -{ - forward_non_attached (messages, MAIL_CONFIG_FORWARD_INLINE); -} - -/** - * em_utils_forward_inline: - * @folder: folder containing messages to forward - * @uids: uids of messages to forward - * - * Forwards each message in the 'inline' form, each in its own composer window. - **/ -void -em_utils_forward_inline (CamelFolder *folder, GPtrArray *uids) -{ - g_return_if_fail (CAMEL_IS_FOLDER (folder)); - g_return_if_fail (uids != NULL); - - mail_get_messages (folder, uids, forward_inline, NULL); -} - -static void -forward_quoted (CamelFolder *folder, GPtrArray *uids, GPtrArray *messages, void *user_data) -{ - forward_non_attached (messages, MAIL_CONFIG_FORWARD_QUOTED); -} - -/** - * em_utils_forward_quoted: - * @folder: folder containing messages to forward - * @uids: uids of messages to forward - * - * Forwards each message in the 'quoted' form (each line starting with - * a "> "), each in its own composer window. - **/ -void -em_utils_forward_quoted (CamelFolder *folder, GPtrArray *uids) -{ - g_return_if_fail (CAMEL_IS_FOLDER (folder)); - g_return_if_fail (uids != NULL); - - mail_get_messages (folder, uids, forward_quoted, NULL); -} - -/** - * em_utils_forward_message: - * @parent: parent window - * @message: message to be forwarded - * - * Forwards a message in the user's configured default style. - **/ -void -em_utils_forward_message (CamelMimeMessage *message) -{ - GPtrArray *messages; - CamelMimePart *part; - GConfClient *gconf; - char *subject; - int mode; - - messages = g_ptr_array_new (); - g_ptr_array_add (messages, message); - - gconf = mail_config_get_gconf_client (); - mode = gconf_client_get_int (gconf, "/apps/evolution/mail/format/forward_style", NULL); - - switch (mode) { - case MAIL_CONFIG_FORWARD_ATTACHED: - default: - part = mail_tool_make_message_attachment (message); - - subject = mail_tool_generate_forward_subject (message); - - forward_attached (NULL, messages, part, subject, NULL); - camel_object_unref (part); - g_free (subject); - break; - case MAIL_CONFIG_FORWARD_INLINE: - forward_non_attached (messages, MAIL_CONFIG_FORWARD_INLINE); - break; - case MAIL_CONFIG_FORWARD_QUOTED: - forward_non_attached (messages, MAIL_CONFIG_FORWARD_QUOTED); - break; - } - - g_ptr_array_free (messages, TRUE); -} - -/** - * em_utils_forward_messages: - * @folder: folder containing messages to forward - * @uids: uids of messages to forward - * - * Forwards a group of messages in the user's configured default - * style. - **/ -void -em_utils_forward_messages (CamelFolder *folder, GPtrArray *uids) -{ - GConfClient *gconf; - int mode; - - gconf = mail_config_get_gconf_client (); - mode = gconf_client_get_int (gconf, "/apps/evolution/mail/format/forward_style", NULL); - - switch (mode) { - case MAIL_CONFIG_FORWARD_ATTACHED: - default: - em_utils_forward_attached (folder, uids); - break; - case MAIL_CONFIG_FORWARD_INLINE: - em_utils_forward_inline (folder, uids); - break; - case MAIL_CONFIG_FORWARD_QUOTED: - em_utils_forward_quoted (folder, uids); - break; - } -} - -/* Redirecting messages... */ - -static EMsgComposer * -redirect_get_composer (CamelMimeMessage *message) -{ - EMsgComposer *composer; - EAccount *account; - - /* QMail will refuse to send a message if it finds one of - it's Delivered-To headers in the message, so remove all - Delivered-To headers. Fixes bug #23635. */ - while (camel_medium_get_header (CAMEL_MEDIUM (message), "Delivered-To")) - camel_medium_remove_header (CAMEL_MEDIUM (message), "Delivered-To"); - - account = guess_account (message, NULL); - - composer = e_msg_composer_new_redirect (message, account ? account->name : NULL); - - em_composer_utils_setup_default_callbacks (composer); - - return composer; -} - -/** - * em_utils_redirect_message: - * @message: message to redirect - * - * Opens a composer to redirect @message (Note: only headers will be - * editable). Adds Resent-From/Resent-To/etc headers. - **/ -void -em_utils_redirect_message (CamelMimeMessage *message) -{ - EMsgComposer *composer; - CamelDataWrapper *wrapper; - - g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message)); - - composer = redirect_get_composer (message); - - wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (message)); - - gtk_widget_show (GTK_WIDGET (composer)); - e_msg_composer_unset_changed (composer); - e_msg_composer_drop_editor_undo (composer); -} - -static void -redirect_msg (CamelFolder *folder, const char *uid, CamelMimeMessage *message, void *user_data) -{ - if (message == NULL) - return; - - em_utils_redirect_message (message); -} - -/** - * em_utils_redirect_message_by_uid: - * @folder: folder containing message to be redirected - * @uid: uid of message to be redirected - * - * Opens a composer to redirect the message (Note: only headers will - * be editable). Adds Resent-From/Resent-To/etc headers. - **/ -void -em_utils_redirect_message_by_uid (CamelFolder *folder, const char *uid) -{ - g_return_if_fail (CAMEL_IS_FOLDER (folder)); - g_return_if_fail (uid != NULL); - - mail_get_message (folder, uid, redirect_msg, NULL, mail_thread_new); -} - -/* Replying to messages... */ - -static GHashTable * -generate_account_hash (void) -{ - GHashTable *account_hash; - EAccount *account, *def; - EAccountList *accounts; - EIterator *iter; - - accounts = mail_config_get_accounts (); - account_hash = g_hash_table_new (camel_strcase_hash, camel_strcase_equal); - - /* add the default account to the hash first */ - if ((def = mail_config_get_default_account ())) { - if (def->id->address) - g_hash_table_insert (account_hash, (char *) def->id->address, (void *) def); - } - - iter = e_list_get_iterator ((EList *) accounts); - while (e_iterator_is_valid (iter)) { - account = (EAccount *) e_iterator_get (iter); - - if (account->id->address) { - EAccount *acnt; - - /* Accounts with identical email addresses that are enabled - * take precedence over the accounts that aren't. If all - * accounts with matching email addresses are disabled, then - * the first one in the list takes precedence. The default - * account always takes precedence no matter what. - */ - acnt = g_hash_table_lookup (account_hash, account->id->address); - if (acnt && acnt != def && !acnt->enabled && account->enabled) { - g_hash_table_remove (account_hash, acnt->id->address); - acnt = NULL; - } - - if (!acnt) - g_hash_table_insert (account_hash, (char *) account->id->address, (void *) account); - } - - e_iterator_next (iter); - } - - g_object_unref (iter); - - return account_hash; -} - -static EDestination ** -em_utils_camel_address_to_destination (CamelInternetAddress *iaddr) -{ - EDestination *dest, **destv; - int n, i, j; - - if (iaddr == NULL) - return NULL; - - if ((n = camel_address_length ((CamelAddress *) iaddr)) == 0) - return NULL; - - destv = g_malloc (sizeof (EDestination *) * (n + 1)); - for (i = 0, j = 0; i < n; i++) { - const char *name, *addr; - - if (camel_internet_address_get (iaddr, i, &name, &addr)) { - dest = e_destination_new (); - e_destination_set_name (dest, name); - e_destination_set_email (dest, addr); - - destv[j++] = dest; - } - } - - if (j == 0) { - g_free (destv); - return NULL; - } - - destv[j] = NULL; - - return destv; -} - -static EMsgComposer * -reply_get_composer (CamelMimeMessage *message, EAccount *account, - CamelInternetAddress *to, CamelInternetAddress *cc, - CamelFolder *folder, const char *postto) -{ - const char *message_id, *references; - EDestination **tov, **ccv; - EMsgComposer *composer; - char *subject; - - g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL); - g_return_val_if_fail (to == NULL || CAMEL_IS_INTERNET_ADDRESS (to), NULL); - g_return_val_if_fail (cc == NULL || CAMEL_IS_INTERNET_ADDRESS (cc), NULL); - - /* construct the tov/ccv */ - tov = em_utils_camel_address_to_destination (to); - ccv = em_utils_camel_address_to_destination (cc); - - if (tov || ccv) { - if (postto) - composer = e_msg_composer_new_with_type (E_MSG_COMPOSER_MAIL_POST); - else - composer = e_msg_composer_new_with_type (E_MSG_COMPOSER_MAIL); - } else - composer = e_msg_composer_new_with_type (E_MSG_COMPOSER_POST); - - /* Set the subject of the new message. */ - if ((subject = (char *) camel_mime_message_get_subject (message))) { - if (strncasecmp (subject, "Re: ", 4) != 0) - subject = g_strdup_printf ("Re: %s", subject); - else - subject = g_strdup (subject); - } else { - subject = g_strdup (""); - } - - e_msg_composer_set_headers (composer, account ? account->name : NULL, tov, ccv, NULL, subject); - - g_free (subject); - - /* add post-to, if nessecary */ - if (postto) { - char *store_url = NULL; - - if (folder) { - store_url = camel_url_to_string (CAMEL_SERVICE (folder->parent_store)->url, CAMEL_URL_HIDE_ALL); - if (store_url[strlen (store_url) - 1] == '/') - store_url[strlen (store_url)-1] = '\0'; - } - - e_msg_composer_hdrs_set_post_to_base (E_MSG_COMPOSER_HDRS (composer->hdrs), store_url ? store_url : "", postto); - g_free (store_url); - } - - /* Add In-Reply-To and References. */ - message_id = camel_medium_get_header (CAMEL_MEDIUM (message), "Message-Id"); - references = camel_medium_get_header (CAMEL_MEDIUM (message), "References"); - if (message_id) { - char *reply_refs; - - e_msg_composer_add_header (composer, "In-Reply-To", message_id); - - if (references) - reply_refs = g_strdup_printf ("%s %s", references, message_id); - else - reply_refs = g_strdup (message_id); - - e_msg_composer_add_header (composer, "References", reply_refs); - g_free (reply_refs); - } else if (references) { - e_msg_composer_add_header (composer, "References", references); - } - - e_msg_composer_drop_editor_undo (composer); - - return composer; -} - -static EAccount * -guess_account_folder(CamelFolder *folder) -{ - EAccount *account; - char *tmp; - - tmp = camel_url_to_string(CAMEL_SERVICE(folder->parent_store)->url, CAMEL_URL_HIDE_ALL); - account = mail_config_get_account_by_source_url(tmp); - g_free(tmp); - - return account; -} - -static EAccount * -guess_account (CamelMimeMessage *message, CamelFolder *folder) -{ - GHashTable *account_hash = NULL; - EAccount *account = NULL; - const char *tmp; - int i, j; - char *types[2] = { CAMEL_RECIPIENT_TYPE_TO, CAMEL_RECIPIENT_TYPE_CC }; - - /* check for newsgroup header */ - if (folder - && camel_medium_get_header((CamelMedium *)message, "Newsgroups") - && (account = guess_account_folder(folder))) - return account; - - /* then recipient (to/cc) in account table */ - account_hash = generate_account_hash (); - for (j=0;account == NULL && j<2;j++) { - const CamelInternetAddress *to; - - to = camel_mime_message_get_recipients(message, types[j]); - if (to) { - for (i = 0; camel_internet_address_get(to, i, NULL, &tmp); i++) { - account = g_hash_table_lookup(account_hash, tmp); - if (account) - break; - } - } - } - g_hash_table_destroy(account_hash); - - /* then message source */ - if (account == NULL - && (tmp = camel_mime_message_get_source(message))) - account = mail_config_get_account_by_source_url(tmp); - - /* and finally, source folder */ - if (account == NULL - && folder) - account = guess_account_folder(folder); - - return account; -} - -static void -get_reply_sender (CamelMimeMessage *message, CamelInternetAddress **to, const char **postto) -{ - const CamelInternetAddress *reply_to; - const char *name, *addr, *posthdr; - int i; - - /* check whether there is a 'Newsgroups: ' header in there */ - posthdr = camel_medium_get_header (CAMEL_MEDIUM (message), "Newsgroups"); - if (posthdr && postto) { - *postto = posthdr; - while (**postto == ' ') - (*postto)++; - return; - } - - reply_to = camel_mime_message_get_reply_to (message); - if (!reply_to) - reply_to = camel_mime_message_get_from (message); - - if (reply_to) { - *to = camel_internet_address_new (); - - for (i = 0; camel_internet_address_get (reply_to, i, &name, &addr); i++) - camel_internet_address_add (*to, name, addr); - } -} - -static gboolean -get_reply_list (CamelMimeMessage *message, CamelInternetAddress **to) -{ - const char *header, *p; - char *addr; - - /* Examples: - * - * List-Post: <mailto:list@host.com> - * List-Post: <mailto:moderator@host.com?subject=list%20posting> - * List-Post: NO (posting not allowed on this list) - */ - if (!(header = camel_medium_get_header ((CamelMedium *) message, "List-Post"))) - return FALSE; - - while (*header == ' ' || *header == '\t') - header++; - - /* check for NO */ - if (!strncasecmp (header, "NO", 2)) - return FALSE; - - /* Search for the first mailto angle-bracket enclosed URL. - * (See rfc2369, Section 2, paragraph 3 for details) */ - if (!(header = camel_strstrcase (header, "<mailto:"))) - return FALSE; - - header += 8; - - p = header; - while (*p && !strchr ("?>", *p)) - p++; - - addr = g_strndup (header, p - header); - - *to = camel_internet_address_new (); - camel_internet_address_add (*to, NULL, addr); - - g_free (addr); - - return TRUE; -} - -static void -concat_unique_addrs (CamelInternetAddress *dest, const CamelInternetAddress *src, GHashTable *rcpt_hash) -{ - const char *name, *addr; - int i; - - for (i = 0; camel_internet_address_get (src, i, &name, &addr); i++) { - if (!g_hash_table_lookup (rcpt_hash, addr)) { - camel_internet_address_add (dest, name, addr); - g_hash_table_insert (rcpt_hash, (char *) addr, GINT_TO_POINTER (1)); - } - } -} - -static void -get_reply_all (CamelMimeMessage *message, CamelInternetAddress **to, CamelInternetAddress **cc, const char **postto) -{ - const CamelInternetAddress *reply_to, *to_addrs, *cc_addrs; - const char *name, *addr, *posthdr; - GHashTable *rcpt_hash; - int i; - - /* check whether there is a 'Newsgroups: ' header in there */ - posthdr = camel_medium_get_header (CAMEL_MEDIUM(message), "Newsgroups"); - if (posthdr && postto) { - *postto = posthdr; - while (**postto == ' ') - (*postto)++; - } - - rcpt_hash = generate_account_hash (); - - reply_to = camel_mime_message_get_reply_to (message); - if (!reply_to) - reply_to = camel_mime_message_get_from (message); - - to_addrs = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO); - cc_addrs = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC); - - *to = camel_internet_address_new (); - *cc = camel_internet_address_new (); - - if (reply_to) { - for (i = 0; camel_internet_address_get (reply_to, i, &name, &addr); i++) { - /* ignore references to the Reply-To address in the To and Cc lists */ - if (addr && !g_hash_table_lookup (rcpt_hash, addr)) { - /* In the case that we are doing a Reply-To-All, we do not want - to include the user's email address because replying to oneself - is kinda silly. */ - - camel_internet_address_add (*to, name, addr); - g_hash_table_insert (rcpt_hash, (char *) addr, GINT_TO_POINTER (1)); - } - } - } - - concat_unique_addrs (*cc, to_addrs, rcpt_hash); - concat_unique_addrs (*cc, cc_addrs, rcpt_hash); - - /* promote the first Cc: address to To: if To: is empty */ - if (camel_address_length ((CamelAddress *) *to) == 0 && camel_address_length ((CamelAddress *) *cc) > 0) { - camel_internet_address_get (*cc, 0, &name, &addr); - camel_internet_address_add (*to, name, addr); - camel_address_remove ((CamelAddress *) *cc, 0); - } - - g_hash_table_destroy (rcpt_hash); -} - -static void -composer_set_body (EMsgComposer *composer, CamelMimeMessage *message) -{ - const CamelInternetAddress *sender; - char *text, *credits, format[256]; - const char *name, *addr; - CamelMimePart *part; - GConfClient *gconf; - time_t date; - int date_offset; - - gconf = mail_config_get_gconf_client (); - - switch (gconf_client_get_int (gconf, "/apps/evolution/mail/format/reply_style", NULL)) { - case MAIL_CONFIG_REPLY_DO_NOT_QUOTE: - /* do nothing */ - break; - case MAIL_CONFIG_REPLY_ATTACH: - /* attach the original message as an attachment */ - part = mail_tool_make_message_attachment (message); - e_msg_composer_attach (composer, part); - camel_object_unref (part); - break; - case MAIL_CONFIG_REPLY_QUOTED: - default: - /* do what any sane user would want when replying... */ - sender = camel_mime_message_get_from (message); - if (sender != NULL && camel_address_length (CAMEL_ADDRESS (sender)) > 0) { - camel_internet_address_get (sender, 0, &name, &addr); - } else { - name = _("an unknown sender"); - } - - date = camel_mime_message_get_date(message, &date_offset); - /* Convert to UTC */ - date += (date_offset / 100) * 60 * 60; - date += (date_offset % 100) * 60; - - /* translators: attribution string used when quoting messages, - it must contain a single single %%+05d followed by a single '%%s' */ - e_utf8_strftime(format, sizeof(format), _("On %a, %Y-%m-%d at %H:%M %%+05d, %%s wrote:"), gmtime(&date)); - credits = g_strdup_printf(format, date_offset, name && *name ? name : addr); - text = em_utils_message_to_html(message, credits, EM_FORMAT_QUOTE_CITE); - g_free (credits); - e_msg_composer_set_body_text(composer, text); - g_free (text); - break; - } - - e_msg_composer_drop_editor_undo (composer); -} - -/** - * em_utils_reply_to_message: - * @message: message to reply to - * @mode: reply mode - * - * Creates a new composer ready to reply to @message. - **/ -void -em_utils_reply_to_message (CamelMimeMessage *message, int mode) -{ - CamelInternetAddress *to = NULL, *cc = NULL; - EMsgComposer *composer; - EAccount *account; - - account = guess_account (message, NULL); - - switch (mode) { - case REPLY_MODE_SENDER: - get_reply_sender (message, &to, NULL); - break; - case REPLY_MODE_LIST: - if (get_reply_list (message, &to)) - break; - case REPLY_MODE_ALL: - get_reply_all (message, &to, &cc, NULL); - break; - } - - composer = reply_get_composer (message, account, to, cc, NULL, NULL); - e_msg_composer_add_message_attachments (composer, message, TRUE); - - if (to != NULL) - camel_object_unref (to); - - if (cc != NULL) - camel_object_unref (cc); - - composer_set_body (composer, message); - - em_composer_utils_setup_default_callbacks (composer); - - gtk_widget_show (GTK_WIDGET (composer)); - e_msg_composer_unset_changed (composer); -} - -static void -reply_to_message (CamelFolder *folder, const char *uid, CamelMimeMessage *message, void *user_data) -{ - CamelInternetAddress *to = NULL, *cc = NULL; - const char *postto = NULL; - EMsgComposer *composer; - EAccount *account; - guint32 flags; - int mode; - - if (message == NULL) - return; - - mode = GPOINTER_TO_INT (user_data); - - account = guess_account (message, folder); - flags = CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_SEEN; - - switch (mode) { - case REPLY_MODE_SENDER: - get_reply_sender (message, &to, &postto); - break; - case REPLY_MODE_LIST: - flags |= CAMEL_MESSAGE_ANSWERED_ALL; - if (get_reply_list (message, &to)) - break; - case REPLY_MODE_ALL: - flags |= CAMEL_MESSAGE_ANSWERED_ALL; - get_reply_all (message, &to, &cc, &postto); - break; - } - - composer = reply_get_composer (message, account, to, cc, folder, postto); - e_msg_composer_add_message_attachments (composer, message, TRUE); - - if (to != NULL) - camel_object_unref (to); - - if (cc != NULL) - camel_object_unref (cc); - - composer_set_body (composer, message); - - em_composer_utils_setup_callbacks (composer, folder, uid, flags, flags, NULL, NULL); - - gtk_widget_show (GTK_WIDGET (composer)); - e_msg_composer_unset_changed (composer); -} - -/** - * em_utils_reply_to_message_by_uid: - * @folder: folder containing message to reply to - * @uid: message uid - * @mode: reply mode - * - * Creates a new composer ready to reply to the message referenced by - * @folder and @uid. - **/ -void -em_utils_reply_to_message_by_uid (CamelFolder *folder, const char *uid, int mode) -{ - g_return_if_fail (CAMEL_IS_FOLDER (folder)); - g_return_if_fail (uid != NULL); - - mail_get_message (folder, uid, reply_to_message, GINT_TO_POINTER (mode), mail_thread_new); -} - -/* Posting replies... */ - -static void -post_reply_to_message (CamelFolder *folder, const char *uid, CamelMimeMessage *message, void *user_data) -{ - /* FIXME: would be nice if this shared more code with reply_get_composer() */ - const char *message_id, *references; - CamelInternetAddress *to = NULL; - EDestination **tov = NULL; - EMsgComposer *composer; - char *subject, *url; - EAccount *account; - guint32 flags; - - if (message == NULL) - return; - - account = guess_account (message, folder); - flags = CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_SEEN; - - get_reply_sender (message, &to, NULL); - - composer = e_msg_composer_new_with_type (E_MSG_COMPOSER_MAIL_POST); - - /* construct the tov/ccv */ - tov = em_utils_camel_address_to_destination (to); - - /* Set the subject of the new message. */ - if ((subject = (char *) camel_mime_message_get_subject (message))) { - if (strncasecmp (subject, "Re: ", 4) != 0) - subject = g_strdup_printf ("Re: %s", subject); - else - subject = g_strdup (subject); - } else { - subject = g_strdup (""); - } - - e_msg_composer_set_headers (composer, account ? account->name : NULL, tov, NULL, NULL, subject); - - g_free (subject); - - url = mail_tools_folder_to_url (folder); - e_msg_composer_hdrs_set_post_to ((EMsgComposerHdrs *) composer->hdrs, url); - g_free (url); - - /* Add In-Reply-To and References. */ - message_id = camel_medium_get_header (CAMEL_MEDIUM (message), "Message-Id"); - references = camel_medium_get_header (CAMEL_MEDIUM (message), "References"); - if (message_id) { - char *reply_refs; - - e_msg_composer_add_header (composer, "In-Reply-To", message_id); - - if (references) - reply_refs = g_strdup_printf ("%s %s", references, message_id); - else - reply_refs = g_strdup (message_id); - - e_msg_composer_add_header (composer, "References", reply_refs); - g_free (reply_refs); - } else if (references) { - e_msg_composer_add_header (composer, "References", references); - } - - e_msg_composer_drop_editor_undo (composer); - - e_msg_composer_add_message_attachments (composer, message, TRUE); - - if (to != NULL) - camel_object_unref (to); - - composer_set_body (composer, message); - - em_composer_utils_setup_callbacks (composer, folder, uid, flags, flags, NULL, NULL); - - gtk_widget_show (GTK_WIDGET (composer)); - e_msg_composer_unset_changed (composer); -} - -/** - * em_utils_post_reply_to_message_by_uid: - * @folder: folder containing message to reply to - * @uid: message uid - * @mode: reply mode - * - * Creates a new composer (post mode) ready to reply to the message - * referenced by @folder and @uid. - **/ -void -em_utils_post_reply_to_message_by_uid (CamelFolder *folder, const char *uid) -{ - g_return_if_fail (CAMEL_IS_FOLDER (folder)); - g_return_if_fail (uid != NULL); - - mail_get_message (folder, uid, post_reply_to_message, NULL, mail_thread_new); -} - /* Saving messages... */ static GtkFileSelection * diff --git a/mail/em-utils.h b/mail/em-utils.h index 4c24316b60..368265ba69 100644 --- a/mail/em-utils.h +++ b/mail/em-utils.h @@ -20,7 +20,6 @@ * */ - #ifndef __EM_UTILS_H__ #define __EM_UTILS_H__ @@ -40,7 +39,6 @@ struct _CamelMimeMessage; struct _CamelMimePart; struct _GtkSelectionData; struct _GtkAdjustment; -struct _EMsgComposer; gboolean em_utils_prompt_user(struct _GtkWindow *parent, const char *promptkey, const char *tag, const char *arg0, ...); @@ -53,40 +51,6 @@ gboolean em_utils_check_user_can_send_mail (struct _GtkWidget *parent); void em_utils_edit_filters (struct _GtkWidget *parent); void em_utils_edit_vfolders (struct _GtkWidget *parent); -void em_utils_composer_send_cb(struct _EMsgComposer *composer, gpointer user_data); -void em_utils_composer_save_draft_cb(struct _EMsgComposer *composer, int quit, gpointer user_data); - -void em_utils_compose_new_message (void); - -/* FIXME: mailto? url? should make up its mind what its called. imho use 'uri' */ -void em_utils_compose_new_message_with_mailto (const char *url); -void em_utils_post_to_folder (struct _CamelFolder *folder); -void em_utils_post_to_url (const char *url); - -void em_utils_edit_message (struct _CamelMimeMessage *message); -void em_utils_edit_messages (struct _CamelFolder *folder, GPtrArray *uids); - -void em_utils_forward_attached (struct _CamelFolder *folder, GPtrArray *uids); -void em_utils_forward_inline (struct _CamelFolder *folder, GPtrArray *uids); -void em_utils_forward_quoted (struct _CamelFolder *folder, GPtrArray *uids); - -void em_utils_forward_message (struct _CamelMimeMessage *msg); -void em_utils_forward_messages (struct _CamelFolder *folder, GPtrArray *uids); - -void em_utils_redirect_message (struct _CamelMimeMessage *message); -void em_utils_redirect_message_by_uid (struct _CamelFolder *folder, const char *uid); - -enum { - REPLY_MODE_SENDER, - REPLY_MODE_ALL, - REPLY_MODE_LIST -}; - -void em_utils_reply_to_message (struct _CamelMimeMessage *message, int mode); -void em_utils_reply_to_message_by_uid (struct _CamelFolder *folder, const char *uid, int mode); - -void em_utils_post_reply_to_message_by_uid (struct _CamelFolder *folder, const char *uid); - void em_utils_save_part(struct _GtkWidget *parent, const char *prompt, struct _CamelMimePart *part); gboolean em_utils_save_part_to_file(struct _GtkWidget *parent, const char *filename, struct _CamelMimePart *part); void em_utils_save_messages (struct _GtkWidget *parent, struct _CamelFolder *folder, GPtrArray *uids); |