diff options
Diffstat (limited to 'program/steps/mail')
-rw-r--r-- | program/steps/mail/attachments.inc | 5 | ||||
-rw-r--r-- | program/steps/mail/compose.inc | 142 | ||||
-rw-r--r-- | program/steps/mail/func.inc | 71 | ||||
-rw-r--r-- | program/steps/mail/sendmail.inc | 44 | ||||
-rw-r--r-- | program/steps/mail/show.inc | 5 |
5 files changed, 183 insertions, 84 deletions
diff --git a/program/steps/mail/attachments.inc b/program/steps/mail/attachments.inc index f83f6892e..85aa9542b 100644 --- a/program/steps/mail/attachments.inc +++ b/program/steps/mail/attachments.inc @@ -118,9 +118,12 @@ if (is_array($_FILES['_attachments']['tmp_name'])) { 'alt' => rcube_label('delete') )); } - else { + else if ($COMPOSE['textbuttons']) { $button = Q(rcube_label('delete')); } + else { + $button = ''; + } $content = html::a(array( 'href' => "#delete", diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index bce4fd313..646d2bcd1 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -54,30 +54,12 @@ if (!is_array($COMPOSE)) $COMPOSE_ID = uniqid(mt_rand()); $_SESSION['compose_data_'.$COMPOSE_ID] = array( 'id' => $COMPOSE_ID, - 'param' => request2param(RCUBE_INPUT_GET), + 'param' => rcube_utils::request2param(RCUBE_INPUT_GET, 'task|action', true), 'mailbox' => $RCMAIL->storage->get_folder(), ); $COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID]; - // process values like "mailto:foo@bar.com?subject=new+message&cc=another" - if ($COMPOSE['param']['to']) { - // #1486037: remove "mailto:" prefix - $COMPOSE['param']['to'] = preg_replace('/^mailto:/i', '', $COMPOSE['param']['to']); - $mailto = explode('?', $COMPOSE['param']['to']); - if (count($mailto) > 1) { - $COMPOSE['param']['to'] = $mailto[0]; - parse_str($mailto[1], $query); - foreach ($query as $f => $val) - $COMPOSE['param'][$f] = $val; - } - } - - // select folder where to save the sent message - $COMPOSE['param']['sent_mbox'] = $RCMAIL->config->get('sent_mbox'); - - // pipe compose parameters thru plugins - $plugin = $RCMAIL->plugins->exec_hook('message_compose', $COMPOSE); - $COMPOSE['param'] = array_merge($COMPOSE['param'], $plugin['param']); + rcmail_process_compose_params($COMPOSE); // add attachments listed by message_compose hook if (is_array($plugin['attachments'])) { @@ -149,6 +131,11 @@ if ($font && !is_array($font)) { $OUTPUT->set_env('default_font', $font); } +// default font size for HTML editor +if ($font_size = $RCMAIL->config->get('default_font_size')) { + $OUTPUT->set_env('default_font_size', $font_size); +} + // get reference message and set compose mode if ($msg_uid = $COMPOSE['param']['draft_uid']) { $compose_mode = RCUBE_COMPOSE_DRAFT; @@ -166,6 +153,8 @@ else if ($msg_uid = $COMPOSE['param']['forward_uid']) { else if ($msg_uid = $COMPOSE['param']['uid']) { $compose_mode = RCUBE_COMPOSE_EDIT; } + +$COMPOSE['mode'] = $compose_mode; $OUTPUT->set_env('compose_mode', $compose_mode); $config_show_sig = $RCMAIL->config->get('show_sig', 1); @@ -254,6 +243,14 @@ if (!empty($msg_uid) && empty($COMPOSE['as_attachment'])) } else { $MESSAGE = new stdClass(); + + // apply mailto: URL parameters + if (!empty($COMPOSE['param']['in-reply-to'])) { + $COMPOSE['reply_msgid'] = '<' . $COMPOSE['param']['in-reply-to'] . '>'; + } + if (!empty($COMPOSE['param']['references'])) { + $COMPOSE['references'] = $COMPOSE['param']['references']; + } } $MESSAGE->compose = array(); @@ -378,7 +375,12 @@ foreach ($parts as $header) { $mailto = format_email(rcube_idn_to_utf8($addr_part['mailto'])); if (!in_array($mailto, $a_recipients) - && ($header == 'to' || empty($MESSAGE->compose['from_email']) || $mailto != $MESSAGE->compose['from_email']) + && ( + $header == 'to' + || $compose_mode != RCUBE_COMPOSE_REPLY + || empty($MESSAGE->compose['from_email']) + || $mailto != $MESSAGE->compose['from_email'] + ) ) { if ($addr_part['name'] && $addr_part['mailto'] != $addr_part['name']) $string = format_email_recipient($mailto, $addr_part['name']); @@ -403,6 +405,53 @@ $MESSAGE_BODY = rcmail_prepare_message_body(); /****** compose mode functions ********/ +// process compose request parameters +function rcmail_process_compose_params(&$COMPOSE) +{ + if ($COMPOSE['param']['to']) { + $mailto = explode('?', $COMPOSE['param']['to'], 2); + + // #1486037: remove "mailto:" prefix + $COMPOSE['param']['to'] = preg_replace('/^mailto:/i', '', $mailto[0]); + + // Supported case-insensitive tokens in mailto URL + $url_tokens = array('to', 'cc', 'bcc', 'reply-to', 'in-reply-to', 'references', 'subject', 'body'); + + if (!empty($mailto[1])) { + parse_str($mailto[1], $query); + foreach ($query as $f => $val) { + if (($key = array_search(strtolower($f), $url_tokens)) !== false) { + $f = $url_tokens[$key]; + } + + // merge mailto: addresses with addresses from 'to' parameter + if ($f == 'to' && !empty($COMPOSE['param']['to'])) { + $to_addresses = rcube_mime::decode_address_list($COMPOSE['param']['to'], null, true, null, true); + $add_addresses = rcube_mime::decode_address_list($val, null, true); + foreach ($add_addresses as $addr) { + if (!in_array($addr['mailto'], $to_addresses)) { + $to_addresses[] = $addr['mailto']; + $COMPOSE['param']['to'] = (!empty($to_addresses) ? ', ' : '') . $addr['string']; + } + } + } + else { + $COMPOSE['param'][$f] = $val; + } + } + } + } + + $RCMAIL = rcmail::get_instance(); + + // select folder where to save the sent message + $COMPOSE['param']['sent_mbox'] = $RCMAIL->config->get('sent_mbox'); + + // pipe compose parameters thru plugins + $plugin = $RCMAIL->plugins->exec_hook('message_compose', $COMPOSE); + $COMPOSE['param'] = array_merge($COMPOSE['param'], $plugin['param']); +} + function rcmail_compose_headers($attrib) { global $MESSAGE; @@ -804,29 +853,14 @@ function rcmail_compose_body($attrib) // Set language list if (!empty($CONFIG['enable_spellcheck'])) { - $engine = $RCMAIL->config->get('spellcheck_engine','googie'); + $engine = new rcube_spellchecker(); $dictionary = (bool) $RCMAIL->config->get('spellcheck_dictionary'); - $spellcheck_langs = (array) $RCMAIL->config->get('spellcheck_languages', - array('da'=>'Dansk', 'de'=>'Deutsch', 'en' => 'English', 'es'=>'Español', - 'fr'=>'Français', 'it'=>'Italiano', 'nl'=>'Nederlands', 'pl'=>'Polski', - 'pt'=>'Português', 'ru'=>'Русский', 'fi'=>'Suomi', 'sv'=>'Svenska')); - - // googie works only with two-letter codes - if ($engine == 'googie') { - $lang = strtolower(substr($_SESSION['language'], 0, 2)); - - $spellcheck_langs_googie = array(); - foreach ($spellcheck_langs as $key => $name) - $spellcheck_langs_googie[strtolower(substr($key,0,2))] = $name; - $spellcheck_langs = $spellcheck_langs_googie; - } - else { - $lang = $_SESSION['language']; + $spellcheck_langs = $engine->languages(); + $lang = $_SESSION['language']; - // if not found in the list, try with two-letter code - if (!$spellcheck_langs[$lang]) - $lang = strtolower(substr($lang, 0, 2)); - } + // if not found in the list, try with two-letter code + if (!$spellcheck_langs[$lang]) + $lang = strtolower(substr($lang, 0, 2)); if (!$spellcheck_langs[$lang]) $lang = 'en'; @@ -1364,8 +1398,9 @@ function rcmail_compose_attachment_list($attrib) if (!$attrib['id']) $attrib['id'] = 'rcmAttachmentList'; - $out = "\n"; + $out = "\n"; $jslist = array(); + $button = ''; if (is_array($COMPOSE['attachments'])) { if ($attrib['deleteicon']) { @@ -1374,27 +1409,38 @@ function rcmail_compose_attachment_list($attrib) 'alt' => rcube_label('delete') )); } - else + else if (rcube_utils::get_boolean($attrib['textbuttons'])) { $button = Q(rcube_label('delete')); + } foreach ($COMPOSE['attachments'] as $id => $a_prop) { if (empty($a_prop)) continue; - $out .= html::tag('li', array('id' => 'rcmfile'.$id, 'class' => rcmail_filetype2classname($a_prop['mimetype'], $a_prop['name'])), + $out .= html::tag('li', + array( + 'id' => 'rcmfile'.$id, + 'class' => rcmail_filetype2classname($a_prop['mimetype'], $a_prop['name']), + 'onmouseover' => "rcube_webmail.long_subject_title_ex(this, 0)", + ), html::a(array( 'href' => "#delete", 'title' => rcube_label('delete'), 'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this)", JS_OBJECT_NAME, $id), - 'class' => 'delete'), - $button) . Q($a_prop['name'])); + 'class' => 'delete' + ), + $button + ) . Q($a_prop['name']) + ); - $jslist['rcmfile'.$id] = array('name' => $a_prop['name'], 'complete' => true, 'mimetype' => $a_prop['mimetype']); + $jslist['rcmfile'.$id] = array('name' => $a_prop['name'], 'complete' => true, 'mimetype' => $a_prop['mimetype']); } } if ($attrib['deleteicon']) $COMPOSE['deleteicon'] = $CONFIG['skin_path'] . $attrib['deleteicon']; + else if (rcube_utils::get_boolean($attrib['textbuttons'])) + $COMPOSE['textbuttons'] = true; if ($attrib['cancelicon']) $OUTPUT->set_env('cancelicon', $CONFIG['skin_path'] . $attrib['cancelicon']); if ($attrib['loadingicon']) diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 018a31b84..70441e0d7 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -1383,9 +1383,6 @@ function rcmail_alter_html_link($matches) { global $RCMAIL; - // Support unicode/punycode in top-level domain part - $EMAIL_PATTERN = '([a-z0-9][a-z0-9\-\.\+\_]*@[^&@"\'.][^@&"\']*\\.([^\\x00-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,}))'; - $tag = strtolower($matches[1]); $attrib = parse_attrib_string($matches[2]); $end = '>'; @@ -1400,12 +1397,36 @@ function rcmail_alter_html_link($matches) $attrib['href'] = $RCMAIL->url(array('task' => 'utils', 'action' => 'modcss', 'u' => $tempurl, 'c' => $GLOBALS['rcmail_html_container_id'])); $end = ' />'; } - else if (preg_match('/^mailto:'.$EMAIL_PATTERN.'(\?[^"\'>]+)?/i', $attrib['href'], $mailto)) { - $attrib['href'] = $mailto[0]; - $attrib['onclick'] = sprintf( - "return %s.command('compose','%s',this)", - JS_OBJECT_NAME, - JQ($mailto[1].$mailto[3])); + else if (preg_match('/^mailto:(.+)/i', $attrib['href'], $mailto)) { + list($mailto, $url) = explode('?', html_entity_decode($mailto[1], ENT_QUOTES, 'UTF-8'), 2); + + $url = urldecode($url); + $mailto = urldecode($mailto); + $addresses = rcube_mime::decode_address_list($mailto, null, true); + $mailto = array(); + + // do sanity checks on recipients + foreach ($addresses as $idx => $addr) { + if (rcube_utils::check_email($addr['mailto'], false)) { + $addresses[$idx] = $addr['mailto']; + $mailto[] = $addr['string']; + } + else { + unset($addresses[$idx]); + } + } + + if (!empty($addresses)) { + $attrib['href'] = 'mailto:' . implode(',', $addresses); + $attrib['onclick'] = sprintf( + "return %s.command('compose','%s',this)", + JS_OBJECT_NAME, + JQ(implode(',', $mailto) . ($url ? "?$url" : ''))); + } + else { + $attrib['href'] = '#NOP'; + $attrib['onclick'] = ''; + } } else if (empty($attrib['href']) && !$attrib['name']) { $attrib['href'] = './#NOP'; @@ -1470,15 +1491,13 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null, $mailto = rcube_idn_to_utf8($mailto); if ($PRINT_MODE) { - $out .= ($out ? ', ' : '') . sprintf('%s <%s>', Q($name), $mailto); - // for printing we display all addresses - continue; + $address = sprintf('%s <%s>', Q($name), Q($mailto)); } else if ($valid) { if ($linked) { $attrs = array( 'href' => 'mailto:' . $mailto, - 'onclick' => sprintf("return %s.command('compose','%s',this)", JS_OBJECT_NAME, JQ($mailto)), + 'onclick' => sprintf("return %s.command('compose','%s',this)", JS_OBJECT_NAME, JQ(format_email_recipient($mailto, $name))), 'class' => "rcmContactAddress", ); @@ -1536,6 +1555,15 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null, } if ($moreadrs) { + if ($PRINT_MODE) { + $out .= ' ' . html::a(array( + 'href' => '#more', + 'class' => 'morelink', + 'onclick' => '$(this).hide().next().show()', + ), Q(rcube_label(array('name' => 'andnmore', 'vars' => array('nr' => $moreadrs))))) . + html::span(array('style' => 'display:none'), join(', ', $allvalues)); + } + else { $out .= ' ' . html::a(array( 'href' => '#more', 'class' => 'morelink', @@ -1545,6 +1573,7 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null, JQ($title)) ), Q(rcube_label(array('name' => 'andnmore', 'vars' => array('nr' => $moreadrs))))); + } } return $out; @@ -1730,7 +1759,7 @@ function rcmail_identity_select($MESSAGE, $identities = null, $compose_mode = 'r $a_to = rcube_mime::decode_address_list($MESSAGE->headers->to, null, true, $MESSAGE->headers->charset); foreach ($a_to as $addr) { if (!empty($addr['mailto'])) { - $a_recipients[] = format_email($addr['mailto']); + $a_recipients[] = strtolower($addr['mailto']); $a_names[] = $addr['name']; } } @@ -1739,7 +1768,7 @@ function rcmail_identity_select($MESSAGE, $identities = null, $compose_mode = 'r $a_cc = rcube_mime::decode_address_list($MESSAGE->headers->cc, null, true, $MESSAGE->headers->charset); foreach ($a_cc as $addr) { if (!empty($addr['mailto'])) { - $a_recipients[] = format_email($addr['mailto']); + $a_recipients[] = strtolower($addr['mailto']); $a_names[] = $addr['name']; } } @@ -1765,7 +1794,7 @@ function rcmail_identity_select($MESSAGE, $identities = null, $compose_mode = 'r break; } // use replied message recipients - else if (($found = array_search($ident['email_ascii'], $a_recipients)) !== false) { + else if (($found = array_search(strtolower($ident['email_ascii']), $a_recipients)) !== false) { if ($found_idx === null) { $found_idx = $idx; } @@ -1784,15 +1813,19 @@ function rcmail_identity_select($MESSAGE, $identities = null, $compose_mode = 'r // Try Return-Path if ($from_idx === null && ($return_path = $MESSAGE->headers->others['return-path'])) { + $return_path = array_map('strtolower', (array) $return_path); + foreach ($identities as $idx => $ident) { // Return-Path header contains an email address, but on some mailing list // it can be e.g. <pear-dev-return-55250-local=domain.tld@lists.php.net> // where local@domain.tld is the address we're looking for (#1489241) - $ident1 = $ident['email_ascii']; + $ident1 = strtolower($ident['email_ascii']); $ident2 = str_replace('@', '=', $ident1); + $ident1 = '<' . $ident1 . '>'; + $ident2 = '-' . $ident2 . '@'; - foreach ((array)$return_path as $path) { - if (stripos($path, $ident1) !== false || stripos($path, $ident2)) { + foreach ($return_path as $path) { + if ($path == $ident1 || stripos($path, $ident2)) { $from_idx = $idx; break 2; } diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc index 1a92844c0..ccb8978be 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -414,9 +414,6 @@ if (!empty($headers['Reply-To'])) { if (!empty($_POST['_followupto'])) { $headers['Mail-Followup-To'] = rcmail_email_input_format(get_input_value('_followupto', RCUBE_INPUT_POST, TRUE, $message_charset)); } -if (!empty($COMPOSE['reply_msgid'])) { - $headers['In-Reply-To'] = $COMPOSE['reply_msgid']; -} // remember reply/forward UIDs in special headers if (!empty($COMPOSE['reply_uid']) && $savedraft) { @@ -426,6 +423,9 @@ else if (!empty($COMPOSE['forward_uid']) && $savedraft) { $headers['X-Draft-Info'] = array('type' => 'forward', 'uid' => $COMPOSE['forward_uid']); } +if (!empty($COMPOSE['reply_msgid'])) { + $headers['In-Reply-To'] = $COMPOSE['reply_msgid']; +} if (!empty($COMPOSE['references'])) { $headers['References'] = $COMPOSE['references']; } @@ -473,12 +473,19 @@ $isHtml = (bool) get_input_value('_is_html', RCUBE_INPUT_POST); $message_body = get_input_value('_message', RCUBE_INPUT_POST, TRUE, $message_charset); if ($isHtml) { - $font = rcube_fontdefs($RCMAIL->config->get('default_font')); - $bstyle = $font && is_string($font) ? " style='font-family: $font'" : ''; + $bstyle = array(); + + if ($font_size = $RCMAIL->config->get('default_font_size')) { + $bstyle[] = 'font-size: ' . $font_size; + } + if ($font_family = $RCMAIL->config->get('default_font')) { + $bstyle[] = 'font-family: ' . rcmail::font_defs($font_family); + } // append doctype and html/body wrappers - $message_body = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">' . - "\r\n<html><body$bstyle>\r\n" . $message_body; + $message_body = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">' + . "\r\n<html><body" . (!empty($bstyle) ? " style='" . implode($bstyle, '; ') . "'" : '') . ">\r\n" + . $message_body; } if (!$savedraft) { @@ -752,7 +759,7 @@ if ($store_target) { if (PEAR::isError($msg)) raise_error(array('code' => 650, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, + 'file' => __FILE__, 'line' => __LINE__, 'message' => "Could not create message: ".$msg->getMessage()), TRUE, FALSE); else { @@ -823,15 +830,24 @@ if ($savedraft) { // start the auto-save timer again $OUTPUT->command('auto_save_start'); - - $OUTPUT->send('iframe'); } else { + $folders = array(); + + if ($COMPOSE['mode'] == 'reply' || $COMPOSE['mode'] == 'forward') + $folders[] = $COMPOSE['mailbox']; + rcmail_compose_cleanup($COMPOSE_ID); if ($store_folder && !$saved) - $OUTPUT->command('sent_successfully', 'error', rcube_label('errorsavingsent')); - else - $OUTPUT->command('sent_successfully', 'confirmation', rcube_label('messagesent'), $store_target); - $OUTPUT->send('iframe'); + $OUTPUT->command('sent_successfully', 'error', rcube_label('errorsavingsent'), $folders); + else { + if ($store_folder) { + $folders[] = $store_target; + } + + $OUTPUT->command('sent_successfully', 'confirmation', rcube_label('messagesent'), $folders); + } } + +$OUTPUT->send('iframe'); diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc index 59f4d55e1..9d85f9c8f 100644 --- a/program/steps/mail/show.inc +++ b/program/steps/mail/show.inc @@ -175,9 +175,9 @@ function rcmail_message_attachments($attrib) $ol .= html::tag('li', null, Q(sprintf("%s (%s)", $filename, $size))); } else { - if (mb_strlen($filename) > 50) { + if ($attrib['maxlength'] && mb_strlen($filename) > $attrib['maxlength']) { $title = $filename; - $filename = abbreviate_string($filename, 50); + $filename = abbreviate_string($filename, $attrib['maxlength']); } else { $title = ''; @@ -190,6 +190,7 @@ function rcmail_message_attachments($attrib) 'href' => $MESSAGE->get_part_url($attach_prop->mime_id, false), 'onclick' => sprintf('return %s.command(\'load-attachment\',\'%s\',this)', JS_OBJECT_NAME, $attach_prop->mime_id), + 'onmouseover' => $title ? '' : 'rcube_webmail.long_subject_title_ex(this, 0)', 'title' => Q($title), ), Q($filename)); $ol .= html::tag('li', array('class' => $class, 'id' => $id), $link); |