From 3262cba6ce9e55ae6afd71dd7cee7173992fa493 Mon Sep 17 00:00:00 2001 From: Not Zed Date: Fri, 8 Apr 2005 04:34:25 +0000 Subject: removed. (em_utils_handle_receipt): asynchronously load message if none is 2005-04-08 Not Zed * em-composer-utils.c (em_utils_ask_receipt): removed. (em_utils_handle_receipt): asynchronously load message if none is supplied. * em-folder-view.c (emfv_set_seen): pass message in. * em-composer-utils.c (em_utils_ask_receipt): strip leading lwsp on the disposition address. (em_utils_ask_receipt): added a comment about which rfc. (em_utils_handle_receipt): merged ask_receipt into here. Changed api to take a message instead, so we don't have to load the message every time. Also only set receipt-handled if we have a receipt requested in the first place. 2005-04-05 ERDI Gergo * mail-errors.xml: Added new dialog for receipt requests * em-composer-utils.c (em_utils_guess_account): Made guess_account public, to be callable from em-folder-view (em_utils_send_receipt): New function to send an RFC 2298-compliant message delivery notification svn path=/trunk/; revision=29185 --- mail/ChangeLog | 25 +++++++ mail/em-account-editor.c | 61 +++++++++++++++ mail/em-composer-utils.c | 189 ++++++++++++++++++++++++++++++++++++++++++++++- mail/em-composer-utils.h | 4 + mail/em-folder-view.c | 21 +++++- mail/mail-config.glade | 114 ++++++++++++++++++++++++++++ mail/mail-errors.xml | 8 ++ mail/mail-errors.xml.h | 7 ++ 8 files changed, 426 insertions(+), 3 deletions(-) diff --git a/mail/ChangeLog b/mail/ChangeLog index c06c0409db..6fc1517054 100644 --- a/mail/ChangeLog +++ b/mail/ChangeLog @@ -1,3 +1,28 @@ +2005-04-08 Not Zed + + * em-composer-utils.c (em_utils_ask_receipt): removed. + (em_utils_handle_receipt): asynchronously load message if none is + supplied. + + * em-folder-view.c (emfv_set_seen): pass message in. + + * em-composer-utils.c (em_utils_ask_receipt): strip leading lwsp + on the disposition address. + (em_utils_ask_receipt): added a comment about which rfc. + (em_utils_handle_receipt): merged ask_receipt into here. Changed + api to take a message instead, so we don't have to load the + message every time. Also only set receipt-handled if we have a + receipt requested in the first place. + +2005-04-05 ERDI Gergo + + * mail-errors.xml: Added new dialog for receipt requests + + * em-composer-utils.c (em_utils_guess_account): Made guess_account + public, to be callable from em-folder-view + (em_utils_send_receipt): New function to send an RFC + 2298-compliant message delivery notification + 2005-04-05 Not Zed ** See bug #74320 diff --git a/mail/em-account-editor.c b/mail/em-account-editor.c index 3e25485134..56c7b50aad 100644 --- a/mail/em-account-editor.c +++ b/mail/em-account-editor.c @@ -723,6 +723,64 @@ emae_setup_signatures(EMAccountEditor *emae, GladeXML *xml) return (GtkWidget *)dropdown; } +static void +emae_receipt_policy_changed(GtkComboBox *dropdown, EMAccountEditor *emae) +{ + int id = gtk_combo_box_get_active(dropdown); + GtkTreeModel *model; + GtkTreeIter iter; + EAccountReceiptPolicy policy; + + if (id != -1) { + model = gtk_combo_box_get_model(dropdown); + if (gtk_tree_model_iter_nth_child(model, &iter, NULL, id)) { + gtk_tree_model_get(model, &iter, 1, &policy, -1); + e_account_set_int (emae->account, E_ACCOUNT_RECEIPT_POLICY, policy); + } + } + +} + +static GtkWidget * +emae_setup_receipt_policy (EMAccountEditor *emae, GladeXML *xml) +{ + GtkComboBox *dropdown = (GtkComboBox *)glade_xml_get_widget(xml, "receipt_policy_dropdown"); + GtkListStore *store; + int i = 0, active = 0; + GtkTreeIter iter; + EAccountReceiptPolicy current = emae->account->receipt_policy; + static struct { + EAccountReceiptPolicy policy; + char *label; + } receipt_policies[] = { + { E_ACCOUNT_RECEIPT_NEVER, N_("Never") }, + { E_ACCOUNT_RECEIPT_ALWAYS, N_("Always") }, + { E_ACCOUNT_RECEIPT_ASK, N_("Ask for each message") } + }; + + gtk_widget_show((GtkWidget *)dropdown); + + store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT); + + for (i = 0; i < 3; ++i) { + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, _(receipt_policies[i].label), + 1, receipt_policies[i].policy, + -1); + if (current == receipt_policies[i].policy) + active = i; + } + + gtk_combo_box_set_model(dropdown, (GtkTreeModel *)store); + gtk_combo_box_set_active(dropdown, active); + + g_signal_connect(dropdown, "changed", G_CALLBACK(emae_receipt_policy_changed), emae); + gtk_widget_set_sensitive((GtkWidget *)dropdown, e_account_writable(emae->account, E_ACCOUNT_RECEIPT_POLICY)); + + return (GtkWidget *)dropdown; +} + static void emae_account_entry_changed(GtkEntry *entry, EMAccountEditor *emae) { @@ -2081,6 +2139,9 @@ emae_defaults_page(EConfig *ec, EConfigItem *item, struct _GtkWidget *parent, st gtk_widget_set_sensitive((GtkWidget *)gui->restore_folders_button, e_account_writable(emae->account, E_ACCOUNT_SENT_FOLDER_URI) || e_account_writable(emae->account, E_ACCOUNT_DRAFTS_FOLDER_URI)); + + /* Receipt policy */ + emae_setup_receipt_policy (emae, xml); w = glade_xml_get_widget(xml, item->label); gtk_notebook_append_page((GtkNotebook *)parent, w, gtk_label_new(_("Defaults"))); diff --git a/mail/em-composer-utils.c b/mail/em-composer-utils.c index 4f8ecb951a..c018337584 100644 --- a/mail/em-composer-utils.c +++ b/mail/em-composer-utils.c @@ -50,9 +50,10 @@ #include "e-util/e-account-list.h" #include +#include #include -static EAccount *guess_account (CamelMimeMessage *message, CamelFolder *folder); +static EAccount * guess_account (CamelMimeMessage *message, CamelFolder *folder); struct emcs_t { unsigned int ref_count; @@ -190,6 +191,7 @@ composer_send_queued_cb (CamelFolder *folder, CamelMimeMessage *msg, CamelMessag if (emcs && emcs->folder) { /* set any replied flags etc */ camel_folder_set_message_flags (emcs->folder, emcs->uid, emcs->flags, emcs->set); + camel_folder_set_message_user_flag (emcs->folder, emcs->uid, "receipt-handled", TRUE); camel_object_unref (emcs->folder); emcs->folder = NULL; g_free (emcs->uid); @@ -1066,6 +1068,191 @@ em_utils_redirect_message_by_uid (CamelFolder *folder, const char *uid) mail_get_message (folder, uid, redirect_msg, NULL, mail_thread_new); } +static void +emu_handle_receipt_message(CamelFolder *folder, const char *uid, CamelMimeMessage *msg, void *data) +{ + if (msg) + em_utils_handle_receipt(folder, uid, msg); +} + +/* Message disposition notifications, rfc 2298 */ +void +em_utils_handle_receipt (CamelFolder *folder, const char *uid, CamelMimeMessage *msg) +{ + EAccount *account; + const char *addr; + CamelMessageInfo *info; + + info = camel_folder_get_message_info(folder, uid); + if (info == NULL) + return; + + if (camel_message_info_user_flag(info, "receipt-handled")) { + camel_message_info_free(info); + return; + } + + if (msg == NULL) { + mail_get_message(folder, uid, emu_handle_receipt_message, NULL, mail_thread_new); + camel_message_info_free(info); + return; + } + + if ( (addr = camel_medium_get_header((CamelMedium *)msg, "Disposition-Notification-To")) == NULL) { + camel_message_info_free(info); + return; + } + + camel_message_info_set_user_flag(info, "receipt-handled", TRUE); + camel_message_info_free(info); + + account = guess_account(msg, folder); + + /* TODO: should probably decode/format the address, it could be in rfc2047 format */ + if (addr == NULL) { + addr = ""; + } else { + while (camel_mime_is_lwsp(*addr)) + addr++; + } + + if (account->receipt_policy == E_ACCOUNT_RECEIPT_ALWAYS + || (account->receipt_policy == E_ACCOUNT_RECEIPT_ASK + && e_error_run (NULL, "mail:ask-receipt", addr, camel_mime_message_get_subject(msg)) == GTK_RESPONSE_YES)) + em_utils_send_receipt(folder, msg); +} + +static void +em_utils_receipt_done (CamelFolder *folder, CamelMimeMessage *msg, CamelMessageInfo *info, + int queued, const char *appended_uid, void *data) +{ + camel_message_info_free (info); + mail_send (); +} + + +void +em_utils_send_receipt (CamelFolder *folder, CamelMimeMessage *message) +{ + /* See RFC #2298 for a description of message receipts */ + + EAccount *account = guess_account (message, folder); + + CamelMimeMessage *receipt = camel_mime_message_new (); + CamelMultipart *body = camel_multipart_new (); + CamelMimePart *part; + CamelDataWrapper *receipt_text, *receipt_data; + CamelContentType *type; + CamelInternetAddress *addr; + CamelStream *stream; + CamelFolder *out_folder; + CamelMessageInfo *info; + const char *message_id = camel_medium_get_header (CAMEL_MEDIUM (message), "Message-ID"); + const char *message_date = camel_medium_get_header (CAMEL_MEDIUM (message), "Date"); + const char *message_subject = camel_mime_message_get_subject (message); + const char *receipt_address = camel_medium_get_header (CAMEL_MEDIUM (message), "Disposition-Notification-To"); + char *fake_msgid; + char *hostname; + char *self_address, *receipt_subject; + char *ua, *recipient; + + if (!receipt_address) + return; + + /* Collect information for the receipt */ + + /* We use camel_header_msgid_generate () to get a canonical + * hostname, then skip the part leading to '@' */ + fake_msgid = camel_header_msgid_generate (); + for (hostname = fake_msgid; hostname && *hostname != '@'; ++hostname); + ++hostname; + + self_address = account->id->address; + + if (!message_id) + message_id = ""; + if (!message_date) + message_date =""; + + /* Create toplevel container */ + camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (body), + "multipart/report;" + "report-type=\"disposition-notification\""); + camel_multipart_set_boundary (body, NULL); + + /* Create textual receipt */ + receipt_text = camel_data_wrapper_new (); + type = camel_content_type_new ("text", "plain"); + camel_content_type_set_param (type, "format", "flowed"); + camel_data_wrapper_set_mime_type_field (receipt_text, type); + camel_content_type_unref (type); + stream = camel_stream_mem_new (); + camel_stream_printf (stream, + "Your message to %s about \"%s\" on %s has been read.", + self_address, message_subject, message_date); + camel_data_wrapper_construct_from_stream (receipt_text, stream); + camel_object_unref (stream); + + part = camel_mime_part_new (); + camel_medium_set_content_object (CAMEL_MEDIUM (part), receipt_text); + camel_object_unref (receipt_text); + camel_multipart_add_part (body, part); + camel_object_unref (part); + + /* Create the machine-readable receipt */ + receipt_data = camel_data_wrapper_new (); + type = camel_content_type_new ("message", "disposition-notification"); + camel_data_wrapper_set_mime_type_field (receipt_data, type); + camel_content_type_unref (type); + stream = camel_stream_mem_new (); + part = camel_mime_part_new (); + + ua = g_strdup_printf ("%s; %s", hostname, "Evolution " VERSION SUB_VERSION " " VERSION_COMMENT); + recipient = g_strdup_printf ("rfc822; %s", self_address); + + camel_medium_add_header (CAMEL_MEDIUM (part), "Reporting-UA", ua); + camel_medium_add_header (CAMEL_MEDIUM (part), "Final-Recipient", recipient); + camel_medium_add_header (CAMEL_MEDIUM (part), "Original-Message-ID", message_id); + camel_medium_add_header (CAMEL_MEDIUM (part), "Disposition", "manual-action/MDN-sent-manually; displayed"); + + g_free (ua); + g_free (recipient); + g_free (fake_msgid); + + camel_data_wrapper_construct_from_stream (receipt_data, stream); + camel_object_unref (stream); + camel_medium_set_content_object (CAMEL_MEDIUM (part), receipt_data); + camel_object_unref (receipt_data); + camel_multipart_add_part (body, part); + camel_object_unref (part); + + /* Finish creating the message */ + camel_medium_set_content_object (CAMEL_MEDIUM (receipt), CAMEL_DATA_WRAPPER (body)); + camel_object_unref (body); + + receipt_subject = g_strdup_printf ("Delivery Notification for: \"%s\"", message_subject); + camel_mime_message_set_subject (receipt, receipt_subject); + g_free (receipt_subject); + + addr = camel_internet_address_new (); + camel_address_decode (CAMEL_ADDRESS (addr), self_address); + camel_mime_message_set_recipients (receipt, CAMEL_RECIPIENT_TYPE_TO, addr); + camel_object_unref (addr); + + addr = camel_internet_address_new (); + camel_address_decode (CAMEL_ADDRESS (addr), receipt_address); + camel_mime_message_set_from (receipt, addr); + camel_object_unref (addr); + + camel_medium_set_header (CAMEL_MEDIUM (receipt), "Return-Path", "<>"); + + /* Send the receipt */ + out_folder = mail_component_get_folder(NULL, MAIL_COMPONENT_FOLDER_OUTBOX); + info = camel_message_info_new (NULL); + camel_message_info_set_flags (info, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN); + mail_append_mail (out_folder, receipt, info, em_utils_receipt_done, 0); +} + /* Replying to messages... */ static GHashTable * diff --git a/mail/em-composer-utils.h b/mail/em-composer-utils.h index 99b1200346..4ee2e1beba 100644 --- a/mail/em-composer-utils.h +++ b/mail/em-composer-utils.h @@ -34,6 +34,7 @@ struct _CamelFolder; struct _CamelMimeMessage; struct _EMsgComposer; struct _EMFormat; +struct _EAccount; 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); @@ -63,6 +64,9 @@ void em_utils_forward_messages (struct _CamelFolder *folder, GPtrArray *uids, co void em_utils_redirect_message (struct _CamelMimeMessage *message); void em_utils_redirect_message_by_uid (struct _CamelFolder *folder, const char *uid); +void em_utils_handle_receipt (struct _CamelFolder *folder, const char *uid, struct _CamelMimeMessage *msg); +void em_utils_send_receipt (struct _CamelFolder *folder, struct _CamelMimeMessage *message); + enum { REPLY_MODE_SENDER, REPLY_MODE_ALL, diff --git a/mail/em-folder-view.c b/mail/em-folder-view.c index 6b6db82332..8a5690ef85 100644 --- a/mail/em-folder-view.c +++ b/mail/em-folder-view.c @@ -64,6 +64,7 @@ #include #include "widgets/misc/e-charset-picker.h" +#include "widgets/misc/e-error.h" #include #include @@ -125,6 +126,8 @@ static void emfv_setting_setup(EMFolderView *emfv); static void emfv_on_url_cb(GObject *emitter, const char *url, EMFolderView *emfv); static void emfv_on_url(EMFolderView *emfv, const char *uri, const char *nice_uri); +static void emfv_set_seen (EMFolderView *emfv, const char *uid); + static gboolean emfv_popup_menu (GtkWidget *widget); static const EMFolderViewEnable emfv_enable_map[]; @@ -441,6 +444,8 @@ em_folder_view_open_selected(EMFolderView *emfv) em_folder_view_set_folder((EMFolderView *)emmb, emfv->folder, emfv->folder_uri); em_folder_view_set_message((EMFolderView *)emmb, views->pdata[i], FALSE); gtk_widget_show(emmb->window); + /* TODO: this loads the message twice (!) */ + em_utils_handle_receipt (emfv->folder, uids->pdata[i], NULL); g_free(views->pdata[i]); } g_ptr_array_free(views, TRUE); @@ -2101,7 +2106,7 @@ do_mark_seen (gpointer user_data) MessageList *list = emfv->list; if (mst->uid && list->cursor_uid && !strcmp (mst->uid, list->cursor_uid)) - camel_folder_set_message_flags (emfv->folder, mst->uid, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN); + emfv_set_seen (emfv, mst->uid); return FALSE; } @@ -2147,7 +2152,7 @@ emfv_list_done_message_selected(CamelFolder *folder, const char *uid, CamelMimeM emfv->priv->seen_id = g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, emfv->mark_seen_timeout, (GSourceFunc)do_mark_seen, mst, (GDestroyNotify)mst_free); } else { - camel_folder_set_message_flags(emfv->folder, uid, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN); + emfv_set_seen (emfv, uid); } } @@ -2421,6 +2426,18 @@ emfv_format_popup_event(EMFormatHTMLDisplay *efhd, GdkEventButton *event, const return TRUE; } +static void +emfv_set_seen(EMFolderView *emfv, const char *uid) +{ + guint32 old_flags = camel_folder_get_message_flags(emfv->folder, uid); + + /* If we're setting the SEEN flag on a message, handle receipt requests */ + if (!(old_flags & CAMEL_MESSAGE_SEEN)) + em_utils_handle_receipt(emfv->folder, uid, (CamelMimeMessage *)((EMFormat *)emfv->preview)->message); + + camel_folder_set_message_flags(emfv->folder, uid, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN); +} + /* keep these two tables in sync */ enum { EMFV_ANIMATE_IMAGES = 1, diff --git a/mail/mail-config.glade b/mail/mail-config.glade index bf61dec601..e559d2369d 100644 --- a/mail/mail-config.glade +++ b/mail/mail-config.glade @@ -3056,6 +3056,120 @@ For example: "Work" or "Personal" False + + + + True + False + 6 + + + + True + <span weight="bold">Message Receipts</span> + False + True + GTK_JUSTIFY_LEFT + False + False + 0 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + False + 12 + + + + True + + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + False + 12 + + + + True + Send message receipts: + False + False + GTK_JUSTIFY_LEFT + False + False + 0.5 + 0.5 + 0 + 0 + + + 0 + False + False + + + + + + True + + + + 0 + True + True + + + + + 0 + True + True + + + + + 0 + False + False + + + + + 0 + True + True + + False diff --git a/mail/mail-errors.xml b/mail/mail-errors.xml index c19cdf5fce..7a60b3917b 100644 --- a/mail/mail-errors.xml +++ b/mail/mail-errors.xml @@ -347,5 +347,13 @@ Please check your account settings and try again. + + Read receipt requested. + Read receipt requested. + A read receipt notification has been requested for "{1}". Send the reciept notification to {0}? +