From 7786ba1adb415fc8fd4478380d7201702a799483 Mon Sep 17 00:00:00 2001 From: alecpl <alec@alec.pl> Date: Fri, 20 May 2011 04:00:40 -0400 Subject: [PATCH] - Fix error when rcube_cache::remove() was used in pattern mode --- program/include/main.inc | 664 ++++++++++++++++++++++++++++++++---------------------- 1 files changed, 391 insertions(+), 273 deletions(-) diff --git a/program/include/main.inc b/program/include/main.inc index 0815c25..44a43c6 100644 --- a/program/include/main.inc +++ b/program/include/main.inc @@ -5,7 +5,7 @@ | program/include/main.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2009, The Roundcube Dev Team | + | Copyright (C) 2005-2011, The Roundcube Dev Team | | Licensed under the GNU GPL | | | | PURPOSE: | @@ -26,11 +26,8 @@ * @author Thomas Bruederli <roundcube@gmail.com> */ -require_once('lib/utf7.inc'); -require_once('include/rcube_shared.inc'); - -// fallback if not PHP modules are available -@include_once('lib/utf8.class.php'); +require_once 'utf7.inc'; +require_once INSTALL_PATH . 'program/include/rcube_shared.inc'; // define constannts for input reading define('RCUBE_INPUT_GET', 0x0101); @@ -84,12 +81,25 @@ * It's a global wrapper for rcmail::gettext() * * @param mixed Named parameters array or label name + * @param string Domain to search in (e.g. plugin name) * @return string Localized text * @see rcmail::gettext() */ function rcube_label($p, $domain=null) { return rcmail::get_instance()->gettext($p, $domain); +} + + +/** + * Global wrapper of rcmail::text_exists() + * to check whether a text label is defined + * + * @see rcmail::text_exists() + */ +function rcube_label_exists($name, $domain=null) +{ + return rcmail::get_instance()->text_exists($name, $domain); } @@ -277,7 +287,7 @@ } $error = true; } - + // encode string for output if ($from == 'UTF-8') { // @TODO: we need a function for UTF-7 (RFC2152) conversion @@ -299,7 +309,7 @@ } $error = true; } - + // report error if ($error && !$convert_warning) { raise_error(array( @@ -309,10 +319,10 @@ 'line' => __LINE__, 'message' => "Could not convert string from $from to $to. Make sure iconv/mbstring is installed or lib/utf8.class is available." ), true, false); - + $convert_warning = true; } - + // return UTF-8 or original string return $str; } @@ -728,6 +738,16 @@ } /** + * Convert the given string into a valid HTML identifier + * Same functionality as done in app.js with this.identifier_expr + * + */ +function html_identifier($str) +{ + return asciiwords($str, true, '_'); +} + +/** * Remove single and double quotes from given string * * @param string Input value @@ -778,7 +798,7 @@ while ($table_data && ($sql_arr = $db->fetch_assoc($table_data))) { $zebra_class = $c % 2 ? 'even' : 'odd'; - $table->add_row(array('id' => 'rcmrow' . $sql_arr[$id_col], 'class' => $zebra_class)); + $table->add_row(array('id' => 'rcmrow' . html_identifier($sql_arr[$id_col]), 'class' => $zebra_class)); // format each col foreach ($a_show_cols as $col) @@ -795,7 +815,7 @@ if (!empty($row_data['class'])) $zebra_class .= ' '.$row_data['class']; - $table->add_row(array('id' => 'rcmrow' . $row_data[$id_col], 'class' => $zebra_class)); + $table->add_row(array('id' => 'rcmrow' . html_identifier($row_data[$id_col]), 'class' => $zebra_class)); // format each col foreach ($a_show_cols as $col) @@ -872,8 +892,8 @@ $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/', $stripped)) + $stripped = preg_replace('/[^a-z\(:;]/', '', rcmail_xss_entity_decode($source)); + if (preg_match('/expression|behavior|url\(|import[^a]/', $stripped)) return '/* evil! */'; // remove css comments (sometimes used for some ugly hacks) @@ -978,6 +998,37 @@ /** + * Improved equivalent to strtotime() + * + * @param string Date string + * @return int + */ +function rcube_strtotime($date) +{ + // check for MS Outlook vCard date format YYYYMMDD + if (preg_match('/^([12][90]\d\d)([01]\d)(\d\d)$/', trim($date), $matches)) { + return mktime(0,0,0, intval($matches[2]), intval($matches[3]), intval($matches[1])); + } + else if (is_numeric($date)) + return $date; + + // support non-standard "GMTXXXX" literal + $date = preg_replace('/GMT\s*([+-][0-9]+)/', '\\1', $date); + + // if date parsing fails, we have a date in non-rfc format. + // remove token from the end and try again + while ((($ts = @strtotime($date)) === false) || ($ts < 0)) { + $d = explode(' ', $date); + array_pop($d); + if (!$d) break; + $date = implode(' ', $d); + } + + return $ts; +} + + +/** * Convert the given date to a human readable form * This uses the date formatting properties from config * @@ -986,31 +1037,17 @@ * @return string Formatted date string */ function format_date($date, $format=NULL) - { +{ global $CONFIG; $ts = NULL; - if (is_numeric($date)) - $ts = $date; - else if (!empty($date)) - { - // support non-standard "GMTXXXX" literal - $date = preg_replace('/GMT\s*([+-][0-9]+)/', '\\1', $date); - // if date parsing fails, we have a date in non-rfc format. - // remove token from the end and try again - while ((($ts = @strtotime($date))===false) || ($ts < 0)) - { - $d = explode(' ', $date); - array_pop($d); - if (!$d) break; - $date = implode(' ', $d); - } - } + if (!empty($date)) + $ts = rcube_strtotime($date); if (empty($ts)) return ''; - + // get user's timezone if ($CONFIG['timezone'] === 'auto') $tz = isset($_SESSION['timezone']) ? $_SESSION['timezone'] : date('Z')/3600; @@ -1022,7 +1059,7 @@ // 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 @@ -1030,30 +1067,33 @@ $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']); + $week_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday']-6, $now_date['year']); // define date format depending on current time if (!$format) { - if ($CONFIG['prettydate'] && $timestamp > $today_limit && $timestamp < $now) - return sprintf('%s %s', rcube_label('today'), date($CONFIG['date_today'] ? $CONFIG['date_today'] : 'H:i', $timestamp)); + if ($CONFIG['prettydate'] && $timestamp > $today_limit && $timestamp < $now) { + $format = $CONFIG['date_today'] ? $CONFIG['date_today'] : 'H:i'; + $today = true; + } else if ($CONFIG['prettydate'] && $timestamp > $week_limit && $timestamp < $now) $format = $CONFIG['date_short'] ? $CONFIG['date_short'] : 'D H:i'; else $format = $CONFIG['date_long'] ? $CONFIG['date_long'] : 'd.m.Y H:i'; - } + } // strftime() format - if (preg_match('/%[a-z]+/i', $format)) - return strftime($format, $timestamp); + if (preg_match('/%[a-z]+/i', $format)) { + $format = strftime($format, $timestamp); + return $today ? (rcube_label('today') . ' ' . $format) : $format; + } // parse format string manually in order to provide localized weekday and month names // an alternative would be to convert the date() format string to fit with strftime() $out = ''; - for($i=0; $i<strlen($format); $i++) - { + for($i=0; $i<strlen($format); $i++) { if ($format{$i}=='\\') // skip escape chars continue; - + // write char "as-is" if ($format{$i}==' ' || $format{$i-1}=='\\') $out .= $format{$i}; @@ -1073,10 +1113,21 @@ $out .= strftime('%x %X', $timestamp); else $out .= date($format{$i}, $timestamp); - } - - return $out; } + + if ($today) { + $label = rcube_label('today'); + // replcae $ character with "Today" label (#1486120) + if (strpos($out, '$') !== false) { + $out = preg_replace('/\$/', $label, $out, 1); + } + else { + $out = $label . ' ' . $out; + } + } + + return $out; +} /** @@ -1087,179 +1138,13 @@ * @return string Formatted string */ function format_email_recipient($email, $name='') - { - if ($name && $name != $email) - { +{ + if ($name && $name != $email) { // Special chars as defined by RFC 822 need to in quoted string (or escaped). return sprintf('%s <%s>', preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $name) ? '"'.addcslashes($name, '"').'"' : $name, trim($email)); - } - else - return trim($email); } - - -/****** debugging functions ********/ - - -/** - * Print or write debug messages - * - * @param mixed Debug message or data - * @return void - */ -function console() - { - $args = func_get_args(); - - if (class_exists('rcmail', false)) { - $rcmail = rcmail::get_instance(); - if (is_object($rcmail->plugins)) - $rcmail->plugins->exec_hook('console', $args); - } - - $msg = array(); - foreach ($args as $arg) - $msg[] = !is_string($arg) ? var_export($arg, true) : $arg; - - if (!($GLOBALS['CONFIG']['debug_level'] & 4)) - write_log('console', join(";\n", $msg)); - else if ($GLOBALS['OUTPUT']->ajax_call) - print "/*\n " . join(";\n", $msg) . " \n*/\n"; - else - { - print '<div style="background:#eee; border:1px solid #ccc; margin-bottom:3px; padding:6px"><pre>'; - print join(";<br/>\n", $msg); - print "</pre></div>\n"; - } - } - - -/** - * Append a line to a logfile in the logs directory. - * Date will be added automatically to the line. - * - * @param $name name of log file - * @param line Line to append - * @return void - */ -function write_log($name, $line) - { - global $CONFIG, $RCMAIL; - - if (!is_string($line)) - $line = var_export($line, true); - - if (empty($CONFIG['log_date_format'])) - $CONFIG['log_date_format'] = 'd-M-Y H:i:s O'; - - $date = date($CONFIG['log_date_format']); - - // trigger logging hook - if (is_object($RCMAIL) && is_object($RCMAIL->plugins)) { - $log = $RCMAIL->plugins->exec_hook('write_log', array('name' => $name, 'date' => $date, 'line' => $line)); - $name = $log['name']; - $line = $log['line']; - $date = $log['date']; - if ($log['abort']) - return true; - } - - if ($CONFIG['log_driver'] == 'syslog') { - $prio = $name == 'errors' ? LOG_ERR : LOG_INFO; - syslog($prio, $line); - return true; - } - else { - $line = sprintf("[%s]: %s\n", $date, $line); - - // log_driver == 'file' is assumed here - if (empty($CONFIG['log_dir'])) - $CONFIG['log_dir'] = INSTALL_PATH.'logs'; - - // try to open specific log file for writing - $logfile = $CONFIG['log_dir'].'/'.$name; - if ($fp = @fopen($logfile, 'a')) { - fwrite($fp, $line); - fflush($fp); - fclose($fp); - return true; - } - else - trigger_error("Error writing to log file $logfile; Please check permissions", E_USER_WARNING); - } - return false; -} - - -/** - * Write login data (name, ID, IP address) to the 'userlogins' log file. - * - * @return void - */ -function rcmail_log_login() -{ - global $RCMAIL; - - 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())); -} - - -/** - * Returns remote IP address and forwarded addresses if found - * - * @return string Remote IP address(es) - */ -function rcmail_remote_ip() -{ - $address = $_SERVER['REMOTE_ADDR']; - - // append the NGINX X-Real-IP header, if set - if (!empty($_SERVER['HTTP_X_REAL_IP'])) { - $remote_ip[] = 'X-Real-IP: ' . $_SERVER['HTTP_X_REAL_IP']; - } - // append the X-Forwarded-For header, if set - if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { - $remote_ip[] = 'X-Forwarded-For: ' . $_SERVER['HTTP_X_FORWARDED_FOR']; - } - - if (!empty($remote_ip)) - $address .= '(' . implode(',', $remote_ip) . ')'; - - return $address; -} - - -/** - * @access private - * @return mixed - */ -function rcube_timer() -{ - return microtime(true); -} - - -/** - * @access private - * @return void - */ -function rcube_print_time($timer, $label='Timer', $dest='console') -{ - static $print_count = 0; - - $print_count++; - $now = rcube_timer(); - $diff = $now-$timer; - - if (empty($label)) - $label = 'Timer '.$print_count; - - write_log($dest, sprintf("%s: %0.4f sec", $label, $diff)); + return trim($email); } @@ -1273,25 +1158,28 @@ { global $RCMAIL; static $a_mailboxes; - + $attrib += array('maxlength' => 100, 'realnames' => false); // add some labels to client $RCMAIL->output->add_label('purgefolderconfirm', 'deletemessagesconfirm'); - + $type = $attrib['type'] ? $attrib['type'] : 'ul'; unset($attrib['type']); if ($type=='ul' && !$attrib['id']) $attrib['id'] = 'rcmboxlist'; + if (empty($attrib['folder_name'])) + $attrib['folder_name'] = '*'; + // get mailbox list $mbox_name = $RCMAIL->imap->get_mailbox_name(); - + // build the folders tree if (empty($a_mailboxes)) { // get mailbox list - $a_folders = $RCMAIL->imap->list_mailboxes(); + $a_folders = $RCMAIL->imap->list_mailboxes('', $attrib['folder_name'], $attrib['folder_filter']); $delimiter = $RCMAIL->imap->get_hierarchy_delimiter(); $a_mailboxes = array(); @@ -1302,20 +1190,20 @@ // allow plugins to alter the folder tree or to localize folder names $hook = $RCMAIL->plugins->exec_hook('render_mailboxlist', array('list' => $a_mailboxes, 'delimiter' => $delimiter)); - if ($type=='select') { + if ($type == 'select') { $select = new html_select($attrib); - + // add no-selection option if ($attrib['noselection']) - $select->add(rcube_label($attrib['noselection']), '0'); - + $select->add(rcube_label($attrib['noselection']), ''); + rcmail_render_folder_tree_select($hook['list'], $mbox_name, $attrib['maxlength'], $select, $attrib['realnames']); $out = $select->show(); } else { $js_mailboxlist = array(); $out = html::tag('ul', $attrib, rcmail_render_folder_tree_html($hook['list'], $mbox_name, $js_mailboxlist, $attrib), html::$common_attrib); - + $RCMAIL->output->add_gui_object('mailboxlist', $attrib['id']); $RCMAIL->output->set_env('mailboxes', $js_mailboxlist); $RCMAIL->output->set_env('collapsed_folders', $RCMAIL->config->get('collapsed_folders')); @@ -1334,26 +1222,32 @@ function rcmail_mailbox_select($p = array()) { global $RCMAIL; - + $p += array('maxlength' => 100, 'realnames' => false); $a_mailboxes = array(); - if ($p['unsubscribed']) - $list = $RCMAIL->imap->list_unsubscribed(); - else - $list = $RCMAIL->imap->list_mailboxes(); + if (empty($p['folder_name'])) + $p['folder_name'] = '*'; - foreach ($list as $folder) + if ($p['unsubscribed']) + $list = $RCMAIL->imap->list_unsubscribed('', $p['folder_name'], $p['folder_filter']); + else + $list = $RCMAIL->imap->list_mailboxes('', $p['folder_name'], $p['folder_filter']); + + $delimiter = $RCMAIL->imap->get_hierarchy_delimiter(); + + foreach ($list as $folder) { if (empty($p['exceptions']) || !in_array($folder, $p['exceptions'])) - rcmail_build_folder_tree($a_mailboxes, $folder, $RCMAIL->imap->get_hierarchy_delimiter()); + rcmail_build_folder_tree($a_mailboxes, $folder, $delimiter); + } $select = new html_select($p); - + if ($p['noselection']) $select->add($p['noselection'], ''); - + rcmail_render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames']); - + return $select; } @@ -1366,6 +1260,17 @@ function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='') { global $RCMAIL; + + // Handle namespace prefix + $prefix = ''; + if (!$path) { + $n_folder = $folder; + $folder = $RCMAIL->imap->mod_mailbox($folder); + + if ($n_folder != $folder) { + $prefix = substr($n_folder, 0, -strlen($folder)); + } + } $pos = strpos($folder, $delm); @@ -1387,14 +1292,14 @@ $virtual = false; } - $path .= $currentFolder; - - // Check \Noselect option (if options are in cache) - if (!$virtual && ($opts = $RCMAIL->imap->mailbox_options($path))) { - $virtual = in_array('\\Noselect', $opts); - } + $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'), @@ -1407,7 +1312,7 @@ if (strlen($subFolders)) rcmail_build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm); } - + /** * Return html for a structured list <ul> for the mailbox tree @@ -1417,7 +1322,7 @@ function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, &$jslist, $attrib, $nestLevel=0) { global $RCMAIL, $CONFIG; - + $maxlength = intval($attrib['maxlength']); $realnames = (bool)$attrib['realnames']; $msgcounts = $RCMAIL->imap->get_cache('messagecount'); @@ -1444,7 +1349,7 @@ } // make folder name safe for ids and class names - $folder_id = asciiwords($folder['id'], true, '_'); + $folder_id = html_identifier($folder['id']); $classes = array('mailbox'); // set special class for Sent, Drafts, Trash and Junk @@ -1460,25 +1365,26 @@ $classes[] = 'inbox'; else $classes[] = '_'.asciiwords($folder_class ? $folder_class : strtolower($folder['id']), true); - + $classes[] = $zebra_class; - + if ($folder['id'] == $mbox_name) $classes[] = 'selected'; $collapsed = preg_match('/&'.rawurlencode($folder['id']).'&/', $RCMAIL->config->get('collapsed_folders')); $unread = $msgcounts ? intval($msgcounts[$folder['id']]['UNSEEN']) : 0; - + if ($folder['virtual']) $classes[] = 'virtual'; else if ($unread) $classes[] = 'unread'; $js_name = JQ($folder['id']); - $html_name = Q($foldername . ($unread ? " ($unread)" : '')); + $html_name = Q($foldername) . ($unread ? html::span('unreadcount', " ($unread)") : ''); $link_attrib = $folder['virtual'] ? array() : array( 'href' => rcmail_url('', array('_mbox' => $folder['id'])), 'onclick' => sprintf("return %s.command('list','%s',this)", JS_OBJECT_NAME, $js_name), + 'rel' => $folder['id'], 'title' => $title, ); @@ -1492,9 +1398,9 @@ 'style' => "position:absolute", 'onclick' => sprintf("%s.command('collapse-folder', '%s')", JS_OBJECT_NAME, $js_name) ), ' ') : '')); - + $jslist[$folder_id] = array('id' => $folder['id'], 'name' => $foldername, 'virtual' => $folder['virtual']); - + if (!empty($folder['folders'])) { $out .= html::tag('ul', array('style' => ($collapsed ? "display:none;" : null)), rcmail_render_folder_tree_html($folder['folders'], $mbox_name, $jslist, $attrib, $nestLevel+1)); @@ -1514,32 +1420,28 @@ * @return string */ function rcmail_render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, &$select, $realnames=false, $nestLevel=0) - { - $idx = 0; +{ $out = ''; - foreach ($arrFolders as $key=>$folder) - { + + foreach ($arrFolders as $key=>$folder) { if (!$realnames && ($folder_class = rcmail_folder_classname($folder['id']))) $foldername = rcube_label($folder_class); - else - { + else { $foldername = $folder['name']; - + // shorten the folder name to a given length if ($maxlength && $maxlength>1) $foldername = abbreviate_string($foldername, $maxlength); - } + } $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); - - $idx++; - } + } return $out; - } +} /** @@ -1593,8 +1495,7 @@ $quota = rcmail_quota_content($attrib); - $OUTPUT->add_script('$(document).ready(function(){ - rcmail.set_quota('.json_serialize($quota).')});', 'foot'); + $OUTPUT->add_script('rcmail.set_quota('.json_serialize($quota).');', 'docready'); return html::span($attrib, ''); } @@ -1680,7 +1581,7 @@ { global $RCMAIL, $CONFIG; - $hook = $RCMAIL->plugins->exec_hook('hmtl_editor', array('mode' => $mode)); + $hook = $RCMAIL->plugins->exec_hook('html_editor', array('mode' => $mode)); if ($hook['abort']) return; @@ -1777,7 +1678,6 @@ } - /** * Replaces hostname variables * @@ -1789,14 +1689,20 @@ { // %n - host $n = preg_replace('/:\d+$/', '', $_SERVER['SERVER_NAME']); - // %d - domain name without first part, e.g. %d=mail.domain.tld, %m=domain.tld + // %d - domain name without first part, e.g. %n=mail.domain.tld, %d=domain.tld $d = preg_replace('/^[^\.]+\./', '', $n); // %h - IMAP host $h = $_SESSION['imap_host'] ? $_SESSION['imap_host'] : $host; // %z - IMAP domain without first part, e.g. %h=imap.domain.tld, %z=domain.tld $z = preg_replace('/^[^\.]+\./', '', $h); + // %s - domain name after the '@' from e-mail address provided at login screen. Returns FALSE if an invalid email is provided + if ( strpos($name, '%s') !== false ){ + $user_email = rcube_idn_convert(get_input_value('_user', RCUBE_INPUT_POST), true); + if ( preg_match('/(.*)@([a-z0-9\.\-\[\]\:]+)/i', $user_email, $s) < 1 || filter_var($s[1]."@".$s[2], FILTER_VALIDATE_EMAIL) === false ) + return false; + } - $name = str_replace(array('%n', '%d', '%h', '%z'), array($n, $d, $h, $z), $name); + $name = str_replace(array('%n', '%d', '%h', '%z', '%s'), array($n, $d, $h, $z, $s[2]), $name); return $name; } @@ -1874,6 +1780,39 @@ return false; } +/* + * Idn_to_ascii wrapper. + * Intl/Idn modules version of this function doesn't work with e-mail address + */ +function rcube_idn_to_ascii($str) +{ + return rcube_idn_convert($str, true); +} + +/* + * Idn_to_ascii wrapper. + * Intl/Idn modules version of this function doesn't work with e-mail address + */ +function rcube_idn_to_utf8($str) +{ + return rcube_idn_convert($str, false); +} + +function rcube_idn_convert($input, $is_utf=false) +{ + if ($at = strpos($input, '@')) { + $user = substr($input, 0, $at); + $domain = substr($input, $at+1); + } + else { + $domain = $input; + } + + $domain = $is_utf ? idn_to_ascii($domain) : idn_to_utf8($domain); + + return $at ? $user . '@' . $domain : $domain; +} + /** * Helper class to turn relative urls into absolute ones @@ -1892,6 +1831,178 @@ { return $matches[1] . '="' . make_absolute_url($matches[3], $this->base_url) . '"'; } +} + + +/****** debugging and logging functions ********/ + +/** + * Print or write debug messages + * + * @param mixed Debug message or data + * @return void + */ +function console() +{ + $args = func_get_args(); + + if (class_exists('rcmail', false)) { + $rcmail = rcmail::get_instance(); + if (is_object($rcmail->plugins)) { + $plugin = $rcmail->plugins->exec_hook('console', array('args' => $args)); + if ($plugin['abort']) + return; + $args = $plugin['args']; + } + } + + $msg = array(); + foreach ($args as $arg) + $msg[] = !is_string($arg) ? var_export($arg, true) : $arg; + + write_log('console', join(";\n", $msg)); +} + + +/** + * Append a line to a logfile in the logs directory. + * Date will be added automatically to the line. + * + * @param $name name of log file + * @param line Line to append + * @return void + */ +function write_log($name, $line) +{ + global $CONFIG, $RCMAIL; + + if (!is_string($line)) + $line = var_export($line, true); + + if (empty($CONFIG['log_date_format'])) + $CONFIG['log_date_format'] = 'd-M-Y H:i:s O'; + + $date = date($CONFIG['log_date_format']); + + // trigger logging hook + if (is_object($RCMAIL) && is_object($RCMAIL->plugins)) { + $log = $RCMAIL->plugins->exec_hook('write_log', array('name' => $name, 'date' => $date, 'line' => $line)); + $name = $log['name']; + $line = $log['line']; + $date = $log['date']; + if ($log['abort']) + return true; + } + + if ($CONFIG['log_driver'] == 'syslog') { + $prio = $name == 'errors' ? LOG_ERR : LOG_INFO; + syslog($prio, $line); + return true; + } + else { + $line = sprintf("[%s]: %s\n", $date, $line); + + // log_driver == 'file' is assumed here + if (empty($CONFIG['log_dir'])) + $CONFIG['log_dir'] = INSTALL_PATH.'logs'; + + // try to open specific log file for writing + $logfile = $CONFIG['log_dir'].'/'.$name; + if ($fp = @fopen($logfile, 'a')) { + fwrite($fp, $line); + fflush($fp); + fclose($fp); + return true; + } + else + trigger_error("Error writing to log file $logfile; Please check permissions", E_USER_WARNING); + } + + return false; +} + + +/** + * Write login data (name, ID, IP address) to the 'userlogins' log file. + * + * @return void + */ +function rcmail_log_login() +{ + global $RCMAIL; + + 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())); +} + + +/** + * Returns remote IP address and forwarded addresses if found + * + * @return string Remote IP address(es) + */ +function rcmail_remote_ip() +{ + $address = $_SERVER['REMOTE_ADDR']; + + // append the NGINX X-Real-IP header, if set + if (!empty($_SERVER['HTTP_X_REAL_IP'])) { + $remote_ip[] = 'X-Real-IP: ' . $_SERVER['HTTP_X_REAL_IP']; + } + // append the X-Forwarded-For header, if set + if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { + $remote_ip[] = 'X-Forwarded-For: ' . $_SERVER['HTTP_X_FORWARDED_FOR']; + } + + if (!empty($remote_ip)) + $address .= '(' . implode(',', $remote_ip) . ')'; + + return $address; +} + + +/** + * Check whether the HTTP referer matches the current request + * + * @return boolean True if referer is the same host+path, false if not + */ +function rcube_check_referer() +{ + $uri = parse_url($_SERVER['REQUEST_URI']); + $referer = parse_url(rc_request_header('Referer')); + return $referer['host'] == rc_request_header('Host') && $referer['path'] == $uri['path']; +} + + +/** + * @access private + * @return mixed + */ +function rcube_timer() +{ + return microtime(true); +} + + +/** + * @access private + * @return void + */ +function rcube_print_time($timer, $label='Timer', $dest='console') +{ + static $print_count = 0; + + $print_count++; + $now = rcube_timer(); + $diff = $now-$timer; + + if (empty($label)) + $label = 'Timer '.$print_count; + + write_log($dest, sprintf("%s: %0.4f sec", $label, $diff)); } @@ -1915,13 +2026,13 @@ // report bug (if not incompatible browser) if ($log && $arg['type'] && $arg['message']) - log_bug($arg); + rcube_log_bug($arg); // display error page and terminate script if ($terminate) { $ERROR_CODE = $arg['code']; $ERROR_MESSAGE = $arg['message']; - include('program/steps/utils/error.inc'); + include INSTALL_PATH . 'program/steps/utils/error.inc'; exit; } } @@ -1935,13 +2046,20 @@ * @return void * @see raise_error() */ -function log_bug($arg_arr) +function rcube_log_bug($arg_arr) { global $CONFIG; + $program = strtoupper($arg_arr['type']); + $level = $CONFIG['debug_level']; + + // disable errors for ajax requests, write to log instead (#1487831) + if (($level & 4) && !empty($_REQUEST['_remote'])) { + $level = ($level ^ 4) | 1; + } // write error to local log file - if ($CONFIG['debug_level'] & 1) { + if ($level & 1) { $post_query = ($_SERVER['REQUEST_METHOD'] == 'POST' ? '?_task='.urlencode($_POST['_task']).'&_action='.urlencode($_POST['_action']) : ''); $log_entry = sprintf("%s Error: %s%s (%s %s)", $program, @@ -1956,13 +2074,13 @@ } } - // resport the bug to the global bug reporting system - if ($CONFIG['debug_level'] & 2) { + // report the bug to the global bug reporting system + if ($level & 2) { // TODO: Send error via HTTP } // show error if debug_mode is on - if ($CONFIG['debug_level'] & 4) { + if ($level & 4) { print "<b>$program Error"; if (!empty($arg_arr['file']) && !empty($arg_arr['line'])) -- Gitblit v1.9.1