From 8297ea1dcfbad6390cdaac6c941b72be59abf300 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak <alec@alec.pl> Date: Fri, 28 Aug 2015 03:02:39 -0400 Subject: [PATCH] Fix handling of plus character in mailto: links (#1490510) --- program/steps/mail/compose.inc | 430 ++++++++++++++++++++++++++++++----------------------- 1 files changed, 242 insertions(+), 188 deletions(-) diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 007efc5..402b32c 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -1,6 +1,6 @@ <?php -/* +/** +-----------------------------------------------------------------------+ | program/steps/mail/compose.inc | | | @@ -47,9 +47,11 @@ if (!is_array($COMPOSE)) { // Infinite redirect prevention in case of broken session (#1487028) if ($COMPOSE_ID) { - rcube::raise_error(array('code' => 500, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, - 'message' => "Invalid compose ID"), true, true); + // if we know the message with specified ID was already sent + // we can ignore the error and compose a new message (#1490009) + if ($COMPOSE_ID != $_SESSION['last_compose_session']) { + rcube::raise_error(array('code' => 450), false, true); + } } $COMPOSE_ID = uniqid(mt_rand()); @@ -91,6 +93,7 @@ $OUTPUT->set_env('session_id', session_id()); $OUTPUT->set_env('mailbox', $RCMAIL->storage->get_folder()); $OUTPUT->set_env('top_posting', intval($RCMAIL->config->get('reply_mode')) > 0); +$OUTPUT->set_env('sig_below', $RCMAIL->config->get('sig_below')); $OUTPUT->set_env('recipients_separator', trim($RCMAIL->config->get('recipients_separator', ','))); $OUTPUT->set_env('save_localstorage', (bool)$RCMAIL->config->get('compose_save_localstorage')); $OUTPUT->set_env('is_sent', false); @@ -272,144 +275,9 @@ else if (count($MESSAGE->identities)) { $ident = rcmail_identity_select($MESSAGE, $MESSAGE->identities, $compose_mode); - $MESSAGE->compose['from_email'] = $ident['email']; - $MESSAGE->compose['from'] = $ident['identity_id']; + $MESSAGE->compose['from'] = $ident['identity_id']; + $MESSAGE->compose['ident'] = $ident; } - -// Set other headers -$a_recipients = array(); -$parts = array('to', 'cc', 'bcc', 'replyto', 'followupto'); -$separator = trim($RCMAIL->config->get('recipients_separator', ',')) . ' '; -$from_email = @mb_strtolower($MESSAGE->compose['from_email']); - -foreach ($parts as $header) { - $fvalue = ''; - $decode_header = true; - $charset = $MESSAGE->headers->charset; - - // we have a set of recipients stored is session - if ($header == 'to' && ($mailto_id = $COMPOSE['param']['mailto']) - && $_SESSION['mailto'][$mailto_id] - ) { - $fvalue = urldecode($_SESSION['mailto'][$mailto_id]); - $decode_header = false; - $charset = $RCMAIL->output->charset; - - // make session to not grow up too much - unset($_SESSION['mailto'][$mailto_id]); - $COMPOSE['param']['to'] = $fvalue; - } - else if (!empty($_POST['_'.$header])) { - $fvalue = rcube_utils::get_input_value('_'.$header, rcube_utils::INPUT_POST, TRUE); - $charset = $RCMAIL->output->charset; - } - else if (!empty($COMPOSE['param'][$header])) { - $fvalue = $COMPOSE['param'][$header]; - $charset = $RCMAIL->output->charset; - } - else if ($compose_mode == RCUBE_COMPOSE_REPLY) { - // get recipent address(es) out of the message headers - if ($header == 'to') { - $mailfollowup = $MESSAGE->headers->others['mail-followup-to']; - $mailreplyto = $MESSAGE->headers->others['mail-reply-to']; - - // Reply to mailing list... - if ($MESSAGE->reply_all == 'list' && $mailfollowup) - $fvalue = $mailfollowup; - else if ($MESSAGE->reply_all == 'list' - && preg_match('/<mailto:([^>]+)>/i', $MESSAGE->headers->others['list-post'], $m)) - $fvalue = $m[1]; - // Reply to... - else if ($MESSAGE->reply_all && $mailfollowup) - $fvalue = $mailfollowup; - else if ($mailreplyto) - $fvalue = $mailreplyto; - else if (!empty($MESSAGE->headers->replyto)) { - $fvalue = $MESSAGE->headers->replyto; - $replyto = true; - } - else if (!empty($MESSAGE->headers->from)) - $fvalue = $MESSAGE->headers->from; - - // Reply to message sent by yourself (#1487074, #1489230) - // Reply-To address need to be unset (#1490233) - if (!empty($ident) && empty($replyto) - && in_array($ident['ident'], array($fvalue, $MESSAGE->headers->from)) - ) { - $fvalue = $MESSAGE->headers->to; - } - } - // add recipient of original message if reply to all - else if ($header == 'cc' && !empty($MESSAGE->reply_all) && $MESSAGE->reply_all != 'list') { - if ($v = $MESSAGE->headers->to) - $fvalue .= $v; - if ($v = $MESSAGE->headers->cc) - $fvalue .= (!empty($fvalue) ? $separator : '') . $v; - // Use Sender header (#1489011) - if (($v = $MESSAGE->headers->get('Sender', false)) && strpos($v, '-bounces@') === false) - $fvalue .= (!empty($fvalue) ? $separator : '') . $v; - - // When To: and Reply-To: are the same we add From: address to the list (#1489037) - if ($v = $MESSAGE->headers->from) { - $from = rcube_mime::decode_address_list($v, null, false, $charset, true); - $to = rcube_mime::decode_address_list($MESSAGE->headers->to, null, false, $charset, true); - $replyto = rcube_mime::decode_address_list($MESSAGE->headers->replyto, null, false, $charset, true); - - if (count($replyto) && !count(array_diff($to, $replyto)) && count(array_diff($from, $to))) { - $fvalue .= (!empty($fvalue) ? $separator : '') . $v; - } - } - } - } - else if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) { - // get drafted headers - if ($header=='to' && !empty($MESSAGE->headers->to)) - $fvalue = $MESSAGE->get_header('to', true); - else if ($header=='cc' && !empty($MESSAGE->headers->cc)) - $fvalue = $MESSAGE->get_header('cc', true); - else if ($header=='bcc' && !empty($MESSAGE->headers->bcc)) - $fvalue = $MESSAGE->get_header('bcc', true); - else if ($header=='replyto' && !empty($MESSAGE->headers->others['mail-reply-to'])) - $fvalue = $MESSAGE->get_header('mail-reply-to'); - else if ($header=='replyto' && !empty($MESSAGE->headers->replyto)) - $fvalue = $MESSAGE->get_header('reply-to'); - else if ($header=='followupto' && !empty($MESSAGE->headers->others['mail-followup-to'])) - $fvalue = $MESSAGE->get_header('mail-followup-to'); - } - - // split recipients and put them back together in a unique way - if (!empty($fvalue) && in_array($header, array('to', 'cc', 'bcc'))) { - $to_addresses = rcube_mime::decode_address_list($fvalue, null, $decode_header, $charset); - $fvalue = array(); - - foreach ($to_addresses as $addr_part) { - if (empty($addr_part['mailto'])) { - continue; - } - - // According to RFC5321 local part of email address is case-sensitive - // however, here it is better to compare addresses in case-insensitive manner - $mailto = format_email(rcube_utils::idn_to_utf8($addr_part['mailto'])); - $mailto_lc = mb_strtolower($addr_part['mailto']); - - if (($header == 'to' || $compose_mode != RCUBE_COMPOSE_REPLY || $mailto_lc != $from_email) - && !in_array($mailto_lc, $a_recipients) - ) { - if ($addr_part['name'] && $mailto != $addr_part['name']) { - $mailto = format_email_recipient($mailto, $addr_part['name']); - } - - $fvalue[] = $mailto; - $a_recipients[] = $mailto_lc; - } - } - - $fvalue = implode($separator, $fvalue); - } - - $MESSAGE->compose[$header] = $fvalue; -} -unset($a_recipients); // process $MESSAGE body/attachments, set $MESSAGE_BODY/$HTML_MODE vars and some session data $MESSAGE_BODY = rcmail_prepare_message_body(); @@ -449,7 +317,8 @@ // #1486037: remove "mailto:" prefix $COMPOSE['param']['to'] = preg_replace('/^mailto:/i', '', $mailto[0]); // #1490346: decode the recipient address - $COMPOSE['param']['to'] = urldecode($COMPOSE['param']['to']); + // #1490510: use raw encoding for correct "+" character handling as specified in RFC6068 + $COMPOSE['param']['to'] = rawurldecode($COMPOSE['param']['to']); // Supported case-insensitive tokens in mailto URL $url_tokens = array('to', 'cc', 'bcc', 'reply-to', 'in-reply-to', 'references', 'subject', 'body'); @@ -583,7 +452,7 @@ // create teaxtarea object $input = new $field_type($field_attrib); - $out = $input->show($MESSAGE->compose[$param]); + $out = $input->show(rcmail_compose_header_value($param)); } if ($form_start) { @@ -599,7 +468,7 @@ function rcmail_compose_header_from($attrib) { - global $MESSAGE, $OUTPUT, $RCMAIL, $COMPOSE, $compose_mode; + global $MESSAGE, $OUTPUT, $RCMAIL, $COMPOSE; // pass the following attributes to the form class $field_attrib = array('name' => '_from'); @@ -612,8 +481,10 @@ if (count($MESSAGE->identities)) { $a_signatures = array(); $identities = array(); - $separator = intval($RCMAIL->config->get('reply_mode')) > 0 - && ($compose_mode == RCUBE_COMPOSE_REPLY || $compose_mode == RCUBE_COMPOSE_FORWARD) ? '---' : '-- '; + $top_posting = intval($RCMAIL->config->get('reply_mode')) > 0 + && !$RCMAIL->config->get('sig_below') + && ($COMPOSE['mode'] == RCUBE_COMPOSE_REPLY || $COMPOSE['mode'] == RCUBE_COMPOSE_FORWARD); + $separator = $top_posting ? '---' : '-- '; $field_attrib['onchange'] = rcmail_output::JS_OBJECT_NAME.".change_identity(this)"; $select_from = new html_select($field_attrib); @@ -628,21 +499,17 @@ $text = $html = $sql_arr['signature']; if ($sql_arr['html_signature']) { - $h2t = new rcube_html2text($sql_arr['signature'], false, true); + $h2t = new rcube_html2text($html, false, true); $text = trim($h2t->get_text()); } else { - $html = htmlentities($html, ENT_NOQUOTES, RCUBE_CHARSET); + $t2h = new rcube_text2html($text, false); + $html = $t2h->get_html(); } if (!preg_match('/^--[ -]\r?\n/m', $text)) { $text = $separator . "\n" . $text; $html = $separator . "<br>" . $html; - } - - if (!$sql_arr['html_signature']) { - $t2h = new rcube_text2html($sql_arr['signature'], false); - $html = $t2h->get_html(); } $a_signatures[$identity_id]['text'] = $text; @@ -674,17 +541,174 @@ return $out; } +function rcmail_compose_header_value($header) +{ + global $COMPOSE, $MESSAGE; + + $RCMAIL = rcube::get_instance(); + $fvalue = ''; + $decode_header = true; + $charset = $MESSAGE->headers->charset; + $separator = trim($RCMAIL->config->get('recipients_separator', ',')) . ' '; + + // we have a set of recipients stored is session + if ($header == 'to' && ($mailto_id = $COMPOSE['param']['mailto']) + && $_SESSION['mailto'][$mailto_id] + ) { + $fvalue = urldecode($_SESSION['mailto'][$mailto_id]); + $decode_header = false; + $charset = $RCMAIL->output->charset; + + // make session to not grow up too much + unset($_SESSION['mailto'][$mailto_id]); + $COMPOSE['param']['to'] = $fvalue; + } + else if (!empty($_POST['_' . $header])) { + $fvalue = rcube_utils::get_input_value('_' . $header, rcube_utils::INPUT_POST, true); + $charset = $RCMAIL->output->charset; + } + else if (!empty($COMPOSE['param'][$header])) { + $fvalue = $COMPOSE['param'][$header]; + $charset = $RCMAIL->output->charset; + } + else if ($COMPOSE['mode'] == RCUBE_COMPOSE_REPLY) { + // get recipent address(es) out of the message headers + if ($header == 'to') { + $mailfollowup = $MESSAGE->headers->others['mail-followup-to']; + $mailreplyto = $MESSAGE->headers->others['mail-reply-to']; + + // Reply to mailing list... + if ($MESSAGE->reply_all == 'list' && $mailfollowup) { + $fvalue = $mailfollowup; + } + else if ($MESSAGE->reply_all == 'list' + && preg_match('/<mailto:([^>]+)>/i', $MESSAGE->headers->others['list-post'], $m) + ) { + $fvalue = $m[1]; + } + // Reply to... + else if ($MESSAGE->reply_all && $mailfollowup) { + $fvalue = $mailfollowup; + } + else if ($mailreplyto) { + $fvalue = $mailreplyto; + } + else if (!empty($MESSAGE->headers->replyto)) { + $fvalue = $MESSAGE->headers->replyto; + $replyto = true; + } + else if (!empty($MESSAGE->headers->from)) { + $fvalue = $MESSAGE->headers->from; + } + + // Reply to message sent by yourself (#1487074, #1489230, #1490439) + // Reply-To address need to be unset (#1490233) + if (!empty($MESSAGE->compose['ident']) && empty($replyto)) { + foreach (array($fvalue, $MESSAGE->headers->from) as $sender) { + $senders = rcube_mime::decode_address_list($sender, null, false, $charset, true); + + if (in_array($MESSAGE->compose['ident']['email_ascii'], $senders)) { + $fvalue = $MESSAGE->headers->to; + break; + } + } + } + } + // add recipient of original message if reply to all + else if ($header == 'cc' && !empty($MESSAGE->reply_all) && $MESSAGE->reply_all != 'list') { + if ($v = $MESSAGE->headers->to) { + $fvalue .= $v; + } + if ($v = $MESSAGE->headers->cc) { + $fvalue .= (!empty($fvalue) ? $separator : '') . $v; + } + // Use Sender header (#1489011) + if ($v = $MESSAGE->headers->get('Sender', false)) { + // Skip common mailing lists addresses: *-bounces@ and *-request@ (#1490452) + if (empty($MESSAGE->headers->others['list-post']) || !preg_match('/-(bounces|request)@/', $v)) { + $fvalue .= (!empty($fvalue) ? $separator : '') . $v; + } + } + + // When To: and Reply-To: are the same we add From: address to the list (#1489037) + if ($v = $MESSAGE->headers->from) { + $from = rcube_mime::decode_address_list($v, null, false, $charset, true); + $to = rcube_mime::decode_address_list($MESSAGE->headers->to, null, false, $charset, true); + $replyto = rcube_mime::decode_address_list($MESSAGE->headers->replyto, null, false, $charset, true); + + if (count($replyto) && !count(array_diff($to, $replyto)) && count(array_diff($from, $to))) { + $fvalue .= (!empty($fvalue) ? $separator : '') . $v; + } + } + } + } + else if (in_array($COMPOSE['mode'], array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) { + // get drafted headers + if ($header == 'to' && !empty($MESSAGE->headers->to)) { + $fvalue = $MESSAGE->get_header('to', true); + } + else if ($header == 'cc' && !empty($MESSAGE->headers->cc)) { + $fvalue = $MESSAGE->get_header('cc', true); + } + else if ($header == 'bcc' && !empty($MESSAGE->headers->bcc)) { + $fvalue = $MESSAGE->get_header('bcc', true); + } + else if ($header == 'replyto' && !empty($MESSAGE->headers->others['mail-reply-to'])) { + $fvalue = $MESSAGE->get_header('mail-reply-to'); + } + else if ($header == 'replyto' && !empty($MESSAGE->headers->replyto)) { + $fvalue = $MESSAGE->get_header('reply-to'); + } + else if ($header == 'followupto' && !empty($MESSAGE->headers->others['mail-followup-to'])) { + $fvalue = $MESSAGE->get_header('mail-followup-to'); + } + } + + // split recipients and put them back together in a unique way + if (!empty($fvalue) && in_array($header, array('to', 'cc', 'bcc'))) { + $from_email = @mb_strtolower($MESSAGE->compose['ident']['email']); + $to_addresses = rcube_mime::decode_address_list($fvalue, null, $decode_header, $charset); + $fvalue = array(); + + foreach ($to_addresses as $addr_part) { + if (empty($addr_part['mailto'])) { + continue; + } + + // According to RFC5321 local part of email address is case-sensitive + // however, here it is better to compare addresses in case-insensitive manner + $mailto = format_email(rcube_utils::idn_to_utf8($addr_part['mailto'])); + $mailto_lc = mb_strtolower($addr_part['mailto']); + + if (($header == 'to' || $COMPOSE['mode'] != RCUBE_COMPOSE_REPLY || $mailto_lc != $from_email) + && !in_array($mailto_lc, (array) $MESSAGE->recipients) + ) { + if ($addr_part['name'] && $mailto != $addr_part['name']) { + $mailto = format_email_recipient($mailto, $addr_part['name']); + } + + $fvalue[] = $mailto; + $MESSAGE->recipients[] = $mailto_lc; + } + } + + $fvalue = implode($separator, $fvalue); + } + + return $fvalue; +} function rcmail_compose_editor_mode() { - global $RCMAIL, $compose_mode; + global $RCMAIL, $COMPOSE; static $useHtml; if ($useHtml !== null) { return $useHtml; } - $html_editor = intval($RCMAIL->config->get('htmleditor')); + $html_editor = intval($RCMAIL->config->get('htmleditor')); + $compose_mode = $COMPOSE['mode']; if (isset($_POST['_is_html'])) { $useHtml = !empty($_POST['_is_html']); @@ -714,7 +738,7 @@ function rcmail_prepare_message_body() { - global $RCMAIL, $MESSAGE, $COMPOSE, $compose_mode, $HTML_MODE; + global $RCMAIL, $MESSAGE, $COMPOSE, $HTML_MODE; // use posted message body if (!empty($_POST['_message'])) { @@ -726,20 +750,20 @@ $isHtml = (bool) $COMPOSE['param']['html']; } // forward as attachment - else if ($compose_mode == RCUBE_COMPOSE_FORWARD && $COMPOSE['as_attachment']) { + else if ($COMPOSE['mode'] == RCUBE_COMPOSE_FORWARD && $COMPOSE['as_attachment']) { $isHtml = rcmail_compose_editor_mode(); $body = ''; rcmail_write_forward_attachments(); } // reply/edit/draft/forward - else if ($compose_mode && ($compose_mode != RCUBE_COMPOSE_REPLY || intval($RCMAIL->config->get('reply_mode')) != -1)) { + else if ($COMPOSE['mode'] && ($COMPOSE['mode'] != RCUBE_COMPOSE_REPLY || intval($RCMAIL->config->get('reply_mode')) != -1)) { $isHtml = rcmail_compose_editor_mode(); $messages = array(); if (!empty($MESSAGE->parts)) { // collect IDs of message/rfc822 parts - if ($compose_mode == RCUBE_COMPOSE_EDIT || $compose_mode == RCUBE_COMPOSE_DRAFT) { + if ($COMPOSE['mode'] == RCUBE_COMPOSE_EDIT || $COMPOSE['mode'] == RCUBE_COMPOSE_DRAFT) { foreach ($MESSAGE->attachments as $part) { if ($part->mimetype == 'message/rfc822') { $messages[] = $part->mime_id; @@ -770,13 +794,13 @@ } // compose reply-body - if ($compose_mode == RCUBE_COMPOSE_REPLY) + if ($COMPOSE['mode'] == RCUBE_COMPOSE_REPLY) $body = rcmail_create_reply_body($body, $isHtml); // forward message body inline - else if ($compose_mode == RCUBE_COMPOSE_FORWARD) + else if ($COMPOSE['mode'] == RCUBE_COMPOSE_FORWARD) $body = rcmail_create_forward_body($body, $isHtml); // load draft message body - else if ($compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT) + else if ($COMPOSE['mode'] == RCUBE_COMPOSE_DRAFT || $COMPOSE['mode'] == RCUBE_COMPOSE_EDIT) $body = rcmail_create_draft_body($body, $isHtml); } else { // new message @@ -784,7 +808,7 @@ } $plugin = $RCMAIL->plugins->exec_hook('message_compose_body', - array('body' => $body, 'html' => $isHtml, 'mode' => $compose_mode)); + array('body' => $body, 'html' => $isHtml, 'mode' => $COMPOSE['mode'])); $body = $plugin['body']; unset($plugin); @@ -807,7 +831,7 @@ function rcmail_compose_part_body($part, $isHtml = false) { - global $RCMAIL, $MESSAGE, $LINE_LENGTH, $compose_mode; + global $RCMAIL, $MESSAGE, $LINE_LENGTH; // Check if we have enough memory to handle the message in it // #1487424: we need up to 10x more memory than the body @@ -831,7 +855,7 @@ } else { // try to remove the signature - if ($compose_mode != RCUBE_COMPOSE_DRAFT && $compose_mode != RCUBE_COMPOSE_EDIT) { + if ($COMPOSE['mode'] != RCUBE_COMPOSE_DRAFT && $COMPOSE['mode'] != RCUBE_COMPOSE_EDIT) { if ($RCMAIL->config->get('strip_existing_sig', true)) { $body = rcmail_remove_signature($body); } @@ -850,7 +874,7 @@ if ($part->ctype_secondary == 'html') { // use html part if it has been used for message (pre)viewing // decrease line length for quoting - $len = $compose_mode == RCUBE_COMPOSE_REPLY ? $LINE_LENGTH-2 : $LINE_LENGTH; + $len = $COMPOSE['mode'] == RCUBE_COMPOSE_REPLY ? $LINE_LENGTH-2 : $LINE_LENGTH; $txt = new rcube_html2text($body, false, true, $len); $body = $txt->get_text(); } @@ -860,7 +884,7 @@ } // try to remove the signature - if ($compose_mode != RCUBE_COMPOSE_DRAFT && $compose_mode != RCUBE_COMPOSE_EDIT) { + if ($COMPOSE['mode'] != RCUBE_COMPOSE_DRAFT && $COMPOSE['mode'] != RCUBE_COMPOSE_EDIT) { if ($RCMAIL->config->get('strip_existing_sig', true)) { $body = rcmail_remove_signature($body); } @@ -1122,8 +1146,24 @@ if ($bodyIsHtml) { $body = rcmail_wash_html($body, array('safe' => 1), $cid_map); - // remove comments (produced by washtml) - $body = preg_replace('/<!--[^>]+-->/', '', $body); + // cleanup + $body = preg_replace(array( + // remove comments (produced by washtml) + '/<!--[^>]+-->/', + // remove <body> tags + '/<body([^>]*)>/i', + '/<\/body>/i', + // convert TinyMCE's empty-line sequence (#1490463) + '/<p>\xC2\xA0<\/p>/', + ), + array( + '', + '', + '', + '<p><br /></p>', + ), + $body + ); // replace cid with href in inline images links if (!empty($cid_map)) { @@ -1159,7 +1199,7 @@ function rcmail_write_compose_attachments(&$message, $bodyIsHtml) { - global $RCMAIL, $COMPOSE, $compose_mode; + global $RCMAIL, $COMPOSE; $loaded_attachments = array(); foreach ((array)$COMPOSE['attachments'] as $attachment) { @@ -1177,12 +1217,12 @@ } // skip message attachments in reply mode - if ($part->ctype_primary == 'message' && $compose_mode == RCUBE_COMPOSE_REPLY) { + if ($part->ctype_primary == 'message' && $COMPOSE['mode'] == RCUBE_COMPOSE_REPLY) { continue; } // skip inline images when forwarding in text mode - if ($part->content_id && $part->disposition == 'inline' && !$bodyIsHtml && $compose_mode == RCUBE_COMPOSE_FORWARD) { + if ($part->content_id && $part->disposition == 'inline' && !$bodyIsHtml && $COMPOSE['mode'] == RCUBE_COMPOSE_FORWARD) { continue; } @@ -1192,12 +1232,12 @@ // skipped e.g. images used in HTML body or other attachments. So, // better to skip .eml attachments but not their content (included files). if ($part->mimetype == 'message/rfc822') { - if ($compose_mode == RCUBE_COMPOSE_FORWARD) { + if ($COMPOSE['mode'] == RCUBE_COMPOSE_FORWARD) { continue; } $messages[] = $part->mime_id; } - else if ($compose_mode != RCUBE_COMPOSE_FORWARD) { + else if ($COMPOSE['mode'] != RCUBE_COMPOSE_FORWARD) { // skip attachments included in message/rfc822 attachment (#1486487) foreach ($messages as $mimeid) { if (strpos($part->mime_id, $mimeid . '.') === 0) { @@ -1460,9 +1500,30 @@ } } +/** + * Creates reply subject by removing common subject + * prefixes/suffixes from the original message subject + */ +function rcmail_reply_subject($subject) +{ + $subject = trim($subject); + + // replace Re:, Re[x]:, Re-x (#1490497) + $prefix = '/^(re:|re\[\d\]:|re-\d:)\s*/i'; + do { + $subject = preg_replace($prefix, '', $subject, -1, $count); + } + while ($count); + + // replace (was: ...) (#1489375) + $subject = preg_replace('/\s*\([wW]as:[^\)]+\)\s*$/', '', $subject); + + return 'Re: ' . $subject; +} + function rcmail_compose_subject($attrib) { - global $MESSAGE, $COMPOSE, $compose_mode; + global $MESSAGE, $COMPOSE; list($form_start, $form_end) = get_form_tags($attrib); unset($attrib['form']); @@ -1481,24 +1542,18 @@ $subject = $COMPOSE['param']['subject']; } // create a reply-subject - else if ($compose_mode == RCUBE_COMPOSE_REPLY) { - if (preg_match('/^re:/i', $MESSAGE->subject)) - $subject = $MESSAGE->subject; - else - $subject = 'Re: '.$MESSAGE->subject; - - // replace (was: ...) (#1489375) - $subject = preg_replace('/\s*\([wW]as:[^\)]+\)\s*$/', '', $subject); + else if ($COMPOSE['mode'] == RCUBE_COMPOSE_REPLY) { + $subject = rcmail_reply_subject($MESSAGE->subject); } // create a forward-subject - else if ($compose_mode == RCUBE_COMPOSE_FORWARD) { + else if ($COMPOSE['mode'] == RCUBE_COMPOSE_FORWARD) { if (preg_match('/^fwd:/i', $MESSAGE->subject)) $subject = $MESSAGE->subject; else $subject = 'Fwd: '.$MESSAGE->subject; } // creeate a draft-subject - else if ($compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT) { + else if ($COMPOSE['mode'] == RCUBE_COMPOSE_DRAFT || $COMPOSE['mode'] == RCUBE_COMPOSE_EDIT) { $subject = $MESSAGE->subject; } @@ -1508,7 +1563,6 @@ return $out; } - function rcmail_compose_attachment_list($attrib) { @@ -1668,7 +1722,7 @@ function rcmail_mdn_checkbox($attrib) { - global $RCMAIL, $MESSAGE, $compose_mode; + global $RCMAIL, $MESSAGE; list($form_start, $form_end) = get_form_tags($attrib); unset($attrib['form']); @@ -1683,7 +1737,7 @@ if (isset($_POST['_mdn'])) $mdn_default = $_POST['_mdn']; - else if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) + else if (in_array($COMPOSE['mode'], array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) $mdn_default = (bool) $MESSAGE->headers->mdn_to; else $mdn_default = $RCMAIL->config->get('mdn_default'); -- Gitblit v1.9.1