From 197203727417a03d87053a47e5aa5175a76e3e0b Mon Sep 17 00:00:00 2001 From: Aleksander Machniak <alec@alec.pl> Date: Thu, 17 Oct 2013 04:24:53 -0400 Subject: [PATCH] Fix vulnerability in handling _session argument of utils/save-prefs (#1489382) --- program/include/main.inc | 597 +++++++++++++++++++++++++++++++++++++++++++++++------------ 1 files changed, 471 insertions(+), 126 deletions(-) diff --git a/program/include/main.inc b/program/include/main.inc index 2a5a660..7a3e1c0 100644 --- a/program/include/main.inc +++ b/program/include/main.inc @@ -97,9 +97,9 @@ * * @see rcmail::text_exists() */ -function rcube_label_exists($name, $domain=null) +function rcube_label_exists($name, $domain=null, &$ref_domain = null) { - return rcmail::get_instance()->text_exists($name, $domain); + return rcmail::get_instance()->text_exists($name, $domain, $ref_domain); } @@ -169,11 +169,19 @@ // get target timestamp $ts = get_offset_time($rcmail->config->get('message_cache_lifetime', '30d'), -1); - $db->query("DELETE FROM ".get_table_name('messages')." - WHERE created < " . $db->fromunixtime($ts)); + if ($rcmail->config->get('messages_cache') || $rcmail->config->get('enable_caching')) { + $db->query("DELETE FROM ".get_table_name('cache_messages') + ." WHERE changed < " . $db->fromunixtime($ts)); - $db->query("DELETE FROM ".get_table_name('cache')." - WHERE created < " . $db->fromunixtime($ts)); + $db->query("DELETE FROM ".get_table_name('cache_index') + ." WHERE changed < " . $db->fromunixtime($ts)); + + $db->query("DELETE FROM ".get_table_name('cache_thread') + ." WHERE changed < " . $db->fromunixtime($ts)); + } + + $db->query("DELETE FROM ".get_table_name('cache') + ." WHERE created < " . $db->fromunixtime($ts)); } @@ -634,20 +642,23 @@ function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL) { $value = NULL; - - if ($source==RCUBE_INPUT_GET && isset($_GET[$fname])) - $value = $_GET[$fname]; - else if ($source==RCUBE_INPUT_POST && isset($_POST[$fname])) - $value = $_POST[$fname]; - else if ($source==RCUBE_INPUT_GPC) - { + + if ($source == RCUBE_INPUT_GET) { + if (isset($_GET[$fname])) + $value = $_GET[$fname]; + } + else if ($source == RCUBE_INPUT_POST) { + if (isset($_POST[$fname])) + $value = $_POST[$fname]; + } + else if ($source == RCUBE_INPUT_GPC) { if (isset($_POST[$fname])) $value = $_POST[$fname]; else if (isset($_GET[$fname])) $value = $_GET[$fname]; else if (isset($_COOKIE[$fname])) $value = $_COOKIE[$fname]; - } + } return parse_input_value($value, $allow_html, $charset); } @@ -655,7 +666,7 @@ /** * Parse/validate input value. See get_input_value() * Performs stripslashes() and charset conversion if necessary - * + * * @param string Input value * @param boolean Allow HTML tags in field value * @param string Charset to convert into @@ -681,15 +692,21 @@ else if (get_magic_quotes_gpc() || get_magic_quotes_runtime()) $value = stripslashes($value); - // remove HTML tags if not allowed + // remove HTML tags if not allowed if (!$allow_html) $value = strip_tags($value); - + + $output_charset = is_object($OUTPUT) ? $OUTPUT->get_charset() : null; + + // remove invalid characters (#1488124) + if ($output_charset == 'UTF-8') + $value = rc_utf8_clean($value); + // convert to internal charset - if (is_object($OUTPUT) && $charset) - return rcube_charset_convert($value, $OUTPUT->get_charset(), $charset); - else - return $value; + if ($charset && $output_charset) + $value = rcube_charset_convert($value, $output_charset, $charset); + + return $value; } /** @@ -699,15 +716,16 @@ * @param int Source to get value from (GPC) * @return array Hash array with all request parameters */ -function request2param($mode = RCUBE_INPUT_GPC) +function request2param($mode = RCUBE_INPUT_GPC, $ignore = 'task|action') { $out = array(); $src = $mode == RCUBE_INPUT_GET ? $_GET : ($mode == RCUBE_INPUT_POST ? $_POST : $_REQUEST); foreach ($src as $key => $value) { $fname = $key[0] == '_' ? substr($key, 1) : $key; - $out[$fname] = get_input_value($key, $mode); + if ($ignore && !preg_match('/^(' . $ignore . ')$/', $fname)) + $out[$fname] = get_input_value($key, $mode); } - + return $out; } @@ -723,12 +741,14 @@ /** * Convert the given string into a valid HTML identifier - * Same functionality as done in app.js with this.identifier_expr - * + * Same functionality as done in app.js with rcube_webmail.html_identifier() */ -function html_identifier($str) +function html_identifier($str, $encode=false) { - return asciiwords($str, true, '_'); + if ($encode) + return rtrim(strtr(base64_encode($str), '+/', '-_'), '='); + else + return asciiwords($str, true, '_'); } /** @@ -765,52 +785,48 @@ * @return string HTML table code */ function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col) - { +{ global $RCMAIL; - + $table = new html_table(/*array('cols' => count($a_show_cols))*/); - + // add table header if (!$attrib['noheader']) foreach ($a_show_cols as $col) $table->add_header($col, Q(rcube_label($col))); - + $c = 0; - if (!is_array($table_data)) + if (!is_array($table_data)) { $db = $RCMAIL->get_dbh(); while ($table_data && ($sql_arr = $db->fetch_assoc($table_data))) { - $zebra_class = $c % 2 ? 'even' : 'odd'; - $table->add_row(array('id' => 'rcmrow' . html_identifier($sql_arr[$id_col]), 'class' => $zebra_class)); + $table->add_row(array('id' => 'rcmrow' . html_identifier($sql_arr[$id_col]))); // format each col foreach ($a_show_cols as $col) $table->add($col, Q($sql_arr[$col])); - + $c++; } } - else - { + else { foreach ($table_data as $row_data) { - $zebra_class = $c % 2 ? 'even' : 'odd'; - if (!empty($row_data['class'])) - $zebra_class .= ' '.$row_data['class']; + $class = !empty($row_data['class']) ? $row_data['class'] : ''; - $table->add_row(array('id' => 'rcmrow' . html_identifier($row_data[$id_col]), 'class' => $zebra_class)); + $table->add_row(array('id' => 'rcmrow' . html_identifier($row_data[$id_col]), 'class' => $class)); // format each col foreach ($a_show_cols as $col) $table->add($col, Q(is_array($row_data[$col]) ? $row_data[$col][0] : $row_data[$col])); - + $c++; } } return $table->show($attrib); - } +} /** @@ -869,23 +885,37 @@ * @param string Container ID to use as prefix * @return string Modified CSS source */ -function rcmail_mod_css_styles($source, $container_id) +function rcmail_mod_css_styles($source, $container_id, $allow_remote=false) { $last_pos = 0; $replacements = new rcube_string_replacer; // ignore the whole block if evil styles are detected - $stripped = preg_replace('/[^a-z\(:;]/', '', rcmail_xss_entity_decode($source)); - if (preg_match('/expression|behavior|url\(|import[^a]/', $stripped)) + $source = rcmail_xss_entity_decode($source); + $stripped = preg_replace('/[^a-z\(:;]/i', '', $source); + $evilexpr = 'expression|behavior|javascript:|import[^a]' . (!$allow_remote ? '|url\(' : ''); + if (preg_match("/$evilexpr/i", $stripped)) return '/* evil! */'; - // remove css comments (sometimes used for some ugly hacks) - $source = preg_replace('!/\*(.+)\*/!Ums', '', $source); - // cut out all contents between { and } - while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos))) - { - $key = $replacements->add(substr($source, $pos+1, $pos2-($pos+1))); + while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos))) { + $styles = substr($source, $pos+1, $pos2-($pos+1)); + + // check every line of a style block... + if ($allow_remote) { + $a_styles = preg_split('/;[\r\n]*/', $styles, -1, PREG_SPLIT_NO_EMPTY); + foreach ($a_styles as $line) { + $stripped = preg_replace('/[^a-z\(:;]/i', '', $line); + // ... and only allow strict url() values + if (stripos($stripped, 'url(') && !preg_match('!url\s*\([ "\'](https?:)//[a-z0-9/._+-]+["\' ]\)!Uims', $line)) { + $a_styles = array('/* evil! */'); + break; + } + } + $styles = join(";\n", $a_styles); + } + + $key = $replacements->add($styles); $source = substr($source, 0, $pos+1) . $replacements->get_replacement($key) . substr($source, $pos2, strlen($source)-$pos2); $last_pos = $pos+2; } @@ -923,7 +953,7 @@ { $out = html_entity_decode(html_entity_decode($content)); $out = preg_replace_callback('/\\\([0-9a-f]{4})/i', 'rcmail_xss_entity_decode_callback', $out); - $out = preg_replace('#/\*.*\*/#Um', '', $out); + $out = preg_replace('#/\*.*\*/#Ums', '', $out); return $out; } @@ -1015,15 +1045,15 @@ * Convert the given date to a human readable form * This uses the date formatting properties from config * - * @param mixed Date representation (string or timestamp) + * @param mixed Date representation (string or timestamp) * @param string Date format to use + * @param bool Enables date convertion according to user timezone + * * @return string Formatted date string */ -function format_date($date, $format=NULL) +function format_date($date, $format=NULL, $convert=true) { - global $CONFIG; - - $ts = NULL; + global $RCMAIL, $CONFIG; if (!empty($date)) $ts = rcube_strtotime($date); @@ -1031,37 +1061,37 @@ if (empty($ts)) return ''; - // get user's timezone - if ($CONFIG['timezone'] === 'auto') - $tz = isset($_SESSION['timezone']) ? $_SESSION['timezone'] : date('Z')/3600; - else { - $tz = $CONFIG['timezone']; - if ($CONFIG['dst_active']) - $tz++; + if ($convert) { + // get user's timezone offset + $tz = $RCMAIL->config->get_timezone(); + + // convert time to user's timezone + $timestamp = $ts - date('Z', $ts) + ($tz * 3600); + + // get current timestamp in user's timezone + $now = time(); // local time + $now -= (int)date('Z'); // make GMT time + $now += ($tz * 3600); // user's time } - - // convert time to user's timezone - $timestamp = $ts - date('Z', $ts) + ($tz * 3600); - - // get current timestamp in user's timezone - $now = time(); // local time - $now -= (int)date('Z'); // make GMT time - $now += ($tz * 3600); // user's time - $now_date = getdate($now); - - $today_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday'], $now_date['year']); - $week_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday']-6, $now_date['year']); + else { + $now = time(); + $timestamp = $ts; + } // define date format depending on current time if (!$format) { + $now_date = getdate($now); + $today_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday'], $now_date['year']); + $week_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday']-6, $now_date['year']); + if ($CONFIG['prettydate'] && $timestamp > $today_limit && $timestamp < $now) { - $format = $CONFIG['date_today'] ? $CONFIG['date_today'] : 'H:i'; + $format = $RCMAIL->config->get('date_today', $RCMAIL->config->get('time_format', 'H:i')); $today = true; } else if ($CONFIG['prettydate'] && $timestamp > $week_limit && $timestamp < $now) - $format = $CONFIG['date_short'] ? $CONFIG['date_short'] : 'D H:i'; + $format = $RCMAIL->config->get('date_short', 'D H:i'); else - $format = $CONFIG['date_long'] ? $CONFIG['date_long'] : 'd.m.Y H:i'; + $format = $RCMAIL->config->get('date_long', 'Y-m-d H:i'); } // strftime() format @@ -1213,9 +1243,9 @@ $p['folder_name'] = '*'; if ($p['unsubscribed']) - $list = $RCMAIL->imap->list_unsubscribed('', $p['folder_name'], $p['folder_filter']); + $list = $RCMAIL->imap->list_unsubscribed('', $p['folder_name'], $p['folder_filter'], $p['folder_rights']); else - $list = $RCMAIL->imap->list_mailboxes('', $p['folder_name'], $p['folder_filter']); + $list = $RCMAIL->imap->list_mailboxes('', $p['folder_name'], $p['folder_filter'], $p['folder_rights']); $delimiter = $RCMAIL->imap->get_hierarchy_delimiter(); @@ -1229,7 +1259,7 @@ if ($p['noselection']) $select->add($p['noselection'], ''); - rcmail_render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames']); + rcmail_render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames'], 0, $p); return $select; } @@ -1278,11 +1308,6 @@ $path .= $prefix.$currentFolder; if (!isset($arrFolders[$currentFolder])) { - // Check \Noselect option (if options are in cache) - if (!$virtual && ($opts = $RCMAIL->imap->mailbox_options($path))) { - $virtual = in_array('\\Noselect', $opts); - } - $arrFolders[$currentFolder] = array( 'id' => $path, 'name' => rcube_charset_convert($currentFolder, 'UTF7-IMAP'), @@ -1310,13 +1335,14 @@ $realnames = (bool)$attrib['realnames']; $msgcounts = $RCMAIL->imap->get_cache('messagecount'); - $idx = 0; $out = ''; foreach ($arrFolders as $key => $folder) { - $zebra_class = (($nestLevel+1)*$idx) % 2 == 0 ? 'even' : 'odd'; - $title = null; + $title = null; + $folder_class = rcmail_folder_classname($folder['id']); + $collapsed = strpos($CONFIG['collapsed_folders'], '&'.rawurlencode($folder['id']).'&') !== false; + $unread = $msgcounts ? intval($msgcounts[$folder['id']]['UNSEEN']) : 0; - if (($folder_class = rcmail_folder_classname($folder['id'])) && !$realnames) { + if ($folder_class && !$realnames) { $foldername = rcube_label($folder_class); } else { @@ -1332,30 +1358,15 @@ } // make folder name safe for ids and class names - $folder_id = html_identifier($folder['id']); + $folder_id = html_identifier($folder['id'], true); $classes = array('mailbox'); // set special class for Sent, Drafts, Trash and Junk - if ($folder['id'] == $CONFIG['sent_mbox']) - $classes[] = 'sent'; - else if ($folder['id'] == $CONFIG['drafts_mbox']) - $classes[] = 'drafts'; - else if ($folder['id'] == $CONFIG['trash_mbox']) - $classes[] = 'trash'; - else if ($folder['id'] == $CONFIG['junk_mbox']) - $classes[] = 'junk'; - else if ($folder['id'] == 'INBOX') - $classes[] = 'inbox'; - else - $classes[] = '_'.asciiwords($folder_class ? $folder_class : strtolower($folder['id']), true); - - $classes[] = $zebra_class; + if ($folder_class) + $classes[] = $folder_class; if ($folder['id'] == $mbox_name) $classes[] = 'selected'; - - $collapsed = strpos($CONFIG['collapsed_folders'], '&'.rawurlencode($folder['id']).'&') !== false; - $unread = $msgcounts ? intval($msgcounts[$folder['id']]['UNSEEN']) : 0; if ($folder['virtual']) $classes[] = 'virtual'; @@ -1390,7 +1401,6 @@ } $out .= "</li>\n"; - $idx++; } return $out; @@ -1402,11 +1412,25 @@ * @access private * @return string */ -function rcmail_render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, &$select, $realnames=false, $nestLevel=0) +function rcmail_render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, &$select, $realnames=false, $nestLevel=0, $opts=array()) { + global $RCMAIL; + $out = ''; - foreach ($arrFolders as $key=>$folder) { + foreach ($arrFolders as $key => $folder) { + // skip exceptions (and its subfolders) + if (!empty($opts['exceptions']) && in_array($folder['id'], $opts['exceptions'])) { + continue; + } + + // skip folders in which it isn't possible to create subfolders + if (!empty($opts['skip_noinferiors']) && ($attrs = $RCMAIL->imap->mailbox_attributes($folder['id'])) + && in_array('\\Noinferiors', $attrs) + ) { + continue; + } + if (!$realnames && ($folder_class = rcmail_folder_classname($folder['id']))) $foldername = rcube_label($folder_class); else { @@ -1420,7 +1444,8 @@ $select->add(str_repeat(' ', $nestLevel*4) . $foldername, $folder['id']); if (!empty($folder['folders'])) - $out .= rcmail_render_folder_tree_select($folder['folders'], $mbox_name, $maxlength, $select, $realnames, $nestLevel+1); + $out .= rcmail_render_folder_tree_select($folder['folders'], $mbox_name, $maxlength, + $select, $realnames, $nestLevel+1, $opts); } return $out; @@ -1461,6 +1486,31 @@ return rcube_label($folder_class); else return rcube_charset_convert($name, 'UTF7-IMAP'); +} + + +function rcmail_localize_folderpath($path) +{ + global $RCMAIL; + + $protect_folders = $RCMAIL->config->get('protect_default_folders'); + $default_folders = (array) $RCMAIL->config->get('default_imap_folders'); + $delimiter = $RCMAIL->imap->get_hierarchy_delimiter(); + $path = explode($delimiter, $path); + $result = array(); + + foreach ($path as $idx => $dir) { + $directory = implode($delimiter, array_slice($path, 0, $idx+1)); + if ($protect_folders && in_array($directory, $default_folders)) { + unset($result); + $result[] = rcmail_localize_foldername($directory); + } + else { + $result[] = rcube_charset_convert($dir, 'UTF7-IMAP'); + } + } + + return implode($delimiter, $result); } @@ -1544,7 +1594,11 @@ $RCMAIL->output->show_message('errorreadonly', 'error'); } else if ($err_code && ($err_str = $RCMAIL->imap->get_error_str())) { - $RCMAIL->output->show_message('servererrormsg', 'error', array('msg' => $err_str)); + // try to detect access rights problem and display appropriate message + if (stripos($err_str, 'Permission denied') !== false) + $RCMAIL->output->show_message('errornoperm', 'error'); + else + $RCMAIL->output->show_message('servererrormsg', 'error', array('msg' => $err_str)); } else if ($fallback) { $RCMAIL->output->show_message($fallback, 'error', $fallback_args); @@ -1567,7 +1621,7 @@ $hook = $RCMAIL->plugins->exec_hook('html_editor', array('mode' => $mode)); if ($hook['abort']) - return; + return; $lang = strtolower($_SESSION['language']); @@ -1579,9 +1633,14 @@ $RCMAIL->output->include_script('tiny_mce/tiny_mce.js'); $RCMAIL->output->include_script('editor.js'); - $RCMAIL->output->add_script(sprintf("rcmail_editor_init('\$__skin_path', '%s', %d, '%s');", - JQ($lang), intval($CONFIG['enable_spellcheck']), $mode), - 'foot'); + $RCMAIL->output->add_script(sprintf("rcmail_editor_init(%s)", + json_encode(array( + 'mode' => $mode, + 'skin_path' => '$__skin_path', + 'lang' => $lang, + 'spellcheck' => intval($CONFIG['enable_spellcheck']), + 'spelldict' => intval($CONFIG['spellcheck_dictionary']), + ))), 'foot'); } @@ -1619,6 +1678,192 @@ } return preg_replace($search, $replace, $html); +} + + +/** + * Send the given message using the configured method + * + * @param object $message Reference to Mail_MIME object + * @param string $from Sender address string + * @param array $mailto Array of recipient address strings + * @param array $smtp_error SMTP error array (reference) + * @param string $body_file Location of file with saved message body (reference), + * used when delay_file_io is enabled + * @param array $smtp_opts SMTP options (e.g. DSN request) + * + * @return boolean Send status. + */ +function rcmail_deliver_message(&$message, $from, $mailto, &$smtp_error, &$body_file=null, $smtp_opts=null) +{ + global $CONFIG, $RCMAIL; + + $headers = $message->headers(); + + // send thru SMTP server using custom SMTP library + if ($CONFIG['smtp_server']) { + // generate list of recipients + $a_recipients = array($mailto); + + if (strlen($headers['Cc'])) + $a_recipients[] = $headers['Cc']; + if (strlen($headers['Bcc'])) + $a_recipients[] = $headers['Bcc']; + + // clean Bcc from header for recipients + $send_headers = $headers; + unset($send_headers['Bcc']); + // here too, it because txtHeaders() below use $message->_headers not only $send_headers + unset($message->_headers['Bcc']); + + $smtp_headers = $message->txtHeaders($send_headers, true); + + if ($message->getParam('delay_file_io')) { + // use common temp dir + $temp_dir = $RCMAIL->config->get('temp_dir'); + $body_file = tempnam($temp_dir, 'rcmMsg'); + if (PEAR::isError($mime_result = $message->saveMessageBody($body_file))) { + raise_error(array('code' => 650, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Could not create message: ".$mime_result->getMessage()), + TRUE, FALSE); + return false; + } + $msg_body = fopen($body_file, 'r'); + } else { + $msg_body = $message->get(); + } + + // send message + if (!is_object($RCMAIL->smtp)) + $RCMAIL->smtp_init(true); + + $sent = $RCMAIL->smtp->send_mail($from, $a_recipients, $smtp_headers, $msg_body, $smtp_opts); + $smtp_response = $RCMAIL->smtp->get_response(); + $smtp_error = $RCMAIL->smtp->get_error(); + + // log error + if (!$sent) + raise_error(array('code' => 800, 'type' => 'smtp', 'line' => __LINE__, 'file' => __FILE__, + 'message' => "SMTP error: ".join("\n", $smtp_response)), TRUE, FALSE); + } + // send mail using PHP's mail() function + else { + // unset some headers because they will be added by the mail() function + $headers_enc = $message->headers($headers); + $headers_php = $message->_headers; + unset($headers_php['To'], $headers_php['Subject']); + + // reset stored headers and overwrite + $message->_headers = array(); + $header_str = $message->txtHeaders($headers_php); + + // #1485779 + if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { + if (preg_match_all('/<([^@]+@[^>]+)>/', $headers_enc['To'], $m)) { + $headers_enc['To'] = implode(', ', $m[1]); + } + } + + $msg_body = $message->get(); + + if (PEAR::isError($msg_body)) + raise_error(array('code' => 650, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Could not create message: ".$msg_body->getMessage()), + TRUE, FALSE); + else { + $delim = $RCMAIL->config->header_delimiter(); + $to = $headers_enc['To']; + $subject = $headers_enc['Subject']; + $header_str = rtrim($header_str); + + if ($delim != "\r\n") { + $header_str = str_replace("\r\n", $delim, $header_str); + $msg_body = str_replace("\r\n", $delim, $msg_body); + $to = str_replace("\r\n", $delim, $to); + $subject = str_replace("\r\n", $delim, $subject); + } + + if (ini_get('safe_mode')) + $sent = mail($to, $subject, $msg_body, $header_str); + else + $sent = mail($to, $subject, $msg_body, $header_str, "-f$from"); + } + } + + if ($sent) { + $RCMAIL->plugins->exec_hook('message_sent', array('headers' => $headers, 'body' => $msg_body)); + + // remove MDN headers after sending + unset($headers['Return-Receipt-To'], $headers['Disposition-Notification-To']); + + // get all recipients + if ($headers['Cc']) + $mailto .= $headers['Cc']; + if ($headers['Bcc']) + $mailto .= $headers['Bcc']; + if (preg_match_all('/<([^@]+@[^>]+)>/', $mailto, $m)) + $mailto = implode(', ', array_unique($m[1])); + + if ($CONFIG['smtp_log']) { + write_log('sendmail', sprintf("User %s [%s]; Message for %s; %s", + $RCMAIL->user->get_username(), + $_SERVER['REMOTE_ADDR'], + $mailto, + !empty($smtp_response) ? join('; ', $smtp_response) : '')); + } + } + + if (is_resource($msg_body)) { + fclose($msg_body); + } + + $message->_headers = array(); + $message->headers($headers); + + return $sent; +} + + +// Returns unique Message-ID +function rcmail_gen_message_id() +{ + global $RCMAIL; + + $local_part = md5(uniqid('rcmail'.mt_rand(),true)); + $domain_part = $RCMAIL->user->get_username('domain'); + + // Try to find FQDN, some spamfilters doesn't like 'localhost' (#1486924) + if (!preg_match('/\.[a-z]+$/i', $domain_part)) { + if (($host = preg_replace('/:[0-9]+$/', '', $_SERVER['HTTP_HOST'])) + && preg_match('/\.[a-z]+$/i', $host)) { + $domain_part = $host; + } + else if (($host = preg_replace('/:[0-9]+$/', '', $_SERVER['SERVER_NAME'])) + && preg_match('/\.[a-z]+$/i', $host)) { + $domain_part = $host; + } + } + + return sprintf('<%s@%s>', $local_part, $domain_part); +} + + +// Returns RFC2822 formatted current date in user's timezone +function rcmail_user_date() +{ + global $RCMAIL, $CONFIG; + + // get user's timezone + $tz = $RCMAIL->config->get_timezone(); + + $date = time() + $tz * 60 * 60; + $date = gmdate('r', $date); + $tz = sprintf('%+05d', intval($tz) * 100 + ($tz - intval($tz)) * 60); + $date = preg_replace('/[+-][0-9]{4}$/', $tz, $date); + + return $date; } @@ -1816,7 +2061,68 @@ public function callback($matches) { - return $matches[1] . '="' . make_absolute_url($matches[3], $this->base_url) . '"'; + return $matches[1] . '="' . self::absolute_url($matches[3], $this->base_url) . '"'; + } + + public function replace($body) + { + return preg_replace_callback(array( + '/(src|background|href)=(["\']?)([^"\'\s]+)(\2|\s|>)/Ui', + '/(url\s*\()(["\']?)([^"\'\)\s]+)(\2)\)/Ui', + ), + array($this, 'callback'), $body); + } + + /** + * Convert paths like ../xxx to an absolute path using a base url + * + * @param string $path Relative path + * @param string $base_url Base URL + * + * @return string Absolute URL + */ + public static function absolute_url($path, $base_url) + { + $host_url = $base_url; + $abs_path = $path; + + // check if path is an absolute URL + if (preg_match('/^[fhtps]+:\/\//', $path)) { + return $path; + } + + // check if path is a content-id scheme + if (strpos($path, 'cid:') === 0) { + return $path; + } + + // cut base_url to the last directory + if (strrpos($base_url, '/') > 7) { + $host_url = substr($base_url, 0, strpos($base_url, '/', 7)); + $base_url = substr($base_url, 0, strrpos($base_url, '/')); + } + + // $path is absolute + if ($path[0] == '/') { + $abs_path = $host_url.$path; + } + else { + // strip './' because its the same as '' + $path = preg_replace('/^\.\//', '', $path); + + if (preg_match_all('/\.\.\//', $path, $matches, PREG_SET_ORDER)) { + foreach ($matches as $a_match) { + if (strrpos($base_url, '/')) { + $base_url = substr($base_url, 0, strrpos($base_url, '/')); + } + $path = substr($path, 3); + } + } + + $abs_path = $base_url.'/'.$path; + } + + return $abs_path; } } @@ -1921,8 +2227,8 @@ if (!$RCMAIL->config->get('log_logins') || !$RCMAIL->user) return; - write_log('userlogins', sprintf('Successful login for %s (ID: %d) from %s', - $RCMAIL->user->get_username(), $RCMAIL->user->ID, rcmail_remote_ip())); + write_log('userlogins', sprintf('Successful login for %s (ID: %d) from %s in session %s', + $RCMAIL->user->get_username(), $RCMAIL->user->ID, rcmail_remote_ip(), session_id())); } @@ -2094,7 +2400,7 @@ $status = apc_fetch($prefix . $params['name']); if (!empty($status)) { - $status['percent'] = $status['current']/$status['total']*100; + $status['percent'] = round($status['current']/$status['total']*100); $params = array_merge($status, $params); } } @@ -2105,12 +2411,12 @@ 'current' => show_bytes($params['current']), 'total' => show_bytes($params['total']) ))); -console($params); + $RCMAIL->output->command('upload_progress_update', $params); $RCMAIL->output->send(); } -function rcube_upload_progress_init() +function rcube_upload_init() { global $RCMAIL; @@ -2121,4 +2427,43 @@ $RCMAIL->output->set_env('upload_progress_time', (int) $seconds); } } + + // find max filesize value + $max_filesize = parse_bytes(ini_get('upload_max_filesize')); + $max_postsize = parse_bytes(ini_get('post_max_size')); + if ($max_postsize && $max_postsize < $max_filesize) + $max_filesize = $max_postsize; + + $RCMAIL->output->set_env('max_filesize', $max_filesize); + $max_filesize = show_bytes($max_filesize); + $RCMAIL->output->set_env('filesizeerror', rcube_label(array( + 'name' => 'filesizeerror', 'vars' => array('size' => $max_filesize)))); + + return $max_filesize; +} + +/** + * Initializes client-side autocompletion + */ +function rcube_autocomplete_init() +{ + global $RCMAIL; + static $init; + + if ($init) + return; + + $init = 1; + + if (($threads = (int)$RCMAIL->config->get('autocomplete_threads')) > 0) { + $book_types = (array) $RCMAIL->config->get('autocomplete_addressbooks', 'sql'); + if (count($book_types) > 1) { + $RCMAIL->output->set_env('autocomplete_threads', $threads); + $RCMAIL->output->set_env('autocomplete_sources', $book_types); + } + } + + $RCMAIL->output->set_env('autocomplete_max', (int)$RCMAIL->config->get('autocomplete_max', 15)); + $RCMAIL->output->set_env('autocomplete_min_length', $RCMAIL->config->get('autocomplete_min_length')); + $RCMAIL->output->add_label('autocompletechars', 'autocompletemore'); } -- Gitblit v1.9.1