From f22ea7ba1875863890b486db3e5f448f99c1debc Mon Sep 17 00:00:00 2001 From: alecpl Date: Thu, 7 Oct 2010 08:52:05 +0000 Subject: - Support SMTP Delivery Status Notifications - RFC3461 (#1486142) --- CHANGELOG | 1 + config/main.inc.php.dist | 19 ++++++++++++------- program/include/rcube_message.php | 4 ++-- program/include/rcube_smtp.php | 33 +++++++++++++++++++++++---------- program/localization/en_US/labels.inc | 2 ++ program/localization/en_US/messages.inc | 1 + program/localization/pl_PL/labels.inc | 2 ++ program/localization/pl_PL/messages.inc | 1 + program/steps/mail/compose.inc | 23 +++++++++++++++++++++++ program/steps/mail/func.inc | 5 +++-- program/steps/mail/sendmail.inc | 8 +++++++- program/steps/settings/func.inc | 10 ++++++++++ program/steps/settings/save_prefs.inc | 1 + skins/default/templates/compose.html | 3 +++ 14 files changed, 91 insertions(+), 22 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index e408d4892..0de16f271 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -21,6 +21,7 @@ CHANGELOG Roundcube Webmail - Add unique index on users.username+users.mail_host - Make htmleditor option more consistent and add option to use HTML on reply to HTML message (#1485840) - Use empty envelope sender address for message disposition notifications (RFC2298.3) +- Support SMTP Delivery Status Notifications - RFC3461 (#1486142) RELEASE 0.4.2 ------------- diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index 8ec7f08e4..bbf614996 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -334,12 +334,6 @@ $rcmail_config['protect_default_folders'] = true; // if in your system 0 quota means no limit set this option to true $rcmail_config['quota_zero_as_unlimited'] = false; -// Behavior if a received message requests a message delivery notification (read receipt) -// 0 = ask the user, 1 = send automatically, 2 = ignore (never send or ask) -// 3 = send automatically if sender is in addressbook, otherwise ask the user -// 4 = send automatically if sender is in addressbook, otherwise ignore -$rcmail_config['mdn_requests'] = 0; - // Make use of the built-in spell checker. It is based on GoogieSpell. // Since Google only accepts connections over https your PHP installatation // requires to be compiled with Open SSL support @@ -571,5 +565,16 @@ $rcmail_config['search_mods'] = null; // Example: array('*' => array('subject'= // when user is over quota and Trash is included in the quota. $rcmail_config['delete_always'] = false; -// end of config file +// Behavior if a received message requests a message delivery notification (read receipt) +// 0 = ask the user, 1 = send automatically, 2 = ignore (never send or ask) +// 3 = send automatically if sender is in addressbook, otherwise ask the user +// 4 = send automatically if sender is in addressbook, otherwise ignore +$rcmail_config['mdn_requests'] = 0; +// Return receipt checkbox default state +$rcmail_config['mdn_default'] = 0; + +// Delivery Status Notification checkbox default state +$rcmail_config['dsn_default'] = 0; + +// end of config file diff --git a/program/include/rcube_message.php b/program/include/rcube_message.php index dfccb36e9..fd42e4ad4 100644 --- a/program/include/rcube_message.php +++ b/program/include/rcube_message.php @@ -399,9 +399,9 @@ class rcube_message if ($part_orig_mimetype == 'message/rfc822' && !empty($mail_part->filename)) $this->attachments[] = $mail_part; } - // part text/[plain|html] OR message/delivery-status + // part text/[plain|html] or delivery status else if ((($part_mimetype == 'text/plain' || $part_mimetype == 'text/html') && $mail_part->disposition != 'attachment') || - $part_mimetype == 'message/delivery-status' || $part_mimetype == 'message/disposition-notification' + in_array($part_mimetype, array('message/delivery-status', 'text/rfc822-headers', 'message/disposition-notification')) ) { // Allow plugins to handle also this part $plugin = $this->app->plugins->exec_hook('message_part_structure', diff --git a/program/include/rcube_smtp.php b/program/include/rcube_smtp.php index 61eebf3fa..5acc156c6 100644 --- a/program/include/rcube_smtp.php +++ b/program/include/rcube_smtp.php @@ -153,17 +153,16 @@ class rcube_smtp * each RFC822 valid. This may contain recipients not * specified in the headers, for Bcc:, resending * messages, etc. - * * @param mixed The message headers to send with the mail * Either as an associative array or a finally * formatted string - * * @param mixed The full text of the message body, including any Mime parts * or file handle + * @param array Delivery options (e.g. DSN request) * * @return bool Returns true on success, or false on error */ - public function send_mail($from, $recipients, &$headers, &$body) + public function send_mail($from, $recipients, &$headers, &$body, $opts=null) { if (!is_object($this->conn)) return false; @@ -183,7 +182,7 @@ class rcube_smtp else { $this->reset(); - $this->response[] .= "Invalid message headers"; + $this->response[] = "Invalid message headers"; return false; } @@ -191,10 +190,24 @@ class rcube_smtp if (!isset($from)) { $this->reset(); - $this->response[] .= "No From address has been provided"; + $this->response[] = "No From address has been provided"; return false; } + // RFC3461: Delivery Status Notification + if ($opts['dsn']) { + $exts = $this->conn->getServiceExtensions(); + + if (!isset($exts['DSN'])) { + $this->error = array('label' => 'smtpdsnerror'); + $this->response[] = "DSN not supported"; + return false; + } + + $from_params = 'RET=HDRS'; + $recipient_params = 'NOTIFY=SUCCESS,FAILURE'; + } + // RFC2298.3: remove envelope sender address if (preg_match('/Content-Type: multipart\/report/', $text_headers) && preg_match('/report-type=disposition-notification/', $text_headers) @@ -203,12 +216,12 @@ class rcube_smtp } // set From: address - if (PEAR::isError($this->conn->mailFrom($from))) + if (PEAR::isError($this->conn->mailFrom($from, $from_params))) { $err = $this->conn->getResponse(); $this->error = array('label' => 'smtpfromerror', 'vars' => array( 'from' => $from, 'code' => $this->conn->_code, 'msg' => $err[1])); - $this->response[] .= "Failed to set sender '$from'"; + $this->response[] = "Failed to set sender '$from'"; $this->reset(); return false; } @@ -225,11 +238,11 @@ class rcube_smtp // set mail recipients foreach ($recipients as $recipient) { - if (PEAR::isError($this->conn->rcptTo($recipient))) { + if (PEAR::isError($this->conn->rcptTo($recipient, $recipient_params))) { $err = $this->conn->getResponse(); $this->error = array('label' => 'smtptoerror', 'vars' => array( 'to' => $recipient, 'code' => $this->conn->_code, 'msg' => $err[1])); - $this->response[] .= "Failed to add recipient '$recipient'"; + $this->response[] = "Failed to add recipient '$recipient'"; $this->reset(); return false; } @@ -261,7 +274,7 @@ class rcube_smtp $msg = $result->getMessage(); $this->error = array('label' => 'smtperror', 'vars' => array('msg' => $msg)); - $this->response[] .= "Failed to send data"; + $this->response[] = "Failed to send data"; $this->reset(); return false; } diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc index a34be5f23..aace461f5 100644 --- a/program/localization/en_US/labels.inc +++ b/program/localization/en_US/labels.inc @@ -209,6 +209,7 @@ $labels['addattachment'] = 'Attach a file'; $labels['charset'] = 'Charset'; $labels['editortype'] = 'Editor type'; $labels['returnreceipt'] = 'Return receipt'; +$labels['dsn'] = 'Delivery status notification'; $labels['editidents'] = 'Edit identities'; $labels['checkspelling'] = 'Check spelling'; @@ -374,6 +375,7 @@ $labels['insertsignature'] = 'Insert signature'; $labels['previewpanemarkread'] = 'Mark previewed messages as read'; $labels['afternseconds'] = 'after $n seconds'; $labels['reqmdn'] = 'Always request a return receipt'; +$labels['reqdsn'] = 'Always request a delivery status notification'; $labels['folder'] = 'Folder'; $labels['folders'] = 'Folders'; diff --git a/program/localization/en_US/messages.inc b/program/localization/en_US/messages.inc index 900a6db83..26fa36ad7 100644 --- a/program/localization/en_US/messages.inc +++ b/program/localization/en_US/messages.inc @@ -109,6 +109,7 @@ $messages['smtpautherror'] = 'SMTP Error ($code): Authentication failed'; $messages['smtpfromerror'] = 'SMTP Error ($code): Failed to set sender "$from" ($msg)'; $messages['smtptoerror'] = 'SMTP Error ($code): Failed to add recipient "$to" ($msg)'; $messages['smtprecipientserror'] = 'SMTP Error: Unable to parse recipients list'; +$messages['smtpdsnerror'] = 'SMTP Error: No support for Delivery Status Notifications'; $messages['smtperror'] = 'SMTP Error: $msg'; $messages['emailformaterror'] = 'Invalid e-mail address: $email'; $messages['toomanyrecipients'] = 'Too many recipients. Reduce the number of recipients to $max.'; diff --git a/program/localization/pl_PL/labels.inc b/program/localization/pl_PL/labels.inc index b083fb54b..804b3d130 100644 --- a/program/localization/pl_PL/labels.inc +++ b/program/localization/pl_PL/labels.inc @@ -360,5 +360,7 @@ $labels['replylist'] = 'Odpowiedz na listę'; $labels['editidents'] = 'Edytuj tożsamości'; $labels['addmailreplyto'] = 'Dodaj Mail-Reply-To'; $labels['addmailfollowupto'] = 'Dodaj Mail-Followup-To'; +$labels['dsn'] = 'Status dostarczenia (DSN)'; +$labels['reqdsn'] = 'Zawsze żądaj statusu dostarczenia (DSN)'; ?> diff --git a/program/localization/pl_PL/messages.inc b/program/localization/pl_PL/messages.inc index 781ad0bbf..91b3c901c 100644 --- a/program/localization/pl_PL/messages.inc +++ b/program/localization/pl_PL/messages.inc @@ -114,6 +114,7 @@ $messages['smtpautherror'] = 'Błąd SMTP ($code): Uwierzytelnianie nie powiodł $messages['smtpfromerror'] = 'Błąd SMTP ($code): Nie można ustawić nadawcy "$from" ($msg)'; $messages['smtptoerror'] = 'Błąd SMTP ($code): Nie można dodać odbiorcy "$to" ($msg)'; $messages['smtprecipientserror'] = 'Błąd SMTP: Parsowanie listy odbiorców nie powiodło się'; +$messages['smtpdsnerror'] = 'Błąd SMTP: Statusy dostarczenia (DSN) nie są obsługiwane przez serwer'; $messages['smtperror'] = 'Błąd SMTP: $msg'; $messages['emailformaterror'] = 'Błędny adres e-mail: $email'; $messages['toomanyrecipients'] = 'Zbyt wielu odbiorców. Zmniejsz ich liczbę do $max.'; diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index c1f491403..406033cb9 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -1174,6 +1174,28 @@ function rcmail_receipt_checkbox($attrib) } +function rcmail_dsn_checkbox($attrib) +{ + global $RCMAIL; + + list($form_start, $form_end) = get_form_tags($attrib); + unset($attrib['form']); + + if (!isset($attrib['id'])) + $attrib['id'] = 'dsn'; + + $attrib['name'] = '_dsn'; + $attrib['value'] = '1'; + $checkbox = new html_checkbox($attrib); + + $out = $form_start ? "$form_start\n" : ''; + $out .= $checkbox->show($RCMAIL->config->get('dsn_default')); + $out .= $form_end ? "\n$form_end" : ''; + + return $out; +} + + function rcmail_editor_selector($attrib) { global $CONFIG, $MESSAGE, $compose_mode; @@ -1251,6 +1273,7 @@ $OUTPUT->add_handlers(array( 'priorityselector' => 'rcmail_priority_selector', 'editorselector' => 'rcmail_editor_selector', 'receiptcheckbox' => 'rcmail_receipt_checkbox', + 'dsncheckbox' => 'rcmail_dsn_checkbox', 'storetarget' => 'rcmail_store_target_selection', )); diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index aad127cfc..0fa22753d 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -1478,10 +1478,11 @@ function rcmail_compose_cleanup() * @param array $mailto Array of recipient address strings * @param array $smtp_error SMTP error array (reference) * @param string $body_file Location of file with saved message body (reference) + * @param array $smtp_opts SMTP options (e.g. DSN request) * * @return boolean Send status. */ -function rcmail_deliver_message(&$message, $from, $mailto, &$smtp_error, &$body_file) +function rcmail_deliver_message(&$message, $from, $mailto, &$smtp_error, &$body_file, $smtp_opts=null) { global $CONFIG, $RCMAIL; @@ -1525,7 +1526,7 @@ function rcmail_deliver_message(&$message, $from, $mailto, &$smtp_error, &$body_ if (!is_object($RCMAIL->smtp)) $RCMAIL->smtp_init(true); - $sent = $RCMAIL->smtp->send_mail($from, $a_recipients, $smtp_headers, $msg_body); + $sent = $RCMAIL->smtp->send_mail($from, $a_recipients, $smtp_headers, $msg_body, $smtp_opts); $smtp_response = $RCMAIL->smtp->get_response(); $smtp_error = $RCMAIL->smtp->get_error(); diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc index a9ecf2ea2..da79c2f83 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -546,7 +546,13 @@ if (!$savedraft) $OUTPUT->send('iframe'); } - $sent = rcmail_deliver_message($MAIL_MIME, $from, $mailto, $smtp_error, $mailbody_file); + // Handle Delivery Status Notification request + if (!empty($_POST['_dsn'])) { + $smtp_opts['dsn'] = true; + } + + $sent = rcmail_deliver_message($MAIL_MIME, $from, $mailto, + $smtp_error, $mailbody_file, $smtp_opts); // return to compose page if sending failed if (!$sent) diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc index a7d4c11f0..e0aa5629b 100644 --- a/program/steps/settings/func.inc +++ b/program/steps/settings/func.inc @@ -524,6 +524,16 @@ function rcmail_user_prefs($current=null) ); } + if (!isset($no_override['dsn_default'])) { + $field_id = 'rcmfd_dsn_default'; + $input_dsn = new html_checkbox(array('name' => '_dsn_default', 'id' => $field_id, 'value' => 1)); + + $blocks['main']['options']['dsn_default'] = array( + 'title' => html::label($field_id, Q(rcube_label('reqdsn'))), + 'content' => $input_dsn->show($config['dsn_default']?1:0), + ); + } + if (!isset($no_override['top_posting'])) { $field_id = 'rcmfd_top_posting'; $select_replymode = new html_select(array('name' => '_top_posting', 'id' => $field_id, 'onchange' => "\$('#rcmfd_sig_above').attr('disabled',this.selectedIndex==0)")); diff --git a/program/steps/settings/save_prefs.inc b/program/steps/settings/save_prefs.inc index ae3d6d704..63654eefc 100644 --- a/program/steps/settings/save_prefs.inc +++ b/program/steps/settings/save_prefs.inc @@ -66,6 +66,7 @@ switch ($CURR_SECTION) 'mime_param_folding' => isset($_POST['_mime_param_folding']) ? intval($_POST['_mime_param_folding']) : 0, 'force_7bit' => isset($_POST['_force_7bit']) ? TRUE : FALSE, 'mdn_default' => isset($_POST['_mdn_default']) ? TRUE : FALSE, + 'dsn_default' => isset($_POST['_dsn_default']) ? TRUE : FALSE, 'show_sig' => isset($_POST['_show_sig']) ? intval($_POST['_show_sig']) : 1, 'top_posting' => !empty($_POST['_top_posting']), 'strip_existing_sig' => isset($_POST['_strip_existing_sig']), diff --git a/skins/default/templates/compose.html b/skins/default/templates/compose.html index f99afdd9e..99015abee 100644 --- a/skins/default/templates/compose.html +++ b/skins/default/templates/compose.html @@ -136,6 +136,9 @@ + + + -- cgit v1.2.3