diff options
-rw-r--r-- | mail/ChangeLog | 25 | ||||
-rw-r--r-- | mail/em-account-editor.c | 61 | ||||
-rw-r--r-- | mail/em-composer-utils.c | 189 | ||||
-rw-r--r-- | mail/em-composer-utils.h | 4 | ||||
-rw-r--r-- | mail/em-folder-view.c | 21 | ||||
-rw-r--r-- | mail/mail-config.glade | 114 | ||||
-rw-r--r-- | mail/mail-errors.xml | 8 | ||||
-rw-r--r-- | 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 <NotZed@Ximian.com> + + * 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 <cactus@cactus.rulez.org> + + * 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 <NotZed@Ximian.com> ** 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 @@ -724,6 +724,64 @@ emae_setup_signatures(EMAccountEditor *emae, GladeXML *xml) } 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) { int item = GPOINTER_TO_INT(g_object_get_data((GObject *)entry, "account-item")); @@ -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 <camel/camel-string-utils.h> +#include <camel/camel-stream-mem.h> #include <camel/camel-nntp-address.h> -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 <bonobo/bonobo-ui-util.h> #include "widgets/misc/e-charset-picker.h" +#include "widgets/misc/e-error.h" #include <e-util/e-dialog-utils.h> #include <e-util/e-icon-factory.h> @@ -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"</property> <property name="fill">False</property> </packing> </child> + + <child> + <widget class="GtkVBox" id="vbox205"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkLabel" id="label578"> + <property name="visible">True</property> + <property name="label" translatable="yes"><span weight="bold">Message Receipts</span></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox231"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="label581"> + <property name="visible">True</property> + <property name="label" translatable="yes"></property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox232"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">12</property> + + <child> + <widget class="GtkLabel" id="label583"> + <property name="visible">True</property> + <property name="label" translatable="yes">Send message receipts:</property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="receipt_policy_dropdown"> + <property name="visible">True</property> + <property name="items" translatable="yes"></property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> </widget> <packing> <property name="tab_expand">False</property> 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. </secondary> </error> + <error id="ask-receipt" type="question" default="GTK_RESPONSE_NO"> + <title>Read receipt requested.</title> + <primary>Read receipt requested.</primary> + <secondary>A read receipt notification has been requested for "{1}". Send the reciept notification to {0}?</secondary> + <button stock="gtk-no" response="GTK_RESPONSE_NO"/> + <button stock="gtk-yes" label="Send Receipt" response="GTK_RESPONSE_YES"/> + </error> + </error-list> diff --git a/mail/mail-errors.xml.h b/mail/mail-errors.xml.h index 7161251dea..7537716cea 100644 --- a/mail/mail-errors.xml.h +++ b/mail/mail-errors.xml.h @@ -258,3 +258,10 @@ char *s = N_("Unable to connect to the GroupWise\n" char *s = N_("\n" "Please check your account settings and try again.\n" ""); +/* mail:ask-receipt title */ +char *s = N_("Read receipt requested."); +/* mail:ask-receipt primary */ +char *s = N_("Read receipt requested."); +/* mail:ask-receipt secondary */ +char *s = N_("A read receipt notification has been requested for \"{1}\". Send the reciept notification to {0}?"); +char *s = N_("Send Receipt"); |