Improved mailto: link arguments handling (#1489363)
| | |
| | | CHANGELOG Roundcube Webmail |
| | | =========================== |
| | | |
| | | - Improved mailto: link arguments handling (#1489363) |
| | | - Use DOMDocument LIBXML_PARSEHUGE and LIBXML_COMPACT options if possible (#1489302) |
| | | - Support HTTP_HOST, SERVER_NAME and SERVER_ADDR values in include_host_config feature |
| | | - Hide Delivery Status Notification option when smtp_server is unset (#1489336) |
| | |
| | | * |
| | | * @param int $mode Source to get value from (GPC) |
| | | * @param string $ignore PCRE expression to skip parameters by name |
| | | * @param boolean $allow_html Allow HTML tags in field value |
| | | * |
| | | * @return array Hash array with all request parameters |
| | | */ |
| | | public static function request2param($mode = null, $ignore = 'task|action') |
| | | public static function request2param($mode = null, $ignore = 'task|action', $allow_html = false) |
| | | { |
| | | $out = array(); |
| | | $src = $mode == self::INPUT_GET ? $_GET : ($mode == self::INPUT_POST ? $_POST : $_REQUEST); |
| | |
| | | foreach (array_keys($src) as $key) { |
| | | $fname = $key[0] == '_' ? substr($key, 1) : $key; |
| | | if ($ignore && !preg_match('/^(' . $ignore . ')$/', $fname)) { |
| | | $out[$fname] = self::get_input_value($key, $mode); |
| | | $out[$fname] = self::get_input_value($key, $mode, $allow_html); |
| | | } |
| | | } |
| | | |
| | |
| | | $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'])) { |
| | |
| | | } |
| | | 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(); |
| | |
| | | |
| | | /****** 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; |
| | |
| | | { |
| | | 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 = '>'; |
| | |
| | | $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]; |
| | | 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($mailto[1].$mailto[3])); |
| | | JQ(implode(',', $mailto) . ($url ? "?$url" : ''))); |
| | | } |
| | | else { |
| | | $attrib['href'] = '#NOP'; |
| | | $attrib['onclick'] = ''; |
| | | } |
| | | } |
| | | else if (empty($attrib['href']) && !$attrib['name']) { |
| | | $attrib['href'] = './#NOP'; |
| | |
| | | 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", |
| | | ); |
| | | |
| | |
| | | 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) { |
| | |
| | | $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']; |
| | | } |