Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

- Support SMTP Delivery Status Notifications - RFC3461 (#1486142)

  • Loading branch information...
commit f22ea7ba1875863890b486db3e5f448f99c1debc 1 parent 9db4ca9
Aleksander Machniak alecpl authored
1  CHANGELOG
@@ -21,6 +21,7 @@ CHANGELOG Roundcube Webmail
21 21 - Add unique index on users.username+users.mail_host
22 22 - Make htmleditor option more consistent and add option to use HTML on reply to HTML message (#1485840)
23 23 - Use empty envelope sender address for message disposition notifications (RFC2298.3)
  24 +- Support SMTP Delivery Status Notifications - RFC3461 (#1486142)
24 25
25 26 RELEASE 0.4.2
26 27 -------------
19 config/main.inc.php.dist
@@ -334,12 +334,6 @@ $rcmail_config['protect_default_folders'] = true;
334 334 // if in your system 0 quota means no limit set this option to true
335 335 $rcmail_config['quota_zero_as_unlimited'] = false;
336 336
337   -// Behavior if a received message requests a message delivery notification (read receipt)
338   -// 0 = ask the user, 1 = send automatically, 2 = ignore (never send or ask)
339   -// 3 = send automatically if sender is in addressbook, otherwise ask the user
340   -// 4 = send automatically if sender is in addressbook, otherwise ignore
341   -$rcmail_config['mdn_requests'] = 0;
342   -
343 337 // Make use of the built-in spell checker. It is based on GoogieSpell.
344 338 // Since Google only accepts connections over https your PHP installatation
345 339 // requires to be compiled with Open SSL support
@@ -571,5 +565,16 @@ $rcmail_config['search_mods'] = null; // Example: array('*' => array('subject'=
571 565 // when user is over quota and Trash is included in the quota.
572 566 $rcmail_config['delete_always'] = false;
573 567
574   -// end of config file
  568 +// Behavior if a received message requests a message delivery notification (read receipt)
  569 +// 0 = ask the user, 1 = send automatically, 2 = ignore (never send or ask)
  570 +// 3 = send automatically if sender is in addressbook, otherwise ask the user
  571 +// 4 = send automatically if sender is in addressbook, otherwise ignore
  572 +$rcmail_config['mdn_requests'] = 0;
575 573
  574 +// Return receipt checkbox default state
  575 +$rcmail_config['mdn_default'] = 0;
  576 +
  577 +// Delivery Status Notification checkbox default state
  578 +$rcmail_config['dsn_default'] = 0;
  579 +
  580 +// end of config file
4 program/include/rcube_message.php
@@ -399,9 +399,9 @@ private function parse_structure($structure, $recursive = false)
399 399 if ($part_orig_mimetype == 'message/rfc822' && !empty($mail_part->filename))
400 400 $this->attachments[] = $mail_part;
401 401 }
402   - // part text/[plain|html] OR message/delivery-status
  402 + // part text/[plain|html] or delivery status
403 403 else if ((($part_mimetype == 'text/plain' || $part_mimetype == 'text/html') && $mail_part->disposition != 'attachment') ||
404   - $part_mimetype == 'message/delivery-status' || $part_mimetype == 'message/disposition-notification'
  404 + in_array($part_mimetype, array('message/delivery-status', 'text/rfc822-headers', 'message/disposition-notification'))
405 405 ) {
406 406 // Allow plugins to handle also this part
407 407 $plugin = $this->app->plugins->exec_hook('message_part_structure',
33 program/include/rcube_smtp.php
@@ -153,17 +153,16 @@ public function connect($host=null, $port=null, $user=null, $pass=null)
153 153 * each RFC822 valid. This may contain recipients not
154 154 * specified in the headers, for Bcc:, resending
155 155 * messages, etc.
156   - *
157 156 * @param mixed The message headers to send with the mail
158 157 * Either as an associative array or a finally
159 158 * formatted string
160   - *
161 159 * @param mixed The full text of the message body, including any Mime parts
162 160 * or file handle
  161 + * @param array Delivery options (e.g. DSN request)
163 162 *
164 163 * @return bool Returns true on success, or false on error
165 164 */
166   - public function send_mail($from, $recipients, &$headers, &$body)
  165 + public function send_mail($from, $recipients, &$headers, &$body, $opts=null)
167 166 {
168 167 if (!is_object($this->conn))
169 168 return false;
@@ -183,7 +182,7 @@ public function send_mail($from, $recipients, &$headers, &$body)
183 182 else
184 183 {
185 184 $this->reset();
186   - $this->response[] .= "Invalid message headers";
  185 + $this->response[] = "Invalid message headers";
187 186 return false;
188 187 }
189 188
@@ -191,10 +190,24 @@ public function send_mail($from, $recipients, &$headers, &$body)
191 190 if (!isset($from))
192 191 {
193 192 $this->reset();
194   - $this->response[] .= "No From address has been provided";
  193 + $this->response[] = "No From address has been provided";
195 194 return false;
196 195 }
197 196
  197 + // RFC3461: Delivery Status Notification
  198 + if ($opts['dsn']) {
  199 + $exts = $this->conn->getServiceExtensions();
  200 +
  201 + if (!isset($exts['DSN'])) {
  202 + $this->error = array('label' => 'smtpdsnerror');
  203 + $this->response[] = "DSN not supported";
  204 + return false;
  205 + }
  206 +
  207 + $from_params = 'RET=HDRS';
  208 + $recipient_params = 'NOTIFY=SUCCESS,FAILURE';
  209 + }
  210 +
198 211 // RFC2298.3: remove envelope sender address
199 212 if (preg_match('/Content-Type: multipart\/report/', $text_headers)
200 213 && preg_match('/report-type=disposition-notification/', $text_headers)
@@ -203,12 +216,12 @@ public function send_mail($from, $recipients, &$headers, &$body)
203 216 }
204 217
205 218 // set From: address
206   - if (PEAR::isError($this->conn->mailFrom($from)))
  219 + if (PEAR::isError($this->conn->mailFrom($from, $from_params)))
207 220 {
208 221 $err = $this->conn->getResponse();
209 222 $this->error = array('label' => 'smtpfromerror', 'vars' => array(
210 223 'from' => $from, 'code' => $this->conn->_code, 'msg' => $err[1]));
211   - $this->response[] .= "Failed to set sender '$from'";
  224 + $this->response[] = "Failed to set sender '$from'";
212 225 $this->reset();
213 226 return false;
214 227 }
@@ -225,11 +238,11 @@ public function send_mail($from, $recipients, &$headers, &$body)
225 238 // set mail recipients
226 239 foreach ($recipients as $recipient)
227 240 {
228   - if (PEAR::isError($this->conn->rcptTo($recipient))) {
  241 + if (PEAR::isError($this->conn->rcptTo($recipient, $recipient_params))) {
229 242 $err = $this->conn->getResponse();
230 243 $this->error = array('label' => 'smtptoerror', 'vars' => array(
231 244 'to' => $recipient, 'code' => $this->conn->_code, 'msg' => $err[1]));
232   - $this->response[] .= "Failed to add recipient '$recipient'";
  245 + $this->response[] = "Failed to add recipient '$recipient'";
233 246 $this->reset();
234 247 return false;
235 248 }
@@ -261,7 +274,7 @@ public function send_mail($from, $recipients, &$headers, &$body)
261 274 $msg = $result->getMessage();
262 275
263 276 $this->error = array('label' => 'smtperror', 'vars' => array('msg' => $msg));
264   - $this->response[] .= "Failed to send data";
  277 + $this->response[] = "Failed to send data";
265 278 $this->reset();
266 279 return false;
267 280 }
2  program/localization/en_US/labels.inc
@@ -209,6 +209,7 @@ $labels['addattachment'] = 'Attach a file';
209 209 $labels['charset'] = 'Charset';
210 210 $labels['editortype'] = 'Editor type';
211 211 $labels['returnreceipt'] = 'Return receipt';
  212 +$labels['dsn'] = 'Delivery status notification';
212 213
213 214 $labels['editidents'] = 'Edit identities';
214 215 $labels['checkspelling'] = 'Check spelling';
@@ -374,6 +375,7 @@ $labels['insertsignature'] = 'Insert signature';
374 375 $labels['previewpanemarkread'] = 'Mark previewed messages as read';
375 376 $labels['afternseconds'] = 'after $n seconds';
376 377 $labels['reqmdn'] = 'Always request a return receipt';
  378 +$labels['reqdsn'] = 'Always request a delivery status notification';
377 379
378 380 $labels['folder'] = 'Folder';
379 381 $labels['folders'] = 'Folders';
1  program/localization/en_US/messages.inc
@@ -109,6 +109,7 @@ $messages['smtpautherror'] = 'SMTP Error ($code): Authentication failed';
109 109 $messages['smtpfromerror'] = 'SMTP Error ($code): Failed to set sender "$from" ($msg)';
110 110 $messages['smtptoerror'] = 'SMTP Error ($code): Failed to add recipient "$to" ($msg)';
111 111 $messages['smtprecipientserror'] = 'SMTP Error: Unable to parse recipients list';
  112 +$messages['smtpdsnerror'] = 'SMTP Error: No support for Delivery Status Notifications';
112 113 $messages['smtperror'] = 'SMTP Error: $msg';
113 114 $messages['emailformaterror'] = 'Invalid e-mail address: $email';
114 115 $messages['toomanyrecipients'] = 'Too many recipients. Reduce the number of recipients to $max.';
2  program/localization/pl_PL/labels.inc
@@ -360,5 +360,7 @@ $labels['replylist'] = 'Odpowiedz na listę';
360 360 $labels['editidents'] = 'Edytuj tożsamości';
361 361 $labels['addmailreplyto'] = 'Dodaj Mail-Reply-To';
362 362 $labels['addmailfollowupto'] = 'Dodaj Mail-Followup-To';
  363 +$labels['dsn'] = 'Status dostarczenia (DSN)';
  364 +$labels['reqdsn'] = 'Zawsze żądaj statusu dostarczenia (DSN)';
363 365
364 366 ?>
1  program/localization/pl_PL/messages.inc
@@ -114,6 +114,7 @@ $messages['smtpautherror'] = 'Błąd SMTP ($code): Uwierzytelnianie nie powiodł
114 114 $messages['smtpfromerror'] = 'Błąd SMTP ($code): Nie można ustawić nadawcy "$from" ($msg)';
115 115 $messages['smtptoerror'] = 'Błąd SMTP ($code): Nie można dodać odbiorcy "$to" ($msg)';
116 116 $messages['smtprecipientserror'] = 'Błąd SMTP: Parsowanie listy odbiorców nie powiodło się';
  117 +$messages['smtpdsnerror'] = 'Błąd SMTP: Statusy dostarczenia (DSN) nie są obsługiwane przez serwer';
117 118 $messages['smtperror'] = 'Błąd SMTP: $msg';
118 119 $messages['emailformaterror'] = 'Błędny adres e-mail: $email';
119 120 $messages['toomanyrecipients'] = 'Zbyt wielu odbiorców. Zmniejsz ich liczbę do $max.';
23 program/steps/mail/compose.inc
@@ -1174,6 +1174,28 @@ function rcmail_receipt_checkbox($attrib)
1174 1174 }
1175 1175
1176 1176
  1177 +function rcmail_dsn_checkbox($attrib)
  1178 +{
  1179 + global $RCMAIL;
  1180 +
  1181 + list($form_start, $form_end) = get_form_tags($attrib);
  1182 + unset($attrib['form']);
  1183 +
  1184 + if (!isset($attrib['id']))
  1185 + $attrib['id'] = 'dsn';
  1186 +
  1187 + $attrib['name'] = '_dsn';
  1188 + $attrib['value'] = '1';
  1189 + $checkbox = new html_checkbox($attrib);
  1190 +
  1191 + $out = $form_start ? "$form_start\n" : '';
  1192 + $out .= $checkbox->show($RCMAIL->config->get('dsn_default'));
  1193 + $out .= $form_end ? "\n$form_end" : '';
  1194 +
  1195 + return $out;
  1196 +}
  1197 +
  1198 +
1177 1199 function rcmail_editor_selector($attrib)
1178 1200 {
1179 1201 global $CONFIG, $MESSAGE, $compose_mode;
@@ -1251,6 +1273,7 @@ $OUTPUT->add_handlers(array(
1251 1273 'priorityselector' => 'rcmail_priority_selector',
1252 1274 'editorselector' => 'rcmail_editor_selector',
1253 1275 'receiptcheckbox' => 'rcmail_receipt_checkbox',
  1276 + 'dsncheckbox' => 'rcmail_dsn_checkbox',
1254 1277 'storetarget' => 'rcmail_store_target_selection',
1255 1278 ));
1256 1279
5 program/steps/mail/func.inc
@@ -1478,10 +1478,11 @@ function rcmail_compose_cleanup()
1478 1478 * @param array $mailto Array of recipient address strings
1479 1479 * @param array $smtp_error SMTP error array (reference)
1480 1480 * @param string $body_file Location of file with saved message body (reference)
  1481 + * @param array $smtp_opts SMTP options (e.g. DSN request)
1481 1482 *
1482 1483 * @return boolean Send status.
1483 1484 */
1484   -function rcmail_deliver_message(&$message, $from, $mailto, &$smtp_error, &$body_file)
  1485 +function rcmail_deliver_message(&$message, $from, $mailto, &$smtp_error, &$body_file, $smtp_opts=null)
1485 1486 {
1486 1487 global $CONFIG, $RCMAIL;
1487 1488
@@ -1525,7 +1526,7 @@ function rcmail_deliver_message(&$message, $from, $mailto, &$smtp_error, &$body_
1525 1526 if (!is_object($RCMAIL->smtp))
1526 1527 $RCMAIL->smtp_init(true);
1527 1528
1528   - $sent = $RCMAIL->smtp->send_mail($from, $a_recipients, $smtp_headers, $msg_body);
  1529 + $sent = $RCMAIL->smtp->send_mail($from, $a_recipients, $smtp_headers, $msg_body, $smtp_opts);
1529 1530 $smtp_response = $RCMAIL->smtp->get_response();
1530 1531 $smtp_error = $RCMAIL->smtp->get_error();
1531 1532
8 program/steps/mail/sendmail.inc
@@ -546,7 +546,13 @@ if (!$savedraft)
546 546 $OUTPUT->send('iframe');
547 547 }
548 548
549   - $sent = rcmail_deliver_message($MAIL_MIME, $from, $mailto, $smtp_error, $mailbody_file);
  549 + // Handle Delivery Status Notification request
  550 + if (!empty($_POST['_dsn'])) {
  551 + $smtp_opts['dsn'] = true;
  552 + }
  553 +
  554 + $sent = rcmail_deliver_message($MAIL_MIME, $from, $mailto,
  555 + $smtp_error, $mailbody_file, $smtp_opts);
550 556
551 557 // return to compose page if sending failed
552 558 if (!$sent)
10 program/steps/settings/func.inc
@@ -524,6 +524,16 @@ function rcmail_user_prefs($current=null)
524 524 );
525 525 }
526 526
  527 + if (!isset($no_override['dsn_default'])) {
  528 + $field_id = 'rcmfd_dsn_default';
  529 + $input_dsn = new html_checkbox(array('name' => '_dsn_default', 'id' => $field_id, 'value' => 1));
  530 +
  531 + $blocks['main']['options']['dsn_default'] = array(
  532 + 'title' => html::label($field_id, Q(rcube_label('reqdsn'))),
  533 + 'content' => $input_dsn->show($config['dsn_default']?1:0),
  534 + );
  535 + }
  536 +
527 537 if (!isset($no_override['top_posting'])) {
528 538 $field_id = 'rcmfd_top_posting';
529 539 $select_replymode = new html_select(array('name' => '_top_posting', 'id' => $field_id, 'onchange' => "\$('#rcmfd_sig_above').attr('disabled',this.selectedIndex==0)"));
1  program/steps/settings/save_prefs.inc
@@ -66,6 +66,7 @@ switch ($CURR_SECTION)
66 66 'mime_param_folding' => isset($_POST['_mime_param_folding']) ? intval($_POST['_mime_param_folding']) : 0,
67 67 'force_7bit' => isset($_POST['_force_7bit']) ? TRUE : FALSE,
68 68 'mdn_default' => isset($_POST['_mdn_default']) ? TRUE : FALSE,
  69 + 'dsn_default' => isset($_POST['_dsn_default']) ? TRUE : FALSE,
69 70 'show_sig' => isset($_POST['_show_sig']) ? intval($_POST['_show_sig']) : 1,
70 71 'top_posting' => !empty($_POST['_top_posting']),
71 72 'strip_existing_sig' => isset($_POST['_strip_existing_sig']),
3  skins/default/templates/compose.html
@@ -137,6 +137,9 @@
137 137 <td><label for="rcmcomposereceipt"><roundcube:label name="returnreceipt" />:</label></td>
138 138 <td><roundcube:object name="receiptCheckBox" form="form" id="rcmcomposereceipt" /></td>
139 139 </tr><tr>
  140 + <td><label for="rcmcomposedsn"><roundcube:label name="dsn" />:</label></td>
  141 + <td><roundcube:object name="dsnCheckBox" form="form" id="rcmcomposedsn" /></td>
  142 + </tr><tr>
140 143 <td><label for="rcmcomposepriority"><roundcube:label name="priority" />:</label></td>
141 144 <td><roundcube:object name="prioritySelector" form="form" id="rcmcomposepriority" /></td>
142 145 </tr><tr>

0 comments on commit f22ea7b

Please sign in to comment.
Something went wrong with that request. Please try again.