From 46f7b7096450939fe03c95aa81ce06ae4bfca89d Mon Sep 17 00:00:00 2001 From: Aleksander Machniak <alec@alec.pl> Date: Mon, 28 Mar 2016 06:51:43 -0400 Subject: [PATCH] Enable reply/reply-all/forward buttons also in preview frame of message/rfc822 --- program/steps/mail/compose.inc | 805 +++++++++++++++++++++++++++++++-------------------------- 1 files changed, 434 insertions(+), 371 deletions(-) diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index d9a33bf..9ef8647 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -1,11 +1,11 @@ <?php -/* +/** +-----------------------------------------------------------------------+ | program/steps/mail/compose.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2013, The Roundcube Dev Team | + | Copyright (C) 2005-2016, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -47,19 +47,22 @@ 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()); + $params = rcube_utils::request2param(rcube_utils::INPUT_GET, 'task|action', true); + $_SESSION['compose_data_'.$COMPOSE_ID] = array( 'id' => $COMPOSE_ID, - 'param' => rcube_utils::request2param(rcube_utils::INPUT_GET, 'task|action', true), - 'mailbox' => $RCMAIL->storage->get_folder(), + 'param' => $params, + 'mailbox' => $params['mbox'] ?: $RCMAIL->storage->get_folder(), ); $COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID]; - rcmail_process_compose_params($COMPOSE); // check if folder for saving sent messages exists and is subscribed (#1486802) @@ -80,9 +83,12 @@ $OUTPUT->add_label('nosubject', 'nosenderwarning', 'norecipientwarning', 'nosubjectwarning', 'cancel', 'nobodywarning', 'notsentwarning', 'notuploadedwarning', 'savingmessage', 'sendingmessage', 'messagesaved', 'converting', 'editorwarning', 'searching', 'uploading', 'uploadingmany', - 'fileuploaderror', 'sendmessage', 'savenewresponse', 'responsename', 'responsetext', 'save', + 'fileuploaderror', 'sendmessage', 'newresponse', 'responsename', 'responsetext', 'save', 'savingresponse', 'restoresavedcomposedata', 'restoremessage', 'delete', 'restore', 'ignore', - 'selectimportfile'); + 'selectimportfile', 'messageissent', 'loadingdata', 'nopubkeyfor', 'nopubkeyforsender', + 'encryptnoattachments','encryptedsendialog','searchpubkeyservers', 'importpubkeys', + 'encryptpubkeysfound', 'search', 'close', 'import', 'keyid', 'keylength', 'keyexpired', + 'keyrevoked', 'keyimportsuccess', 'keyservererror', 'attaching'); $OUTPUT->set_pagetitle($RCMAIL->gettext('compose')); @@ -90,7 +96,10 @@ $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); $drafts_mbox = $RCMAIL->config->get('drafts_mbox'); $config_show_sig = $RCMAIL->config->get('show_sig', 1); @@ -130,8 +139,10 @@ $compose_mode = RCUBE_COMPOSE_EDIT; } -$COMPOSE['mode'] = $compose_mode; -$OUTPUT->set_env('compose_mode', $compose_mode); +if ($compose_mode) { + $COMPOSE['mode'] = $compose_mode; + $OUTPUT->set_env('compose_mode', $compose_mode); +} if ($compose_mode == RCUBE_COMPOSE_EDIT || $compose_mode == RCUBE_COMPOSE_DRAFT) { // don't add signature in draft/edit mode, we'll also not remove the old-one @@ -168,7 +179,7 @@ $MESSAGE = new rcube_message($msg_uid); // make sure message is marked as read - if ($MESSAGE->headers && empty($MESSAGE->headers->flags['SEEN'])) { + if ($MESSAGE->headers && $MESSAGE->context === null && empty($MESSAGE->headers->flags['SEEN'])) { $RCMAIL->storage->set_flag($msg_uid, 'SEEN'); } @@ -181,7 +192,7 @@ } else if ($compose_mode == RCUBE_COMPOSE_FORWARD || $compose_mode == RCUBE_COMPOSE_REPLY) { if ($compose_mode == RCUBE_COMPOSE_REPLY) { - $COMPOSE['reply_uid'] = $msg_uid; + $COMPOSE['reply_uid'] = $MESSAGE->context === null ? $msg_uid : null; if (!empty($COMPOSE['param']['all'])) { $MESSAGE->reply_all = $COMPOSE['param']['all']; @@ -267,139 +278,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; - else if (!empty($MESSAGE->headers->from)) - $fvalue = $MESSAGE->headers->from; - - // Reply to message sent by yourself (#1487074, #1489230) - if (!empty($ident) && 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(); @@ -416,7 +297,8 @@ 'filedroparea' => 'compose_file_drop_area', 'priorityselector' => 'rcmail_priority_selector', 'editorselector' => 'rcmail_editor_selector', - 'receiptcheckbox' => 'rcmail_receipt_checkbox', + 'receiptcheckbox' => 'rcmail_mdn_checkbox', // deprecated + 'mdncheckbox' => 'rcmail_mdn_checkbox', 'dsncheckbox' => 'rcmail_dsn_checkbox', 'storetarget' => 'rcmail_store_target_selection', 'addressbooks' => 'rcmail_addressbook_list', @@ -437,6 +319,9 @@ // #1486037: remove "mailto:" prefix $COMPOSE['param']['to'] = preg_replace('/^mailto:/i', '', $mailto[0]); + // #1490346: decode the recipient address + // #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'); @@ -474,7 +359,12 @@ // clean HTML message body which can be submitted by URL if (!empty($COMPOSE['param']['body'])) { - $COMPOSE['param']['body'] = rcmail_wash_html($COMPOSE['param']['body'], array('safe' => false, 'inline_html' => true), array()); + if ($COMPOSE['param']['html'] = strpos($COMPOSE['param']['body'], '<') !== false) { + $wash_params = array('safe' => false, 'inline_html' => true); + $COMPOSE['param']['body'] = rcmail_wash_html($COMPOSE['param']['body'], $wash_params, array()); + $COMPOSE['param']['body'] = preg_replace('/<!--[^>\n]+>/', '', $COMPOSE['param']['body']); + $COMPOSE['param']['body'] = preg_replace('/<\/?body>/', '', $COMPOSE['param']['body']); + } } $RCMAIL = rcmail::get_instance(); @@ -500,6 +390,7 @@ 'group' => $COMPOSE_ID, 'name' => $filename, 'mimetype' => rcube_mime::file_content_type($attach, $filename), + 'size' => filesize($attach), 'path' => $attach, ); } @@ -570,7 +461,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) { @@ -586,7 +477,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'); @@ -599,8 +490,12 @@ 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 ? '---' : '-- '; + $add_separator = (bool) $RCMAIL->config->get('sig_separator'); $field_attrib['onchange'] = rcmail_output::JS_OBJECT_NAME.".change_identity(this)"; $select_from = new html_select($field_attrib); @@ -615,21 +510,17 @@ $text = $html = $sql_arr['signature']; if ($sql_arr['html_signature']) { - $h2t = new rcube_html2text($sql_arr['signature'], false, true); - $text = trim($h2t->get_text()); + $text = $RCMAIL->html2text($html, array('links' => false)); + $text = trim($text, "\r\n"); } else { - $html = htmlentities($html, ENT_NOQUOTES, RCUBE_CHARSET); - } - - 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); + $t2h = new rcube_text2html($text, false); $html = $t2h->get_html(); + } + + if ($add_separator && !preg_match('/^--[ -]\r?\n/m', $text)) { + $text = $separator . "\n" . ltrim($text, "\r\n"); + $html = $separator . "<br>" . $html; } $a_signatures[$identity_id]['text'] = $text; @@ -643,6 +534,8 @@ if (!empty($sql_arr['bcc'])) { $identities[$identity_id]['bcc'] = $sql_arr['bcc']; } + + $identities[$identity_id]['email'] = $sql_arr['email']; } $out = $select_from->show($MESSAGE->compose['from']); @@ -661,32 +554,193 @@ 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'])) { + if (is_bool($COMPOSE['param']['html'])) { + $useHtml = $COMPOSE['param']['html']; + } + else if (isset($_POST['_is_html'])) { $useHtml = !empty($_POST['_is_html']); } else if ($compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT) { $useHtml = rcmail_message_is_html(); } else if ($compose_mode == RCUBE_COMPOSE_REPLY) { - $useHtml = ($html_editor == 1 || ($html_editor >= 2 && rcmail_message_is_html())); + $useHtml = $html_editor == 1 || ($html_editor >= 2 && rcmail_message_is_html()); } else if ($compose_mode == RCUBE_COMPOSE_FORWARD) { - $useHtml = ($html_editor == 1 || ($html_editor == 3 && rcmail_message_is_html())); + $useHtml = $html_editor == 1 || $html_editor == 4 + || ($html_editor == 3 && rcmail_message_is_html()); } else { - $useHtml = ($html_editor == 1); + $useHtml = $html_editor == 1 || $html_editor == 4; } return $useHtml; @@ -701,7 +755,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'])) { @@ -713,34 +767,44 @@ $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) { - foreach ($MESSAGE->attachments as $part) { - if ($part->mimetype == 'message/rfc822') { - $messages[] = $part->mime_id; - } + foreach ($MESSAGE->mime_parts() as $part) { + if ($part->mimetype == 'message/rfc822') { + $messages[] = $part->mime_id; } } foreach ($MESSAGE->parts as $part) { + if ($part->realtype == 'multipart/encrypted') { + // find the encrypted message payload part + if ($pgp_mime_part = $MESSAGE->get_multipart_encrypted_part()) { + $RCMAIL->output->set_env('pgp_mime_message', array( + '_mbox' => $RCMAIL->storage->get_folder(), + '_uid' => $MESSAGE->uid, + '_part' => $pgp_mime_part->mime_id, + )); + } + continue; + } + // skip no-content and attachment parts (#1488557) if ($part->type != 'content' || !$part->size || $MESSAGE->is_attachment($part)) { continue; } - // skip all content parts inside the message/rfc822 part in DRAFT/EDIT mode + // skip all content parts inside the message/rfc822 part foreach ($messages as $mimeid) { if (strpos($part->mime_id, $mimeid . '.') === 0) { continue 2; @@ -757,32 +821,40 @@ } // compose reply-body - if ($compose_mode == RCUBE_COMPOSE_REPLY) + if ($COMPOSE['mode'] == RCUBE_COMPOSE_REPLY) { $body = rcmail_create_reply_body($body, $isHtml); + + if ($MESSAGE->pgp_mime) { + $RCMAIL->output->set_env('compose_reply_header', rcmail_get_reply_header($MESSAGE)); + } + } // 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 $isHtml = rcmail_compose_editor_mode(); } $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); // add blocked.gif attachment (#1486516) - if ($isHtml && preg_match('#<img src="\./program/resources/blocked\.gif"#', $body)) { - if ($attachment = rcmail_save_image('program/resources/blocked.gif', 'image/gif')) { + if ($isHtml && preg_match('#<img src="program/resources/blocked\.gif"#', $body)) { + $content = $RCMAIL->get_resource_content('blocked.gif'); + if ($content && ($attachment = rcmail_save_image('blocked.gif', 'image/gif', $content))) { $COMPOSE['attachments'][$attachment['id']] = $attachment; $url = sprintf('%s&_id=%s&_action=display-attachment&_file=rcmfile%s', $RCMAIL->comm_path, $COMPOSE['id'], $attachment['id']); - $body = preg_replace('#\./program/resources/blocked\.gif#', $url, $body); + $body = preg_replace('#program/resources/blocked\.gif#', $url, $body); } } @@ -793,7 +865,7 @@ function rcmail_compose_part_body($part, $isHtml = false) { - global $RCMAIL, $MESSAGE, $LINE_LENGTH, $compose_mode; + global $RCMAIL, $COMPOSE, $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 @@ -801,21 +873,21 @@ return ''; } - if (empty($part->ctype_parameters) || empty($part->ctype_parameters['charset'])) { - $part->ctype_parameters['charset'] = $MESSAGE->headers->charset; - } - // fetch part if not available - if (!isset($part->body)) { - $part->body = $MESSAGE->get_part_content($part->mime_id); - } + $body = $MESSAGE->get_part_body($part->mime_id, true); // message is cached but not exists (#1485443), or other error - if ($part->body === false) { + if ($body === false) { return ''; } - $body = $part->body; + // register this part as pgp encrypted + if (strpos($body, '-----BEGIN PGP MESSAGE-----') !== false) { + $MESSAGE->pgp_mime = true; + $RCMAIL->output->set_env('pgp_mime_message', array( + '_mbox' => $RCMAIL->storage->get_folder(), '_uid' => $MESSAGE->uid, '_part' => $part->mime_id, + )); + } if ($isHtml) { if ($part->ctype_secondary == 'html') { @@ -825,7 +897,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); } @@ -844,9 +916,8 @@ 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; - $txt = new rcube_html2text($body, false, true, $len); - $body = $txt->get_text(); + $len = $COMPOSE['mode'] == RCUBE_COMPOSE_REPLY ? $LINE_LENGTH-2 : $LINE_LENGTH; + $body = $RCMAIL->html2text($body, array('width' => $len)); } else { if ($part->ctype_secondary == 'plain' && $part->ctype_parameters['format'] == 'flowed') { @@ -854,7 +925,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); } @@ -956,7 +1027,7 @@ "googie.setCurrentLanguage('%s');\n". "googie.setDecoration(false);\n". "googie.decorateTextarea('%s');\n", - $RCMAIL->output->get_skin_path(), + $RCMAIL->output->asset_url($RCMAIL->output->get_skin_path()), $RCMAIL->url(array('_task' => 'utils', '_action' => 'spell', '_remote' => 1)), !empty($dictionary) ? 'true' : 'false', rcube::JQ(rcube::Q($RCMAIL->gettext('checkspelling'))), @@ -983,15 +1054,8 @@ { global $RCMAIL, $MESSAGE, $LINE_LENGTH; - // build reply prefix - $from = array_pop(rcube_mime::decode_address_list($MESSAGE->get_header('from'), 1, false, $MESSAGE->headers->charset)); - $prefix = $RCMAIL->gettext(array( - 'name' => 'mailreplyintro', - 'vars' => array( - 'date' => $RCMAIL->format_date($MESSAGE->headers->date, $RCMAIL->config->get('date_long')), - 'sender' => $from['name'] ? $from['name'] : rcube_utils::idn_to_utf8($from['mailto']), - ) - )); + $prefix = rcmail_get_reply_header($MESSAGE); + $reply_mode = intval($RCMAIL->config->get('reply_mode')); if (!$bodyIsHtml) { $body = preg_replace('/\r?\n/', "\n", $body); @@ -1001,10 +1065,13 @@ $body = rcmail_wrap_and_quote($body, $LINE_LENGTH); $prefix .= "\n"; - $suffix = ''; - if (intval($RCMAIL->config->get('reply_mode')) > 0) { // top-posting + if ($reply_mode > 0) { // top-posting $prefix = "\n\n\n" . $prefix; + $suffix = ''; + } + else { + $suffix = "\n"; } } else { @@ -1019,18 +1086,31 @@ $prefix = '<p>' . rcube::Q($prefix) . "</p>\n"; $prefix .= '<blockquote>'; - if (intval($RCMAIL->config->get('reply_mode')) > 0) { // top-posting + if ($reply_mode > 0) { // top-posting $prefix = '<br>' . $prefix; $suffix = '</blockquote>'; } else { - $suffix = '</blockquote><p></p>'; + $suffix = '</blockquote><p><br/></p>'; } } return $prefix . $body . $suffix; } +function rcmail_get_reply_header($message) +{ + global $RCMAIL; + + $from = array_pop(rcube_mime::decode_address_list($message->get_header('from'), 1, false, $message->headers->charset)); + return $RCMAIL->gettext(array( + 'name' => 'mailreplyintro', + 'vars' => array( + 'date' => $RCMAIL->format_date($message->headers->date, $RCMAIL->config->get('date_long')), + 'sender' => $from['name'] ?: rcube_utils::idn_to_utf8($from['mailto']), + ) + )); +} function rcmail_create_forward_body($body, $bodyIsHtml) { @@ -1111,8 +1191,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)) { @@ -1148,7 +1244,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) { @@ -1158,7 +1254,15 @@ $cid_map = array(); $messages = array(); - foreach ((array)$message->mime_parts as $pid => $part) { + if ($message->pgp_mime) { + return $cid_map; + } + + foreach ((array) $message->mime_parts() as $pid => $part) { + if ($part->mimetype == 'message/rfc822') { + $messages[] = $part->mime_id; + } + if ($part->disposition == 'attachment' || ($part->disposition == 'inline' && $bodyIsHtml) || $part->filename) { // skip parts that aren't valid attachments if ($part->ctype_primary == 'multipart' || $part->mimetype == 'application/ms-tnef') { @@ -1166,40 +1270,30 @@ } // 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; } - // skip message/rfc822 attachments on forwards (#1489214) - // Thunderbird when forwarding in inline mode displays such attachments - // and skips any attachments from inside of such part, this however - // 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) { - continue; - } - $messages[] = $part->mime_id; + // skip version.txt parts of multipart/encrypted messages + if ($message->pgp_mime && $part->mimetype == 'application/pgp-encrypted' && $part->filename == 'version.txt') { + continue; } - 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) { - continue 2; - } + + // skip attachments included in message/rfc822 attachment (#1486487, #1490607) + foreach ($messages as $mimeid) { + if (strpos($part->mime_id, $mimeid . '.') === 0) { + continue 2; } } if (($attachment = $loaded_attachments[rcmail_attachment_name($part) . $part->mimetype]) - || ($attachment = rcmail_save_attachment($message, $pid)) + || ($attachment = rcmail_save_attachment($message, $pid, $COMPOSE['id'])) ) { - $COMPOSE['attachments'][$attachment['id']] = $attachment; - if ($bodyIsHtml && ($part->content_id || $part->content_location)) { $url = sprintf('%s&_id=%s&_action=display-attachment&_file=rcmfile%s', $RCMAIL->comm_path, $COMPOSE['id'], $attachment['id']); @@ -1223,11 +1317,27 @@ { global $RCMAIL, $COMPOSE; - $cid_map = array(); - foreach ((array)$message->mime_parts as $pid => $part) { + $cid_map = array(); + $messages = array(); + + if ($message->pgp_mime) { + return $cid_map; + } + + foreach ((array) $message->mime_parts() as $pid => $part) { + if ($part->mimetype == 'message/rfc822') { + $messages[] = $part->mime_id; + } + if (($part->content_id || $part->content_location) && $part->filename) { - if ($attachment = rcmail_save_attachment($message, $pid)) { - $COMPOSE['attachments'][$attachment['id']] = $attachment; + // skip attachments included in message/rfc822 attachment (#1486487, #1490607) + foreach ($messages as $mimeid) { + if (strpos($part->mime_id, $mimeid . '.') === 0) { + continue 2; + } + } + + if ($attachment = rcmail_save_attachment($message, $pid, $COMPOSE['id'])) { $url = sprintf('%s&_id=%s&_action=display-attachment&_file=rcmfile%s', $RCMAIL->comm_path, $COMPOSE['id'], $attachment['id']); @@ -1250,6 +1360,10 @@ $storage = $RCMAIL->get_storage(); $names = array(); $refs = array(); + + if ($MESSAGE->pgp_mime) { + return; + } $loaded_attachments = array(); foreach ((array)$COMPOSE['attachments'] as $attachment) { @@ -1291,47 +1405,11 @@ $names[$name] = 1; $name .= '.eml'; - $data = $path = null; - if (!empty($loaded_attachments[$name . 'message/rfc822'])) { continue; } - // don't load too big attachments into memory - if (!rcube_utils::mem_check($message->size)) { - $temp_dir = unslashify($RCMAIL->config->get('temp_dir')); - $path = tempnam($temp_dir, 'rcmAttmnt'); - if ($fp = fopen($path, 'w')) { - $storage->get_raw_body($message->uid, $fp); - fclose($fp); - } - else { - return false; - } - } - else { - $data = $storage->get_raw_body($message->uid); - $curr_mem += $message->size; - } - - $attachment = array( - 'group' => $COMPOSE['id'], - 'name' => $name, - 'mimetype' => 'message/rfc822', - 'data' => $data, - 'path' => $path, - 'size' => $path ? filesize($path) : strlen($data), - ); - - $attachment = $RCMAIL->plugins->exec_hook('attachment_save', $attachment); - - if ($attachment['status']) { - unset($attachment['data'], $attachment['status'], $attachment['content_id'], $attachment['abort']); - $COMPOSE['attachments'][$attachment['id']] = $attachment; - } - else if ($path) { - @unlink($path); - } + rcmail_save_attachment($message, null, $COMPOSE['id'], array('filename' => $name)); if ($message->headers->messageID) { $refs[] = $message->headers->messageID; @@ -1347,70 +1425,31 @@ } } - -function rcmail_save_attachment(&$message, $pid) -{ - global $COMPOSE; - - $rcmail = rcmail::get_instance(); - $part = $message->mime_parts[$pid]; - $data = $path = null; - - // don't load too big attachments into memory - if (!rcube_utils::mem_check($part->size)) { - $temp_dir = unslashify($rcmail->config->get('temp_dir')); - $path = tempnam($temp_dir, 'rcmAttmnt'); - - if ($fp = fopen($path, 'w')) { - $message->get_part_content($pid, $fp, true, 0, false); - fclose($fp); - } - else { - return false; - } - } - else { - $data = $message->get_part_content($pid, null, true, 0, false); - } - - $mimetype = $part->ctype_primary . '/' . $part->ctype_secondary; - $filename = rcmail_attachment_name($part); - - $attachment = array( - 'group' => $COMPOSE['id'], - 'name' => $filename, - 'mimetype' => $mimetype, - 'content_id' => $part->content_id, - 'data' => $data, - 'path' => $path, - 'size' => $path ? filesize($path) : strlen($data), - ); - - $attachment = $rcmail->plugins->exec_hook('attachment_save', $attachment); - - if ($attachment['status']) { - unset($attachment['data'], $attachment['status'], $attachment['content_id'], $attachment['abort']); - return $attachment; - } - else if ($path) { - @unlink($path); - } - - return false; -} - -function rcmail_save_image($path, $mimetype='') +function rcmail_save_image($path, $mimetype = '', $data = null) { global $COMPOSE; // handle attachments in memory - $data = file_get_contents($path); + if (empty($data)) { + $data = file_get_contents($path); + $is_file = true; + } + $name = rcmail_basename($path); + + if (empty($mimetype)) { + if ($is_file) { + $mimetype = rcube_mime::file_content_type($path, $name); + } + else { + $mimetype = rcube_mime::file_content_type($data, $name, 'application/octet-stream', true); + } + } $attachment = array( 'group' => $COMPOSE['id'], 'name' => $name, - 'mimetype' => $mimetype ? $mimetype : rcube_mime::file_content_type($path, $name), + 'mimetype' => $mimetype, 'data' => $data, 'size' => strlen($data), ); @@ -1436,9 +1475,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']); @@ -1453,29 +1513,23 @@ if (isset($_POST['_subject'])) { $subject = rcube_utils::get_input_value('_subject', rcube_utils::INPUT_POST, TRUE); } + else if (!empty($COMPOSE['param']['subject'])) { + $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; - } - else if (!empty($COMPOSE['param']['subject'])) { - $subject = $COMPOSE['param']['subject']; } $out = $form_start ? "$form_start\n" : ''; @@ -1485,7 +1539,6 @@ return $out; } - function rcmail_compose_attachment_list($attrib) { global $RCMAIL, $OUTPUT, $COMPOSE; @@ -1494,15 +1547,14 @@ if (!$attrib['id']) $attrib['id'] = 'rcmAttachmentList'; - $out = "\n"; - $jslist = array(); - $button = ''; - $skin_path = $RCMAIL->config->get('skin_path'); + $out = "\n"; + $jslist = array(); + $button = ''; if (is_array($COMPOSE['attachments'])) { if ($attrib['deleteicon']) { $button = html::img(array( - 'src' => $skin_path . $attrib['deleteicon'], + 'src' => $RCMAIL->output->abs_url($attrib['deleteicon'], true), 'alt' => $RCMAIL->gettext('delete') )); } @@ -1515,6 +1567,9 @@ continue; } + $content = sprintf('%s <span class="attachment-size">(%s)</span>', + rcube::Q($a_prop['name']), $RCMAIL->show_bytes($a_prop['size'])); + $out .= html::tag('li', array( 'id' => 'rcmfile'.$id, 'class' => rcube_utils::file2class($a_prop['mimetype'], $a_prop['name']), @@ -1524,10 +1579,12 @@ 'href' => "#delete", 'title' => $RCMAIL->gettext('delete'), 'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this)", rcmail_output::JS_OBJECT_NAME, $id), - 'class' => 'delete' + 'class' => 'delete', + 'tabindex' => $attrib['tabindex'] ?: '0', + 'aria-label' => $RCMAIL->gettext('delete') . ' ' . $a_prop['name'], ), $button - ) . rcube::Q($a_prop['name']) + ) . $content ); $jslist['rcmfile'.$id] = array( @@ -1539,16 +1596,22 @@ } if ($attrib['deleteicon']) - $COMPOSE['deleteicon'] = $skin_path . $attrib['deleteicon']; + $COMPOSE['deleteicon'] = $RCMAIL->output->abs_url($attrib['deleteicon'], true); else if (rcube_utils::get_boolean($attrib['textbuttons'])) $COMPOSE['textbuttons'] = true; if ($attrib['cancelicon']) - $OUTPUT->set_env('cancelicon', $skin_path . $attrib['cancelicon']); + $OUTPUT->set_env('cancelicon', $RCMAIL->output->abs_url($attrib['cancelicon'], true)); if ($attrib['loadingicon']) - $OUTPUT->set_env('loadingicon', $skin_path . $attrib['loadingicon']); + $OUTPUT->set_env('loadingicon', $RCMAIL->output->abs_url($attrib['loadingicon'], true)); $OUTPUT->set_env('attachments', $jslist); $OUTPUT->add_gui_object('attachmentlist', $attrib['id']); + + // put tabindex value into data-tabindex attribute + if (isset($attrib['tabindex'])) { + $attrib['data-tabindex'] = $attrib['tabindex']; + unset($attrib['tabindex']); + } return html::tag('ul', $attrib, $out, html::$common_attrib); } @@ -1635,9 +1698,9 @@ } -function rcmail_receipt_checkbox($attrib) +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']); @@ -1645,14 +1708,14 @@ if (!isset($attrib['id'])) $attrib['id'] = 'receipt'; - $attrib['name'] = '_receipt'; + $attrib['name'] = '_mdn'; $attrib['value'] = '1'; $checkbox = new html_checkbox($attrib); - if (isset($_POST['_receipt'])) - $mdn_default = $_POST['_receipt']; - else if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) + if (isset($_POST['_mdn'])) + $mdn_default = $_POST['_mdn']; + 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'); @@ -1772,8 +1835,8 @@ $form_start .= $hiddenfields->show(); } - $form_end = ($MESSAGE_FORM && !strlen($attrib['form'])) ? '</form>' : ''; - $form_name = !empty($attrib['form']) ? $attrib['form'] : 'form'; + $form_end = ($MESSAGE_FORM && !strlen($attrib['form'])) ? '</form>' : ''; + $form_name = $attrib['form'] ?: 'form'; if (!$MESSAGE_FORM) $RCMAIL->output->add_gui_object('messageform', $form_name); @@ -1795,7 +1858,7 @@ 'id' => 'rcmli%s', 'class' => '%s'), html::a(array('href' => '#list', 'rel' => '%s', - 'onclick' => "return ".rcmail_output::JS_OBJECT_NAME.".command('list-adresses','%s',this)"), '%s')); + 'onclick' => "return ".rcmail_output::JS_OBJECT_NAME.".command('list-addresses','%s',this)"), '%s')); foreach ($RCMAIL->get_address_sources(false, true) as $j => $source) { $id = strval(strlen($source['id']) ? $source['id'] : $j); @@ -1810,7 +1873,7 @@ rcube_utils::html_identifier($id,true), $class_name, $source['id'], - $js_id, (!empty($source['name']) ? $source['name'] : $id)); + $js_id, ($source['name'] ?: $id)); } $OUTPUT->add_gui_object('addressbookslist', $attrib['id']); -- Gitblit v1.9.1