- Merge devel-framework branch, resolved conflicts
6 files added
2 files renamed
2 files deleted
36 files modified
| | |
| | | CHANGELOG Roundcube Webmail |
| | | =========================== |
| | | |
| | | - Roundcube Framework: |
| | | Add possibility to replace IMAP driver with custom class |
| | | Add IMAP auto-connection feature, improving performance with caching enabled |
| | | Replace imap_init hook with storage_init (with additional 'driver' argument) |
| | | Improved performance by caching IMAP server's capabilities in session |
| | | Unified global functions naming (rcube_ prefix) |
| | | Move global functions from main.inc and rcube_shared.inc into classes |
| | | Better classes separation |
| | | |
| | | RELEASE 0.8-rc |
| | | ---------------- |
| | | - Set flexible width to login form fields (#1488418) |
| | | - Fix re-draw bug on list columns change in IE8 (#1487822) |
| | | - Allow mass-removal of addresses from a group (#1487748) |
| | |
| | | $IMAP->set_folder($mbox); |
| | | |
| | | $index = $IMAP->index($mbox, null, 'ASC'); |
| | | $count = $index->countMessages(); |
| | | $count = $index->count(); |
| | | $index = $index->get(); |
| | | |
| | | vputs("Getting message list of {$mbox}..."); |
| | |
| | | /* |
| | | +-------------------------------------------------------------------------+ |
| | | | Roundcube Webmail IMAP Client | |
| | | | Version 0.8-svn | |
| | | | Version 0.9-svn | |
| | | | | |
| | | | Copyright (C) 2005-2012, The Roundcube Dev Team | |
| | | | | |
| | |
| | | $RCMAIL = rcmail::get_instance(); |
| | | |
| | | // Make the whole PHP output non-cacheable (#1487797) |
| | | send_nocacheing_headers(); |
| | | $RCMAIL->output->nocacheing_headers(); |
| | | |
| | | // turn on output buffering |
| | | ob_start(); |
| | |
| | | } |
| | | |
| | | // error steps |
| | | if ($RCMAIL->action=='error' && !empty($_GET['_code'])) { |
| | | if ($RCMAIL->action == 'error' && !empty($_GET['_code'])) { |
| | | raise_error(array('code' => hexdec($_GET['_code'])), FALSE, TRUE); |
| | | } |
| | | |
| | | // check if https is required (for login) and redirect if necessary |
| | | if (empty($_SESSION['user_id']) && ($force_https = $RCMAIL->config->get('force_https', false))) { |
| | | $https_port = is_bool($force_https) ? 443 : $force_https; |
| | | if (!rcube_https_check($https_port)) { |
| | | if (!rcube_ui::https_check($https_port)) { |
| | | $host = preg_replace('/:[0-9]+$/', '', $_SERVER['HTTP_HOST']); |
| | | $host .= ($https_port != 443 ? ':' . $https_port : ''); |
| | | header('Location: https://' . $host . $_SERVER['REQUEST_URI']); |
| | |
| | | |
| | | // try to log in |
| | | if ($RCMAIL->task == 'login' && $RCMAIL->action == 'login') { |
| | | $request_valid = $_SESSION['temp'] && $RCMAIL->check_request(RCUBE_INPUT_POST, 'login'); |
| | | $request_valid = $_SESSION['temp'] && $RCMAIL->check_request(rcube_ui::INPUT_POST, 'login'); |
| | | |
| | | // purge the session in case of new login when a session already exists |
| | | $RCMAIL->kill_session(); |
| | | |
| | | $auth = $RCMAIL->plugins->exec_hook('authenticate', array( |
| | | 'host' => $RCMAIL->autoselect_host(), |
| | | 'user' => trim(get_input_value('_user', RCUBE_INPUT_POST)), |
| | | 'pass' => get_input_value('_pass', RCUBE_INPUT_POST, true, |
| | | 'user' => trim(rcube_ui::get_input_value('_user', rcube_ui::INPUT_POST)), |
| | | 'pass' => rcube_ui::get_input_value('_pass', rcube_ui::INPUT_POST, true, |
| | | $RCMAIL->config->get('password_charset', 'ISO-8859-1')), |
| | | 'cookiecheck' => true, |
| | | 'valid' => $request_valid, |
| | |
| | | $RCMAIL->session->set_auth_cookie(); |
| | | |
| | | // log successful login |
| | | rcmail_log_login(); |
| | | $RCMAIL->log_login(); |
| | | |
| | | // restore original request parameters |
| | | $query = array(); |
| | | if ($url = get_input_value('_url', RCUBE_INPUT_POST)) { |
| | | if ($url = rcube_ui::get_input_value('_url', rcube_ui::INPUT_POST)) { |
| | | parse_str($url, $query); |
| | | |
| | | // prevent endless looping on login page |
| | |
| | | } |
| | | |
| | | // end session (after optional referer check) |
| | | else if ($RCMAIL->task == 'logout' && isset($_SESSION['user_id']) && (!$RCMAIL->config->get('referer_check') || rcube_check_referer())) { |
| | | else if ($RCMAIL->task == 'logout' && isset($_SESSION['user_id']) && (!$RCMAIL->config->get('referer_check') || rcmail::check_referer())) { |
| | | $userdata = array( |
| | | 'user' => $_SESSION['username'], |
| | | 'host' => $_SESSION['storage_host'], |
| | |
| | | // not logged in -> show login page |
| | | if (empty($RCMAIL->user->ID)) { |
| | | // log session failures |
| | | if (($task = get_input_value('_task', RCUBE_INPUT_GPC)) && !in_array($task, array('login','logout')) && !$session_error && ($sess_id = $_COOKIE[ini_get('session.name')])) { |
| | | $task = rcube_ui::get_input_value('_task', rcube_ui::INPUT_GPC); |
| | | if ($task && !in_array($task, array('login','logout')) && !$session_error && ($sess_id = $_COOKIE[ini_get('session.name')])) { |
| | | $RCMAIL->session->log("Aborted session " . $sess_id . "; no valid session data found"); |
| | | $session_error = true; |
| | | } |
| | |
| | | |
| | | // check client X-header to verify request origin |
| | | if ($OUTPUT->ajax_call) { |
| | | if (rc_request_header('X-Roundcube-Request') != $RCMAIL->get_request_token() && !$RCMAIL->config->get('devel_mode')) { |
| | | if (rcube_request_header('X-Roundcube-Request') != $RCMAIL->get_request_token() && !$RCMAIL->config->get('devel_mode')) { |
| | | header('HTTP/1.1 403 Forbidden'); |
| | | die("Invalid Request"); |
| | | } |
| | |
| | | } |
| | | |
| | | // check referer if configured |
| | | if (!$request_check_whitelist[$RCMAIL->action] && $RCMAIL->config->get('referer_check') && !rcube_check_referer()) { |
| | | if (!$request_check_whitelist[$RCMAIL->action] && $RCMAIL->config->get('referer_check') && !rcmail::check_referer()) { |
| | | raise_error(array( |
| | | 'code' => 403, |
| | | 'type' => 'php', |
| | |
| | | set_include_path($include_path); |
| | | |
| | | require_once 'utils.php'; |
| | | require_once 'rcube_shared.inc'; |
| | | // deprecated aliases (to be removed) |
| | | require_once 'main.inc'; |
| | | |
| | | session_start(); |
| | |
| | | include_once $filename. '.php'; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Fake internal error handler to catch errors |
| | | */ |
| | | function raise_error($p) |
| | | { |
| | | $rci = rcube_install::get_instance(); |
| | | $rci->raise_error($p); |
| | | } |
| | | |
| | | /** |
| | | * Local callback function for PEAR errors |
| | | */ |
| | | function rcube_pear_error($err) |
| | | function __pear_error($err) |
| | | { |
| | | raise_error(array( |
| | | rcmail::raise_error(array( |
| | | 'code' => $err->getCode(), |
| | | 'message' => $err->getMessage(), |
| | | )); |
| | | } |
| | | |
| | | // set PEAR error handling (will also load the PEAR main class) |
| | | PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'rcube_pear_error'); |
| | | PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, '__pear_error'); |
| | |
| | | continue; |
| | | |
| | | $args[$key] = preg_replace(array('/^["\']/', '/["\']$/'), '', $value); |
| | | |
| | | |
| | | if ($alias = $aliases[$key]) |
| | | $args[$alias] = $args[$key]; |
| | | } |
| | |
| | | $attrib_arr = array(); |
| | | foreach ($attrib as $key => $value) { |
| | | // skip size if not numeric |
| | | if (($key=='size' && !is_numeric($value))) { |
| | | if ($key == 'size' && !is_numeric($value)) { |
| | | continue; |
| | | } |
| | | |
| | |
| | | $attrib_arr[] = $key . '="' . $key . '"'; |
| | | } |
| | | } |
| | | else if ($key=='value') { |
| | | $attrib_arr[] = $key . '="' . Q($value, 'strict', false) . '"'; |
| | | } |
| | | else { |
| | | $attrib_arr[] = $key . '="' . Q($value) . '"'; |
| | | $attrib_arr[] = $key . '="' . self::quote($value) . '"'; |
| | | } |
| | | } |
| | | |
| | | return count($attrib_arr) ? ' '.implode(' ', $attrib_arr) : ''; |
| | | } |
| | | |
| | | /** |
| | | * Convert a HTML attribute string attributes to an associative array (name => value) |
| | | * |
| | | * @param string Input string |
| | | * @return array Key-value pairs of parsed attributes |
| | | */ |
| | | public static function parse_attrib_string($str) |
| | | { |
| | | $attrib = array(); |
| | | $regexp = '/\s*([-_a-z]+)=(["\'])??(?(2)([^\2]*)\2|(\S+?))/Ui'; |
| | | |
| | | preg_match_all($regexp, stripslashes($str), $regs, PREG_SET_ORDER); |
| | | |
| | | // convert attributes to an associative array (name => value) |
| | | if ($regs) { |
| | | foreach ($regs as $attr) { |
| | | $attrib[strtolower($attr[1])] = html_entity_decode($attr[3] . $attr[4]); |
| | | } |
| | | } |
| | | |
| | | return $attrib; |
| | | } |
| | | |
| | | /** |
| | | * Replacing specials characters in html attribute value |
| | | * |
| | | * @param string $str Input string |
| | | * |
| | | * @return string The quoted string |
| | | */ |
| | | public static function quote($str) |
| | | { |
| | | $str = htmlspecialchars($str, ENT_COMPAT, RCMAIL_CHARSET); |
| | | |
| | | // avoid douple quotation of & |
| | | // @TODO: get rid of it? |
| | | $str = preg_replace('/&([A-Za-z]{2,6}|#[0-9]{2,4});/', '&\\1;', $str); |
| | | |
| | | return $str; |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Class to create an HTML input field |
| | |
| | | { |
| | | protected $tagname = 'input'; |
| | | protected $type = 'text'; |
| | | protected $allowed = array('type','name','value','size','tabindex', |
| | | protected $allowed = array( |
| | | 'type','name','value','size','tabindex', |
| | | 'autocomplete','checked','onchange','onclick','disabled','readonly', |
| | | 'spellcheck','results','maxlength','src','multiple','placeholder'); |
| | | 'spellcheck','results','maxlength','src','multiple','placeholder', |
| | | ); |
| | | |
| | | /** |
| | | * Object constructor |
| | |
| | | } |
| | | |
| | | if (!empty($value) && !preg_match('/mce_editor/', $this->attrib['class'])) { |
| | | $value = Q($value, 'strict', false); |
| | | $value = self::quote($value); |
| | | } |
| | | |
| | | return self::tag($this->tagname, $this->attrib, $value, |
| | | array_merge(self::$common_attrib, $this->allowed)); |
| | | array_merge(self::$common_attrib, $this->allowed)); |
| | | } |
| | | } |
| | | |
| | |
| | | protected $options = array(); |
| | | protected $allowed = array('name','size','tabindex','autocomplete', |
| | | 'multiple','onchange','disabled','rel'); |
| | | |
| | | |
| | | /** |
| | | * Add a new option to this drop-down |
| | | * |
| | |
| | | 'selected' => (in_array($option['value'], $select, true) || |
| | | in_array($option['text'], $select, true)) ? 1 : null); |
| | | |
| | | $this->content .= self::tag('option', $attr, Q($option['text'])); |
| | | $this->content .= self::tag('option', $attr, self::quote($option['text'])); |
| | | } |
| | | |
| | | return parent::show(); |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | // application constants |
| | | define('RCMAIL_VERSION', '0.8-svn'); |
| | | define('RCMAIL_VERSION', '0.9-svn'); |
| | | define('RCMAIL_CHARSET', 'UTF-8'); |
| | | define('JS_OBJECT_NAME', 'rcmail'); |
| | | define('RCMAIL_START', microtime(true)); |
| | |
| | | |
| | | if (!defined('RCMAIL_CONFIG_DIR')) { |
| | | define('RCMAIL_CONFIG_DIR', INSTALL_PATH . 'config'); |
| | | } |
| | | |
| | | // make sure path_separator is defined |
| | | if (!defined('PATH_SEPARATOR')) { |
| | | define('PATH_SEPARATOR', (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') ? ';' : ':'); |
| | | } |
| | | |
| | | // RC include folders MUST be included FIRST to avoid other |
| | |
| | | @mb_regex_encoding(RCMAIL_CHARSET); |
| | | } |
| | | |
| | | /** |
| | | * Use PHP5 autoload for dynamic class loading |
| | | * |
| | | * @todo Make Zend, PEAR etc play with this |
| | | * @todo Make our classes conform to a more straight forward CS. |
| | | */ |
| | | function rcube_autoload($classname) |
| | | { |
| | | $filename = preg_replace( |
| | | array( |
| | | '/MDB2_(.+)/', |
| | | '/Mail_(.+)/', |
| | | '/Net_(.+)/', |
| | | '/Auth_(.+)/', |
| | | '/^html_.+/', |
| | | '/^utf8$/', |
| | | ), |
| | | array( |
| | | 'MDB2/\\1', |
| | | 'Mail/\\1', |
| | | 'Net/\\1', |
| | | 'Auth/\\1', |
| | | 'html', |
| | | 'utf8.class', |
| | | ), |
| | | $classname |
| | | ); |
| | | // include global functions |
| | | require_once INSTALL_PATH . 'program/include/rcube_shared.inc'; |
| | | |
| | | if ($fp = @fopen("$filename.php", 'r', true)) { |
| | | fclose($fp); |
| | | include_once("$filename.php"); |
| | | return true; |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | // Register autoloader |
| | | spl_autoload_register('rcube_autoload'); |
| | | |
| | | /** |
| | | * Local callback function for PEAR errors |
| | | */ |
| | | function rcube_pear_error($err) |
| | | { |
| | | error_log(sprintf("%s (%s): %s", |
| | | $err->getMessage(), |
| | | $err->getCode(), |
| | | $err->getUserinfo()), 0); |
| | | } |
| | | |
| | | // set PEAR error handling (will also load the PEAR main class) |
| | | PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'rcube_pear_error'); |
| | | |
| | | // include global functions |
| | | // backward compatybility (to be removed) |
| | | require_once INSTALL_PATH . 'program/include/main.inc'; |
| | | require_once INSTALL_PATH . 'program/include/rcube_shared.inc'; |
| | |
| | | | program/include/main.inc | |
| | | | | |
| | | | This file is part of the Roundcube Webmail client | |
| | | | Copyright (C) 2005-2011, The Roundcube Dev Team | |
| | | | Copyright (C) 2005-2012, The Roundcube Dev Team | |
| | | | | |
| | | | Licensed under the GNU General Public License version 3 or | |
| | | | any later version with exceptions for skins & plugins. | |
| | | | See the README file for a full license statement. | |
| | | | | |
| | | | PURPOSE: | |
| | | | Provide basic functions for the webmail package | |
| | | | Provide deprecated functions aliases for backward compatibility | |
| | | | | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Thomas Bruederli <roundcube@gmail.com> | |
| | |
| | | */ |
| | | |
| | | /** |
| | | * Roundcube Webmail common functions |
| | | * Roundcube Webmail deprecated functions |
| | | * |
| | | * @package Core |
| | | * @author Thomas Bruederli <roundcube@gmail.com> |
| | | */ |
| | | |
| | | require_once INSTALL_PATH . 'program/include/rcube_shared.inc'; |
| | | |
| | | // define constannts for input reading |
| | | define('RCUBE_INPUT_GET', 0x0101); |
| | | define('RCUBE_INPUT_POST', 0x0102); |
| | | define('RCUBE_INPUT_GPC', 0x0103); |
| | | // constants for input reading |
| | | define('RCUBE_INPUT_GET', rcube_ui::INPUT_GET); |
| | | define('RCUBE_INPUT_POST', rcube_ui::INPUT_POST); |
| | | define('RCUBE_INPUT_GPC', rcube_ui::INPUT_GPC); |
| | | |
| | | |
| | | |
| | | /** |
| | | * Return correct name for a specific database table |
| | | * |
| | | * @param string Table name |
| | | * @return string Translated table name |
| | | */ |
| | | function get_table_name($table) |
| | | { |
| | | global $CONFIG; |
| | | { |
| | | return rcmail::get_instance()->db->table_name($table); |
| | | } |
| | | |
| | | // return table name if configured |
| | | $config_key = 'db_table_'.$table; |
| | | |
| | | if (strlen($CONFIG[$config_key])) |
| | | return $CONFIG[$config_key]; |
| | | |
| | | return $table; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return correct name for a specific database sequence |
| | | * (used for Postgres only) |
| | | * |
| | | * @param string Secuence name |
| | | * @return string Translated sequence name |
| | | */ |
| | | function get_sequence_name($sequence) |
| | | { |
| | | // return sequence name if configured |
| | | $config_key = 'db_sequence_'.$sequence; |
| | | $opt = rcmail::get_instance()->config->get($config_key); |
| | | { |
| | | return rcmail::get_instance()->db->sequence_name($sequence); |
| | | } |
| | | |
| | | if (!empty($opt)) |
| | | return $opt; |
| | | |
| | | return $sequence; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get localized text in the desired language |
| | | * 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); |
| | | 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, &$ref_domain = null) |
| | | { |
| | | return rcmail::get_instance()->text_exists($name, $domain, $ref_domain); |
| | | return rcmail::get_instance()->text_exists($name, $domain, $ref_domain); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Overwrite action variable |
| | | * |
| | | * @param string New action value |
| | | */ |
| | | function rcmail_overwrite_action($action) |
| | | { |
| | | $app = rcmail::get_instance(); |
| | | $app->action = $action; |
| | | $app->output->set_env('action', $action); |
| | | } |
| | | { |
| | | rcmail::get_instance()->overwrite_action($action); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Compose an URL for a specific action |
| | | * |
| | | * @param string Request action |
| | | * @param array More URL parameters |
| | | * @param string Request task (omit if the same) |
| | | * @return The application URL |
| | | */ |
| | | function rcmail_url($action, $p=array(), $task=null) |
| | | { |
| | | $app = rcmail::get_instance(); |
| | | return $app->url((array)$p + array('_action' => $action, 'task' => $task)); |
| | | return rcube_ui::url($action, $p, $task); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Garbage collector function for temp files. |
| | | * Remove temp files older than two days |
| | | */ |
| | | function rcmail_temp_gc() |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | |
| | | $tmp = unslashify($rcmail->config->get('temp_dir')); |
| | | $expire = mktime() - 172800; // expire in 48 hours |
| | | |
| | | if ($dir = opendir($tmp)) { |
| | | while (($fname = readdir($dir)) !== false) { |
| | | if ($fname{0} == '.') |
| | | continue; |
| | | |
| | | if (filemtime($tmp.'/'.$fname) < $expire) |
| | | @unlink($tmp.'/'.$fname); |
| | | } |
| | | |
| | | closedir($dir); |
| | | } |
| | | $rcmail = rcmail::get_instance()->temp_gc(); |
| | | } |
| | | |
| | | |
| | | // Deprecated |
| | | function rcube_charset_convert($str, $from, $to=NULL) |
| | | { |
| | | return rcube_charset::convert($str, $from, $to); |
| | | } |
| | | |
| | | |
| | | // Deprecated |
| | | function rc_detect_encoding($string, $failover='') |
| | | { |
| | | return rcube_charset::detect($string, $failover); |
| | | } |
| | | |
| | | |
| | | // Deprecated |
| | | function rc_utf8_clean($input) |
| | | { |
| | | return rcube_charset::clean($input); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Convert a variable into a javascript object notation |
| | | * |
| | | * @param mixed Input value |
| | | * @return string Serialized JSON string |
| | | */ |
| | | function json_serialize($input) |
| | | { |
| | | $input = rcube_charset::clean($input); |
| | | |
| | | // sometimes even using rcube_charset::clean() the input contains invalid UTF-8 sequences |
| | | // that's why we have @ here |
| | | return @json_encode($input); |
| | | return rcube_output::json_serialize($input); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Replacing specials characters to a specific encoding type |
| | | * |
| | | * @param string Input string |
| | | * @param string Encoding type: text|html|xml|js|url |
| | | * @param string Replace mode for tags: show|replace|remove |
| | | * @param boolean Convert newlines |
| | | * @return string The quoted string |
| | | */ |
| | | function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE) |
| | | { |
| | | static $html_encode_arr = false; |
| | | static $js_rep_table = false; |
| | | static $xml_rep_table = false; |
| | | { |
| | | return rcube_ui::rep_specialchars_output($str, $enctype, $mode, $newlines); |
| | | } |
| | | |
| | | if (!$enctype) |
| | | $enctype = $OUTPUT->type; |
| | | |
| | | // encode for HTML output |
| | | if ($enctype=='html') |
| | | { |
| | | if (!$html_encode_arr) |
| | | { |
| | | $html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS); |
| | | unset($html_encode_arr['?']); |
| | | } |
| | | |
| | | $ltpos = strpos($str, '<'); |
| | | $encode_arr = $html_encode_arr; |
| | | |
| | | // don't replace quotes and html tags |
| | | if (($mode=='show' || $mode=='') && $ltpos!==false && strpos($str, '>', $ltpos)!==false) |
| | | { |
| | | unset($encode_arr['"']); |
| | | unset($encode_arr['<']); |
| | | unset($encode_arr['>']); |
| | | unset($encode_arr['&']); |
| | | } |
| | | else if ($mode=='remove') |
| | | $str = strip_tags($str); |
| | | |
| | | $out = strtr($str, $encode_arr); |
| | | |
| | | // avoid douple quotation of & |
| | | $out = preg_replace('/&([A-Za-z]{2,6}|#[0-9]{2,4});/', '&\\1;', $out); |
| | | |
| | | return $newlines ? nl2br($out) : $out; |
| | | } |
| | | |
| | | // if the replace tables for XML and JS are not yet defined |
| | | if ($js_rep_table===false) |
| | | { |
| | | $js_rep_table = $xml_rep_table = array(); |
| | | $xml_rep_table['&'] = '&'; |
| | | |
| | | for ($c=160; $c<256; $c++) // can be increased to support more charsets |
| | | $xml_rep_table[chr($c)] = "&#$c;"; |
| | | |
| | | $xml_rep_table['"'] = '"'; |
| | | $js_rep_table['"'] = '\\"'; |
| | | $js_rep_table["'"] = "\\'"; |
| | | $js_rep_table["\\"] = "\\\\"; |
| | | // Unicode line and paragraph separators (#1486310) |
| | | $js_rep_table[chr(hexdec(E2)).chr(hexdec(80)).chr(hexdec(A8))] = '
'; |
| | | $js_rep_table[chr(hexdec(E2)).chr(hexdec(80)).chr(hexdec(A9))] = '
'; |
| | | } |
| | | |
| | | // encode for javascript use |
| | | if ($enctype=='js') |
| | | return preg_replace(array("/\r?\n/", "/\r/", '/<\\//'), array('\n', '\n', '<\\/'), strtr($str, $js_rep_table)); |
| | | |
| | | // encode for plaintext |
| | | if ($enctype=='text') |
| | | return str_replace("\r\n", "\n", $mode=='remove' ? strip_tags($str) : $str); |
| | | |
| | | if ($enctype=='url') |
| | | return rawurlencode($str); |
| | | |
| | | // encode for XML |
| | | if ($enctype=='xml') |
| | | return strtr($str, $xml_rep_table); |
| | | |
| | | // no encoding given -> return original string |
| | | return $str; |
| | | } |
| | | |
| | | /** |
| | | * Quote a given string. |
| | | * Shortcut function for rep_specialchars_output |
| | | * |
| | | * @return string HTML-quoted string |
| | | * @see rep_specialchars_output() |
| | | */ |
| | | function Q($str, $mode='strict', $newlines=TRUE) |
| | | { |
| | | return rep_specialchars_output($str, 'html', $mode, $newlines); |
| | | } |
| | | { |
| | | return rcube_ui::Q($str, $mode, $newlines); |
| | | } |
| | | |
| | | /** |
| | | * Quote a given string for javascript output. |
| | | * Shortcut function for rep_specialchars_output |
| | | * |
| | | * @return string JS-quoted string |
| | | * @see rep_specialchars_output() |
| | | */ |
| | | function JQ($str) |
| | | { |
| | | return rep_specialchars_output($str, 'js'); |
| | | } |
| | | { |
| | | return rcube_ui::JQ($str); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Read input value and convert it for internal use |
| | | * Performs stripslashes() and charset conversion if necessary |
| | | * |
| | | * @param string Field name to read |
| | | * @param int Source to get value from (GPC) |
| | | * @param boolean Allow HTML tags in field value |
| | | * @param string Charset to convert into |
| | | * @return string Field value or NULL if not available |
| | | */ |
| | | function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL) |
| | | { |
| | | $value = NULL; |
| | | |
| | | 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); |
| | | return rcube_ui::get_input_value($fname, $source, $allow_html, $charset); |
| | | } |
| | | |
| | | /** |
| | | * 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 |
| | | * @return string Parsed value |
| | | */ |
| | | function parse_input_value($value, $allow_html=FALSE, $charset=NULL) |
| | | { |
| | | global $OUTPUT; |
| | | |
| | | if (empty($value)) |
| | | return $value; |
| | | |
| | | if (is_array($value)) { |
| | | foreach ($value as $idx => $val) |
| | | $value[$idx] = parse_input_value($val, $allow_html, $charset); |
| | | return $value; |
| | | } |
| | | |
| | | // strip single quotes if magic_quotes_sybase is enabled |
| | | if (ini_get('magic_quotes_sybase')) |
| | | $value = str_replace("''", "'", $value); |
| | | // strip slashes if magic_quotes enabled |
| | | else if (get_magic_quotes_gpc() || get_magic_quotes_runtime()) |
| | | $value = stripslashes($value); |
| | | |
| | | // 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 ($charset && $output_charset) |
| | | $value = rcube_charset_convert($value, $output_charset, $charset); |
| | | |
| | | return $value; |
| | | return rcube_ui::parse_input_value($value, $allow_html, $charset); |
| | | } |
| | | |
| | | /** |
| | | * Convert array of request parameters (prefixed with _) |
| | | * to a regular array with non-prefixed keys. |
| | | * |
| | | * @param int Source to get value from (GPC) |
| | | * @return array Hash array with all request parameters |
| | | */ |
| | | 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; |
| | | if ($ignore && !preg_match('/^(' . $ignore . ')$/', $fname)) |
| | | $out[$fname] = get_input_value($key, $mode); |
| | | } |
| | | |
| | | return $out; |
| | | return rcube_ui::request2param($mode, $ignore); |
| | | } |
| | | |
| | | /** |
| | | * Remove all non-ascii and non-word chars |
| | | * except ., -, _ |
| | | */ |
| | | function asciiwords($str, $css_id = false, $replace_with = '') |
| | | { |
| | | $allowed = 'a-z0-9\_\-' . (!$css_id ? '\.' : ''); |
| | | return preg_replace("/[^$allowed]/i", $replace_with, $str); |
| | | } |
| | | |
| | | /** |
| | | * Convert the given string into a valid HTML identifier |
| | | * Same functionality as done in app.js with rcube_webmail.html_identifier() |
| | | */ |
| | | function html_identifier($str, $encode=false) |
| | | { |
| | | if ($encode) |
| | | return rtrim(strtr(base64_encode($str), '+/', '-_'), '='); |
| | | else |
| | | return asciiwords($str, true, '_'); |
| | | return rcube_ui::html_identifier($str, $encode); |
| | | } |
| | | |
| | | /** |
| | | * Remove single and double quotes from given string |
| | | * |
| | | * @param string Input value |
| | | * @return string Dequoted string |
| | | */ |
| | | function strip_quotes($str) |
| | | { |
| | | return str_replace(array("'", '"'), '', $str); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Remove new lines characters from given string |
| | | * |
| | | * @param string Input value |
| | | * @return string Stripped string |
| | | */ |
| | | function strip_newlines($str) |
| | | { |
| | | return preg_replace('/[\r\n]/', '', $str); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create a HTML table based on the given data |
| | | * |
| | | * @param array Named table attributes |
| | | * @param mixed Table row data. Either a two-dimensional array or a valid SQL result set |
| | | * @param array List of cols to show |
| | | * @param string Name of the identifier col |
| | | * @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)) |
| | | { |
| | | $db = $RCMAIL->get_dbh(); |
| | | while ($table_data && ($sql_arr = $db->fetch_assoc($table_data))) |
| | | { |
| | | $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 { |
| | | foreach ($table_data as $row_data) |
| | | { |
| | | $class = !empty($row_data['class']) ? $row_data['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); |
| | | return rcube_ui::table_output($attrib, $table_data, $a_show_cols, $id_col); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create an edit field for inclusion on a form |
| | | * |
| | | * @param string col field name |
| | | * @param string value field value |
| | | * @param array attrib HTML element attributes for field |
| | | * @param string type HTML element type (default 'text') |
| | | * @return string HTML field definition |
| | | */ |
| | | function rcmail_get_edit_field($col, $value, $attrib, $type='text') |
| | | { |
| | | static $colcounts = array(); |
| | | |
| | | $fname = '_'.$col; |
| | | $attrib['name'] = $fname . ($attrib['array'] ? '[]' : ''); |
| | | $attrib['class'] = trim($attrib['class'] . ' ff_' . $col); |
| | | |
| | | if ($type == 'checkbox') { |
| | | $attrib['value'] = '1'; |
| | | $input = new html_checkbox($attrib); |
| | | } |
| | | else if ($type == 'textarea') { |
| | | $attrib['cols'] = $attrib['size']; |
| | | $input = new html_textarea($attrib); |
| | | } |
| | | else if ($type == 'select') { |
| | | $input = new html_select($attrib); |
| | | $input->add('---', ''); |
| | | $input->add(array_values($attrib['options']), array_keys($attrib['options'])); |
| | | } |
| | | else if ($attrib['type'] == 'password') { |
| | | $input = new html_passwordfield($attrib); |
| | | } |
| | | else { |
| | | if ($attrib['type'] != 'text' && $attrib['type'] != 'hidden') |
| | | $attrib['type'] = 'text'; |
| | | $input = new html_inputfield($attrib); |
| | | } |
| | | |
| | | // use value from post |
| | | if (isset($_POST[$fname])) { |
| | | $postvalue = get_input_value($fname, RCUBE_INPUT_POST, true); |
| | | $value = $attrib['array'] ? $postvalue[intval($colcounts[$col]++)] : $postvalue; |
| | | } |
| | | |
| | | $out = $input->show($value); |
| | | |
| | | return $out; |
| | | return rcube_ui::get_edit_field($col, $value, $attrib, $type); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Replace all css definitions with #container [def] |
| | | * and remove css-inlined scripting |
| | | * |
| | | * @param string CSS source code |
| | | * @param string Container ID to use as prefix |
| | | * @return string Modified CSS source |
| | | */ |
| | | function rcmail_mod_css_styles($source, $container_id, $allow_remote=false) |
| | | { |
| | | $last_pos = 0; |
| | | $replacements = new rcube_string_replacer; |
| | | { |
| | | return rcube_ui::mod_css_styles($source, $container_id, $allow_remote); |
| | | } |
| | | |
| | | // ignore the whole block if evil styles are detected |
| | | $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! */'; |
| | | |
| | | // cut out all contents between { and } |
| | | 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; |
| | | } |
| | | |
| | | // remove html comments and add #container to each tag selector. |
| | | // also replace body definition because we also stripped off the <body> tag |
| | | $styles = preg_replace( |
| | | array( |
| | | '/(^\s*<!--)|(-->\s*$)/', |
| | | '/(^\s*|,\s*|\}\s*)([a-z0-9\._#\*][a-z0-9\.\-_]*)/im', |
| | | '/'.preg_quote($container_id, '/').'\s+body/i', |
| | | ), |
| | | array( |
| | | '', |
| | | "\\1#$container_id \\2", |
| | | $container_id, |
| | | ), |
| | | $source); |
| | | |
| | | // put block contents back in |
| | | $styles = $replacements->resolve($styles); |
| | | |
| | | return $styles; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Decode escaped entities used by known XSS exploits. |
| | | * See http://downloads.securityfocus.com/vulnerabilities/exploits/26800.eml for examples |
| | | * |
| | | * @param string CSS content to decode |
| | | * @return string Decoded string |
| | | */ |
| | | function rcmail_xss_entity_decode($content) |
| | | { |
| | | $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('#/\*.*\*/#Ums', '', $out); |
| | | return $out; |
| | | return rcube_ui::xss_entity_decode($content); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * preg_replace_callback callback for rcmail_xss_entity_decode_callback |
| | | * |
| | | * @param array matches result from preg_replace_callback |
| | | * @return string decoded entity |
| | | */ |
| | | function rcmail_xss_entity_decode_callback($matches) |
| | | { |
| | | return chr(hexdec($matches[1])); |
| | | } |
| | | |
| | | /** |
| | | * Compose a valid attribute string for HTML tags |
| | | * |
| | | * @param array Named tag attributes |
| | | * @param array List of allowed attributes |
| | | * @return string HTML formatted attribute string |
| | | */ |
| | | function create_attrib_string($attrib, $allowed_attribs=array('id', 'class', 'style')) |
| | | { |
| | | // allow the following attributes to be added to the <iframe> tag |
| | | $attrib_str = ''; |
| | | foreach ($allowed_attribs as $a) |
| | | if (isset($attrib[$a])) |
| | | $attrib_str .= sprintf(' %s="%s"', $a, str_replace('"', '"', $attrib[$a])); |
| | | |
| | | return $attrib_str; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Convert a HTML attribute string attributes to an associative array (name => value) |
| | | * |
| | | * @param string Input string |
| | | * @return array Key-value pairs of parsed attributes |
| | | */ |
| | | function parse_attrib_string($str) |
| | | { |
| | | $attrib = array(); |
| | | preg_match_all('/\s*([-_a-z]+)=(["\'])??(?(2)([^\2]*)\2|(\S+?))/Ui', stripslashes($str), $regs, PREG_SET_ORDER); |
| | | |
| | | // convert attributes to an associative array (name => value) |
| | | if ($regs) { |
| | | foreach ($regs as $attr) { |
| | | $attrib[strtolower($attr[1])] = html_entity_decode($attr[3] . $attr[4]); |
| | | } |
| | | } |
| | | |
| | | return $attrib; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 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; |
| | | return html::attrib_string($attrib, $allowed_attribs); |
| | | } |
| | | |
| | | function parse_attrib_string($str) |
| | | { |
| | | return html::parse_attrib_string($str); |
| | | } |
| | | |
| | | /** |
| | | * Convert the given date to a human readable form |
| | | * This uses the date formatting properties from config |
| | | * |
| | | * @param mixed Date representation (string, timestamp or DateTime object) |
| | | * @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, $convert=true) |
| | | { |
| | | global $RCMAIL, $CONFIG; |
| | | |
| | | if (is_object($date) && is_a($date, 'DateTime')) { |
| | | $timestamp = $date->format('U'); |
| | | } |
| | | else { |
| | | if (!empty($date)) |
| | | $timestamp = rcube_strtotime($date); |
| | | |
| | | if (empty($timestamp)) |
| | | return ''; |
| | | |
| | | try { |
| | | $date = new DateTime("@".$timestamp); |
| | | } |
| | | catch (Exception $e) { |
| | | return ''; |
| | | } |
| | | } |
| | | |
| | | if ($convert) { |
| | | try { |
| | | // convert to the right timezone |
| | | $stz = date_default_timezone_get(); |
| | | $tz = new DateTimeZone($RCMAIL->config->get('timezone')); |
| | | $date->setTimezone($tz); |
| | | date_default_timezone_set($tz->getName()); |
| | | |
| | | $timestamp = $date->format('U'); |
| | | } |
| | | catch (Exception $e) { |
| | | } |
| | | } |
| | | |
| | | // define date format depending on current time |
| | | if (!$format) { |
| | | $now = 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']); |
| | | |
| | | if ($CONFIG['prettydate'] && $timestamp > $today_limit && $timestamp < $now) { |
| | | $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 = $RCMAIL->config->get('date_short', 'D H:i'); |
| | | else |
| | | $format = $RCMAIL->config->get('date_long', 'Y-m-d H:i'); |
| | | } |
| | | |
| | | // strftime() format |
| | | if (preg_match('/%[a-z]+/i', $format)) { |
| | | $format = strftime($format, $timestamp); |
| | | |
| | | if ($convert && $stz) { |
| | | date_default_timezone_set($stz); |
| | | } |
| | | |
| | | 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++) { |
| | | if ($format[$i]=='\\') // skip escape chars |
| | | continue; |
| | | |
| | | // write char "as-is" |
| | | if ($format[$i]==' ' || $format{$i-1}=='\\') |
| | | $out .= $format[$i]; |
| | | // weekday (short) |
| | | else if ($format[$i]=='D') |
| | | $out .= rcube_label(strtolower(date('D', $timestamp))); |
| | | // weekday long |
| | | else if ($format[$i]=='l') |
| | | $out .= rcube_label(strtolower(date('l', $timestamp))); |
| | | // month name (short) |
| | | else if ($format[$i]=='M') |
| | | $out .= rcube_label(strtolower(date('M', $timestamp))); |
| | | // month name (long) |
| | | else if ($format[$i]=='F') |
| | | $out .= rcube_label('long'.strtolower(date('M', $timestamp))); |
| | | else if ($format[$i]=='x') |
| | | $out .= strftime('%x %X', $timestamp); |
| | | else |
| | | $out .= date($format[$i], $timestamp); |
| | | } |
| | | |
| | | 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; |
| | | } |
| | | } |
| | | |
| | | if ($convert && $stz) { |
| | | date_default_timezone_set($stz); |
| | | } |
| | | |
| | | return $out; |
| | | return rcube_ui::format_date($date, $format, $convert); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Compose a valid representation of name and e-mail address |
| | | * |
| | | * @param string E-mail address |
| | | * @param string Person name |
| | | * @return string Formatted string |
| | | */ |
| | | function format_email_recipient($email, $name='') |
| | | { |
| | | 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)); |
| | | } |
| | | |
| | | return trim($email); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return the mailboxlist in HTML |
| | | * |
| | | * @param array Named parameters |
| | | * @return string HTML code for the gui object |
| | | */ |
| | | function rcmail_mailbox_list($attrib) |
| | | { |
| | | global $RCMAIL; |
| | | static $a_mailboxes; |
| | | |
| | | $attrib += array('maxlength' => 100, 'realnames' => false, 'unreadwrap' => ' (%s)'); |
| | | |
| | | // 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->storage->get_folder(); |
| | | |
| | | // build the folders tree |
| | | if (empty($a_mailboxes)) { |
| | | // get mailbox list |
| | | $a_folders = $RCMAIL->storage->list_folders_subscribed('', $attrib['folder_name'], $attrib['folder_filter']); |
| | | $delimiter = $RCMAIL->storage->get_hierarchy_delimiter(); |
| | | $a_mailboxes = array(); |
| | | |
| | | foreach ($a_folders as $folder) |
| | | rcmail_build_folder_tree($a_mailboxes, $folder, $delimiter); |
| | | } |
| | | |
| | | // 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, |
| | | 'type' => $type, |
| | | 'attribs' => $attrib, |
| | | )); |
| | | |
| | | $a_mailboxes = $hook['list']; |
| | | $attrib = $hook['attribs']; |
| | | |
| | | if ($type == 'select') { |
| | | $select = new html_select($attrib); |
| | | |
| | | // add no-selection option |
| | | if ($attrib['noselection']) |
| | | $select->add(rcube_label($attrib['noselection']), ''); |
| | | |
| | | rcmail_render_folder_tree_select($a_mailboxes, $mbox_name, $attrib['maxlength'], $select, $attrib['realnames']); |
| | | $out = $select->show($attrib['default']); |
| | | } |
| | | else { |
| | | $js_mailboxlist = array(); |
| | | $out = html::tag('ul', $attrib, rcmail_render_folder_tree_html($a_mailboxes, $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('unreadwrap', $attrib['unreadwrap']); |
| | | $RCMAIL->output->set_env('collapsed_folders', (string)$RCMAIL->config->get('collapsed_folders')); |
| | | } |
| | | |
| | | return $out; |
| | | return rcube_ui::folder_list($attrib); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return the mailboxlist as html_select object |
| | | * |
| | | * @param array Named parameters |
| | | * @return html_select HTML drop-down object |
| | | */ |
| | | function rcmail_mailbox_select($p = array()) |
| | | function rcmail_mailbox_select($attrib = array()) |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | $p += array('maxlength' => 100, 'realnames' => false); |
| | | $a_mailboxes = array(); |
| | | $storage = $RCMAIL->get_storage(); |
| | | |
| | | if (empty($p['folder_name'])) { |
| | | $p['folder_name'] = '*'; |
| | | } |
| | | |
| | | if ($p['unsubscribed']) |
| | | $list = $storage->list_folders('', $p['folder_name'], $p['folder_filter'], $p['folder_rights']); |
| | | else |
| | | $list = $storage->list_folders_subscribed('', $p['folder_name'], $p['folder_filter'], $p['folder_rights']); |
| | | |
| | | $delimiter = $storage->get_hierarchy_delimiter(); |
| | | |
| | | foreach ($list as $folder) { |
| | | if (empty($p['exceptions']) || !in_array($folder, $p['exceptions'])) |
| | | 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'], 0, $p); |
| | | |
| | | return $select; |
| | | return rcube_ui::folder_selector($attrib); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create a hierarchical array of the mailbox list |
| | | * @access private |
| | | * @return void |
| | | */ |
| | | function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='') |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | // Handle namespace prefix |
| | | $prefix = ''; |
| | | if (!$path) { |
| | | $n_folder = $folder; |
| | | $folder = $RCMAIL->storage->mod_folder($folder); |
| | | |
| | | if ($n_folder != $folder) { |
| | | $prefix = substr($n_folder, 0, -strlen($folder)); |
| | | } |
| | | } |
| | | |
| | | $pos = strpos($folder, $delm); |
| | | |
| | | if ($pos !== false) { |
| | | $subFolders = substr($folder, $pos+1); |
| | | $currentFolder = substr($folder, 0, $pos); |
| | | |
| | | // sometimes folder has a delimiter as the last character |
| | | if (!strlen($subFolders)) |
| | | $virtual = false; |
| | | else if (!isset($arrFolders[$currentFolder])) |
| | | $virtual = true; |
| | | else |
| | | $virtual = $arrFolders[$currentFolder]['virtual']; |
| | | } |
| | | else { |
| | | $subFolders = false; |
| | | $currentFolder = $folder; |
| | | $virtual = false; |
| | | } |
| | | |
| | | $path .= $prefix.$currentFolder; |
| | | |
| | | if (!isset($arrFolders[$currentFolder])) { |
| | | $arrFolders[$currentFolder] = array( |
| | | 'id' => $path, |
| | | 'name' => rcube_charset_convert($currentFolder, 'UTF7-IMAP'), |
| | | 'virtual' => $virtual, |
| | | 'folders' => array()); |
| | | } |
| | | else |
| | | $arrFolders[$currentFolder]['virtual'] = $virtual; |
| | | |
| | | 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 |
| | | * @access private |
| | | * @return string |
| | | */ |
| | | 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->storage->get_cache('messagecount'); |
| | | |
| | | $out = ''; |
| | | foreach ($arrFolders as $key => $folder) { |
| | | $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 && !$realnames) { |
| | | $foldername = rcube_label($folder_class); |
| | | } |
| | | else { |
| | | $foldername = $folder['name']; |
| | | |
| | | // shorten the folder name to a given length |
| | | if ($maxlength && $maxlength > 1) { |
| | | $fname = abbreviate_string($foldername, $maxlength); |
| | | if ($fname != $foldername) |
| | | $title = $foldername; |
| | | $foldername = $fname; |
| | | } |
| | | } |
| | | |
| | | // make folder name safe for ids and class names |
| | | $folder_id = html_identifier($folder['id'], true); |
| | | $classes = array('mailbox'); |
| | | |
| | | // set special class for Sent, Drafts, Trash and Junk |
| | | if ($folder_class) |
| | | $classes[] = $folder_class; |
| | | |
| | | if ($folder['id'] == $mbox_name) |
| | | $classes[] = 'selected'; |
| | | |
| | | if ($folder['virtual']) |
| | | $classes[] = 'virtual'; |
| | | else if ($unread) |
| | | $classes[] = 'unread'; |
| | | |
| | | $js_name = JQ($folder['id']); |
| | | $html_name = Q($foldername) . ($unread ? html::span('unreadcount', sprintf($attrib['unreadwrap'], $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, |
| | | ); |
| | | |
| | | $out .= html::tag('li', array( |
| | | 'id' => "rcmli".$folder_id, |
| | | 'class' => join(' ', $classes), |
| | | 'noclose' => true), |
| | | html::a($link_attrib, $html_name) . |
| | | (!empty($folder['folders']) ? html::div(array( |
| | | 'class' => ($collapsed ? 'collapsed' : 'expanded'), |
| | | '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)); |
| | | } |
| | | |
| | | $out .= "</li>\n"; |
| | | } |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return html for a flat list <select> for the mailbox tree |
| | | * @access private |
| | | * @return string |
| | | */ |
| | | 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) { |
| | | // 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->storage->folder_attributes($folder['id'])) |
| | | && in_array('\\Noinferiors', $attrs) |
| | | ) { |
| | | continue; |
| | | } |
| | | |
| | | if (!$realnames && ($folder_class = rcmail_folder_classname($folder['id']))) |
| | | $foldername = rcube_label($folder_class); |
| | | 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, $opts); |
| | | } |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return internal name for the given folder if it matches the configured special folders |
| | | * @access private |
| | | * @return string |
| | | */ |
| | | function rcmail_folder_classname($folder_id) |
| | | { |
| | | global $CONFIG; |
| | | |
| | | if ($folder_id == 'INBOX') |
| | | return 'inbox'; |
| | | |
| | | // for these mailboxes we have localized labels and css classes |
| | | foreach (array('sent', 'drafts', 'trash', 'junk') as $smbx) |
| | | { |
| | | if ($folder_id == $CONFIG[$smbx.'_mbox']) |
| | | return $smbx; |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Try to localize the given IMAP folder name. |
| | | * UTF-7 decode it in case no localized text was found |
| | | * |
| | | * @param string Folder name |
| | | * @return string Localized folder name in UTF-8 encoding |
| | | */ |
| | | function rcmail_localize_foldername($name) |
| | | { |
| | | if ($folder_class = rcmail_folder_classname($name)) |
| | | return rcube_label($folder_class); |
| | | else |
| | | return rcube_charset_convert($name, 'UTF7-IMAP'); |
| | | return rcube_ui::localize_foldername($name); |
| | | } |
| | | |
| | | |
| | | function rcmail_localize_folderpath($path) |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | $protect_folders = $RCMAIL->config->get('protect_default_folders'); |
| | | $default_folders = (array) $RCMAIL->config->get('default_folders'); |
| | | $delimiter = $RCMAIL->storage->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); |
| | | return rcube_ui::localize_folderpath($path); |
| | | } |
| | | |
| | | |
| | | function rcmail_quota_display($attrib) |
| | | { |
| | | global $OUTPUT; |
| | | |
| | | if (!$attrib['id']) |
| | | $attrib['id'] = 'rcmquotadisplay'; |
| | | |
| | | $_SESSION['quota_display'] = !empty($attrib['display']) ? $attrib['display'] : 'text'; |
| | | |
| | | $OUTPUT->add_gui_object('quotadisplay', $attrib['id']); |
| | | |
| | | $quota = rcmail_quota_content($attrib); |
| | | |
| | | $OUTPUT->add_script('rcmail.set_quota('.json_serialize($quota).');', 'docready'); |
| | | |
| | | return html::span($attrib, ''); |
| | | return rcube_ui::quota_display($attrib); |
| | | } |
| | | |
| | | |
| | | function rcmail_quota_content($attrib=NULL) |
| | | function rcmail_quota_content($attrib = null) |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | $quota = $RCMAIL->storage->get_quota(); |
| | | $quota = $RCMAIL->plugins->exec_hook('quota', $quota); |
| | | |
| | | $quota_result = (array) $quota; |
| | | $quota_result['type'] = isset($_SESSION['quota_display']) ? $_SESSION['quota_display'] : ''; |
| | | |
| | | if (!$quota['total'] && $RCMAIL->config->get('quota_zero_as_unlimited')) { |
| | | $quota_result['title'] = rcube_label('unlimited'); |
| | | $quota_result['percent'] = 0; |
| | | } |
| | | else if ($quota['total']) { |
| | | if (!isset($quota['percent'])) |
| | | $quota_result['percent'] = min(100, round(($quota['used']/max(1,$quota['total']))*100)); |
| | | |
| | | $title = sprintf('%s / %s (%.0f%%)', |
| | | show_bytes($quota['used'] * 1024), show_bytes($quota['total'] * 1024), |
| | | $quota_result['percent']); |
| | | |
| | | $quota_result['title'] = $title; |
| | | |
| | | if ($attrib['width']) |
| | | $quota_result['width'] = $attrib['width']; |
| | | if ($attrib['height']) |
| | | $quota_result['height'] = $attrib['height']; |
| | | } |
| | | else { |
| | | $quota_result['title'] = rcube_label('unknown'); |
| | | $quota_result['percent'] = 0; |
| | | } |
| | | |
| | | return $quota_result; |
| | | return rcube_ui::quota_content($attrib); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Outputs error message according to server error/response codes |
| | | * |
| | | * @param string Fallback message label |
| | | * @param string Fallback message label arguments |
| | | * |
| | | * @return void |
| | | */ |
| | | function rcmail_display_server_error($fallback=null, $fallback_args=null) |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | $err_code = $RCMAIL->storage->get_error_code(); |
| | | $res_code = $RCMAIL->storage->get_response_code(); |
| | | |
| | | if ($err_code < 0) { |
| | | $RCMAIL->output->show_message('storageerror', 'error'); |
| | | } |
| | | else if ($res_code == rcube_storage::NOPERM) { |
| | | $RCMAIL->output->show_message('errornoperm', 'error'); |
| | | } |
| | | else if ($res_code == rcube_storage::READONLY) { |
| | | $RCMAIL->output->show_message('errorreadonly', 'error'); |
| | | } |
| | | else if ($err_code && ($err_str = $RCMAIL->storage->get_error_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); |
| | | } |
| | | |
| | | return true; |
| | | rcube_ui::display_server_error($fallback, $fallback_args); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Generate CSS classes from mimetype and filename extension |
| | | * |
| | | * @param string Mimetype |
| | | * @param string The filename |
| | | * @return string CSS classes separated by space |
| | | */ |
| | | function rcmail_filetype2classname($mimetype, $filename) |
| | | { |
| | | list($primary, $secondary) = explode('/', $mimetype); |
| | | |
| | | $classes = array($primary ? $primary : 'unknown'); |
| | | if ($secondary) { |
| | | $classes[] = $secondary; |
| | | } |
| | | if (preg_match('/\.([a-z0-9]+)$/i', $filename, $m)) { |
| | | $classes[] = $m[1]; |
| | | } |
| | | |
| | | return strtolower(join(" ", $classes)); |
| | | return rcube_ui::file2class($mimetype, $filename); |
| | | } |
| | | |
| | | /** |
| | | * Output HTML editor scripts |
| | | * |
| | | * @param string Editor mode |
| | | * @return void |
| | | */ |
| | | function rcube_html_editor($mode='') |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | $hook = $RCMAIL->plugins->exec_hook('html_editor', array('mode' => $mode)); |
| | | |
| | | if ($hook['abort']) |
| | | return; |
| | | |
| | | $lang = strtolower($_SESSION['language']); |
| | | |
| | | // TinyMCE uses two-letter lang codes, with exception of Chinese |
| | | if (strpos($lang, 'zh_') === 0) |
| | | $lang = str_replace('_', '-', $lang); |
| | | else |
| | | $lang = substr($lang, 0, 2); |
| | | |
| | | if (!file_exists(INSTALL_PATH . 'program/js/tiny_mce/langs/'.$lang.'.js')) |
| | | $lang = 'en'; |
| | | |
| | | $RCMAIL->output->include_script('tiny_mce/tiny_mce.js'); |
| | | $RCMAIL->output->include_script('editor.js'); |
| | | $RCMAIL->output->add_script(sprintf("rcmail_editor_init(%s)", |
| | | json_encode(array( |
| | | 'mode' => $mode, |
| | | 'lang' => $lang, |
| | | 'skin_path' => $RCMAIL->output->get_skin_path(), |
| | | 'spellcheck' => intval($RCMAIL->config->get('enable_spellcheck')), |
| | | 'spelldict' => intval($RCMAIL->config->get('spellcheck_dictionary')), |
| | | ))), 'docready'); |
| | | rcube_ui::html_editor($mode); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Replaces TinyMCE's emoticon images with plain-text representation |
| | | * |
| | | * @param string HTML content |
| | | * @return string HTML content |
| | | */ |
| | | function rcmail_replace_emoticons($html) |
| | | { |
| | | $emoticons = array( |
| | | '8-)' => 'smiley-cool', |
| | | ':-#' => 'smiley-foot-in-mouth', |
| | | ':-*' => 'smiley-kiss', |
| | | ':-X' => 'smiley-sealed', |
| | | ':-P' => 'smiley-tongue-out', |
| | | ':-@' => 'smiley-yell', |
| | | ":'(" => 'smiley-cry', |
| | | ':-(' => 'smiley-frown', |
| | | ':-D' => 'smiley-laughing', |
| | | ':-)' => 'smiley-smile', |
| | | ':-S' => 'smiley-undecided', |
| | | ':-$' => 'smiley-embarassed', |
| | | 'O:-)' => 'smiley-innocent', |
| | | ':-|' => 'smiley-money-mouth', |
| | | ':-O' => 'smiley-surprised', |
| | | ';-)' => 'smiley-wink', |
| | | ); |
| | | |
| | | foreach ($emoticons as $idx => $file) { |
| | | // <img title="Cry" src="http://.../program/js/tiny_mce/plugins/emotions/img/smiley-cry.gif" border="0" alt="Cry" /> |
| | | $search[] = '/<img title="[a-z ]+" src="https?:\/\/[a-z0-9_.\/-]+\/tiny_mce\/plugins\/emotions\/img\/'.$file.'.gif"[^>]+\/>/i'; |
| | | $replace[] = $idx; |
| | | } |
| | | |
| | | return preg_replace($search, $replace, $html); |
| | | return rcube_ui::replace_emoticons($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; |
| | | return rcmail::get_instance()->deliver_message($message, $from, $mailto, $smtp_error, $body_file, $smtp_opts); |
| | | } |
| | | |
| | | |
| | | // 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); |
| | | return rcmail::get_instance()->gen_message_id(); |
| | | } |
| | | |
| | | |
| | | // Returns RFC2822 formatted current date in user's timezone |
| | | function rcmail_user_date() |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | // get user's timezone |
| | | try { |
| | | $tz = new DateTimeZone($RCMAIL->config->get('timezone')); |
| | | $date = new DateTime('now', $tz); |
| | | } |
| | | catch (Exception $e) { |
| | | $date = new DateTime(); |
| | | } |
| | | |
| | | return $date->format('r'); |
| | | return rcmail::get_instance()->user_date(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Check if we can process not exceeding memory_limit |
| | | * |
| | | * @param integer Required amount of memory |
| | | * @return boolean |
| | | */ |
| | | function rcmail_mem_check($need) |
| | | { |
| | | $mem_limit = parse_bytes(ini_get('memory_limit')); |
| | | $memory = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB |
| | | |
| | | return $mem_limit > 0 && $memory + $need > $mem_limit ? false : true; |
| | | return rcube_ui::mem_check($need); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Check if working in SSL mode |
| | | * |
| | | * @param integer HTTPS port number |
| | | * @param boolean Enables 'use_https' option checking |
| | | * @return boolean |
| | | */ |
| | | function rcube_https_check($port=null, $use_https=true) |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) != 'off') |
| | | return true; |
| | | if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https') |
| | | return true; |
| | | if ($port && $_SERVER['SERVER_PORT'] == $port) |
| | | return true; |
| | | if ($use_https && isset($RCMAIL) && $RCMAIL->config->get('use_https')) |
| | | return true; |
| | | |
| | | return false; |
| | | return rcube_ui::https_check($port, $use_https); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * For backward compatibility. |
| | | * |
| | | * @global rcmail $RCMAIL |
| | | * @param string $var_name Variable name. |
| | | * @return void |
| | | */ |
| | | function rcube_sess_unset($var_name=null) |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | $RCMAIL->session->remove($var_name); |
| | | rcmail::get_instance()->session->remove($var_name); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Replaces hostname variables |
| | | * |
| | | * @param string $name Hostname |
| | | * @param string $host Optional IMAP hostname |
| | | * @return string |
| | | */ |
| | | function rcube_parse_host($name, $host='') |
| | | { |
| | | // %n - host |
| | | $n = preg_replace('/:\d+$/', '', $_SERVER['SERVER_NAME']); |
| | | // %d - domain name without first part, e.g. %n=mail.domain.tld, %d=domain.tld |
| | | $d = preg_replace('/^[^\.]+\./', '', $n); |
| | | // %h - IMAP host |
| | | $h = $_SESSION['storage_host'] ? $_SESSION['storage_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', '%s'), array($n, $d, $h, $z, $s[2]), $name); |
| | | return $name; |
| | | return rcmail::parse_host($name, $host); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * E-mail address validation |
| | | * |
| | | * @param string $email Email address |
| | | * @param boolean $dns_check True to check dns |
| | | * @return boolean |
| | | */ |
| | | function check_email($email, $dns_check=true) |
| | | { |
| | | // Check for invalid characters |
| | | if (preg_match('/[\x00-\x1F\x7F-\xFF]/', $email)) |
| | | return false; |
| | | |
| | | // Check for length limit specified by RFC 5321 (#1486453) |
| | | if (strlen($email) > 254) |
| | | return false; |
| | | |
| | | $email_array = explode('@', $email); |
| | | |
| | | // Check that there's one @ symbol |
| | | if (count($email_array) < 2) |
| | | return false; |
| | | |
| | | $domain_part = array_pop($email_array); |
| | | $local_part = implode('@', $email_array); |
| | | |
| | | // from PEAR::Validate |
| | | $regexp = '&^(?: |
| | | ("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+")| #1 quoted name |
| | | ([-\w!\#\$%\&\'*+~/^`|{}=]+(?:\.[-\w!\#\$%\&\'*+~/^`|{}=]+)*)) #2 OR dot-atom (RFC5322) |
| | | $&xi'; |
| | | |
| | | if (!preg_match($regexp, $local_part)) |
| | | return false; |
| | | |
| | | // Check domain part |
| | | if (preg_match('/^\[*(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}\]*$/', $domain_part)) |
| | | return true; // IP address |
| | | else { |
| | | // If not an IP address |
| | | $domain_array = explode('.', $domain_part); |
| | | if (sizeof($domain_array) < 2) |
| | | return false; // Not enough parts to be a valid domain |
| | | |
| | | foreach ($domain_array as $part) |
| | | if (!preg_match('/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]))$/', $part)) |
| | | return false; |
| | | |
| | | if (!$dns_check || !rcmail::get_instance()->config->get('email_dns_check')) |
| | | return true; |
| | | |
| | | if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' && version_compare(PHP_VERSION, '5.3.0', '<')) { |
| | | $lookup = array(); |
| | | @exec("nslookup -type=MX " . escapeshellarg($domain_part) . " 2>&1", $lookup); |
| | | foreach ($lookup as $line) { |
| | | if (strpos($line, 'MX preference')) |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | // find MX record(s) |
| | | if (getmxrr($domain_part, $mx_records)) |
| | | return true; |
| | | |
| | | // find any DNS record |
| | | if (checkdnsrr($domain_part, 'ANY')) |
| | | return true; |
| | | } |
| | | |
| | | return false; |
| | | return rcmail::get_instance()->check_email($email, $dns_check); |
| | | } |
| | | |
| | | /* |
| | | * 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); |
| | | |
| | | if ($domain === false) { |
| | | return ''; |
| | | } |
| | | |
| | | return $at ? $user . '@' . $domain : $domain; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Helper class to turn relative urls into absolute ones |
| | | * using a predefined base |
| | | */ |
| | | class rcube_base_replacer |
| | | { |
| | | private $base_url; |
| | | |
| | | public function __construct($base) |
| | | { |
| | | $this->base_url = $base; |
| | | } |
| | | |
| | | public function callback($matches) |
| | | { |
| | | 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; |
| | | } |
| | | } |
| | | |
| | | |
| | | /****** 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)); |
| | | call_user_func_array(array('rcmail', 'console'), func_get_args()); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 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; |
| | | return rcmail::write_log($name, $line); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 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 in session %s', |
| | | $RCMAIL->user->get_username(), $RCMAIL->user->ID, rcmail_remote_ip(), session_id())); |
| | | return rcmail::get_instance()->log_login(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 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; |
| | | return rcmail::remote_ip(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 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']; |
| | | return rcmail::check_referer(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * @access private |
| | | * @return mixed |
| | | */ |
| | | function rcube_timer() |
| | | { |
| | | return microtime(true); |
| | | return rcmail::timer(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * @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)); |
| | | rcmail::print_timer($timer, $label, $dest); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Throw system error and show error page |
| | | * |
| | | * @param array Named parameters |
| | | * - code: Error code (required) |
| | | * - type: Error type [php|db|imap|javascript] (required) |
| | | * - message: Error message |
| | | * - file: File where error occured |
| | | * - line: Line where error occured |
| | | * @param boolean True to log the error |
| | | * @param boolean Terminate script execution |
| | | */ |
| | | // may be defined in Installer |
| | | if (!function_exists('raise_error')) { |
| | | function raise_error($arg=array(), $log=false, $terminate=false) |
| | | { |
| | | global $__page_content, $CONFIG, $OUTPUT, $ERROR_CODE, $ERROR_MESSAGE; |
| | | |
| | | // report bug (if not incompatible browser) |
| | | if ($log && $arg['type'] && $arg['message']) |
| | | rcube_log_bug($arg); |
| | | |
| | | // display error page and terminate script |
| | | if ($terminate) { |
| | | $ERROR_CODE = $arg['code']; |
| | | $ERROR_MESSAGE = $arg['message']; |
| | | include INSTALL_PATH . 'program/steps/utils/error.inc'; |
| | | exit; |
| | | } |
| | | } |
| | | rcmail::raise_error($arg, $log, $terminate); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Report error according to configured debug_level |
| | | * |
| | | * @param array Named parameters |
| | | * @return void |
| | | * @see raise_error() |
| | | */ |
| | | 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 ($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, |
| | | $arg_arr['message'], |
| | | $arg_arr['file'] ? sprintf(' in %s on line %d', $arg_arr['file'], $arg_arr['line']) : '', |
| | | $_SERVER['REQUEST_METHOD'], |
| | | $_SERVER['REQUEST_URI'] . $post_query); |
| | | |
| | | if (!write_log('errors', $log_entry)) { |
| | | // send error to PHPs error handler if write_log didn't succeed |
| | | trigger_error($arg_arr['message']); |
| | | } |
| | | } |
| | | |
| | | // 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 ($level & 4) { |
| | | print "<b>$program Error"; |
| | | |
| | | if (!empty($arg_arr['file']) && !empty($arg_arr['line'])) |
| | | print " in $arg_arr[file] ($arg_arr[line])"; |
| | | |
| | | print ':</b> '; |
| | | print nl2br($arg_arr['message']); |
| | | print '<br />'; |
| | | flush(); |
| | | } |
| | | rcmail::log_bug($arg_arr); |
| | | } |
| | | |
| | | function rcube_upload_progress() |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | $prefix = ini_get('apc.rfc1867_prefix'); |
| | | $params = array( |
| | | 'action' => $RCMAIL->action, |
| | | 'name' => get_input_value('_progress', RCUBE_INPUT_GET), |
| | | ); |
| | | |
| | | if (function_exists('apc_fetch')) { |
| | | $status = apc_fetch($prefix . $params['name']); |
| | | |
| | | if (!empty($status)) { |
| | | $status['percent'] = round($status['current']/$status['total']*100); |
| | | $params = array_merge($status, $params); |
| | | } |
| | | } |
| | | |
| | | if (isset($params['percent'])) |
| | | $params['text'] = rcube_label(array('name' => 'uploadprogress', 'vars' => array( |
| | | 'percent' => $params['percent'] . '%', |
| | | 'current' => show_bytes($params['current']), |
| | | 'total' => show_bytes($params['total']) |
| | | ))); |
| | | |
| | | $RCMAIL->output->command('upload_progress_update', $params); |
| | | $RCMAIL->output->send(); |
| | | rcube_ui::upload_progress(); |
| | | } |
| | | |
| | | function rcube_upload_init() |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | // Enable upload progress bar |
| | | if (($seconds = $RCMAIL->config->get('upload_progress')) && ini_get('apc.rfc1867')) { |
| | | if ($field_name = ini_get('apc.rfc1867_name')) { |
| | | $RCMAIL->output->set_env('upload_progress_name', $field_name); |
| | | $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; |
| | | return rcube_ui::upload_init(); |
| | | } |
| | | |
| | | /** |
| | | * 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'); |
| | | rcube_ui::autocomplete_init(); |
| | | } |
| | | |
| | | function rcube_fontdefs($font = null) |
| | | { |
| | | $fonts = array( |
| | | 'Andale Mono' => '"Andale Mono",Times,monospace', |
| | | 'Arial' => 'Arial,Helvetica,sans-serif', |
| | | 'Arial Black' => '"Arial Black","Avant Garde",sans-serif', |
| | | 'Book Antiqua' => '"Book Antiqua",Palatino,serif', |
| | | 'Courier New' => '"Courier New",Courier,monospace', |
| | | 'Georgia' => 'Georgia,Palatino,serif', |
| | | 'Helvetica' => 'Helvetica,Arial,sans-serif', |
| | | 'Impact' => 'Impact,Chicago,sans-serif', |
| | | 'Tahoma' => 'Tahoma,Arial,Helvetica,sans-serif', |
| | | 'Terminal' => 'Terminal,Monaco,monospace', |
| | | 'Times New Roman' => '"Times New Roman",Times,serif', |
| | | 'Trebuchet MS' => '"Trebuchet MS",Geneva,sans-serif', |
| | | 'Verdana' => 'Verdana,Geneva,sans-serif', |
| | | ); |
| | | return rcube_ui::font_defs($font); |
| | | } |
| | | |
| | | if ($font) |
| | | return $fonts[$font]; |
| | | function send_nocacheing_headers() |
| | | { |
| | | return rcmail::get_instance()->output->nocacheing_headers(); |
| | | } |
| | | |
| | | return $fonts; |
| | | function show_bytes($bytes) |
| | | { |
| | | return rcube_ui::show_bytes($bytes); |
| | | } |
| | | |
| | | function rc_wordwrap($string, $width=75, $break="\n", $cut=false) |
| | | { |
| | | return rcube_mime::wordwrap($string, $width, $break, $cut); |
| | | } |
| | | |
| | | function rc_request_header($name) |
| | | { |
| | | return rcube_request_header($name); |
| | | } |
| | | |
| | | function rc_mime_content_type($path, $name, $failover = 'application/octet-stream', $is_stream=false) |
| | | { |
| | | return rcube_mime::file_content_type($path, $name, $failover, $is_stream); |
| | | } |
| | | |
| | | function rc_image_content_type($data) |
| | | { |
| | | return rcube_mime::image_content_type($data); |
| | | } |
| | |
| | | | program/include/rcmail.php | |
| | | | | |
| | | | This file is part of the Roundcube Webmail client | |
| | | | Copyright (C) 2008-2011, The Roundcube Dev Team | |
| | | | Copyright (C) 2011, Kolab Systems AG | |
| | | | Copyright (C) 2008-2012, The Roundcube Dev Team | |
| | | | Copyright (C) 2011-2012, Kolab Systems AG | |
| | | | | |
| | | | Licensed under the GNU General Public License version 3 or | |
| | | | any later version with exceptions for skins & plugins. | |
| | |
| | | * |
| | | * @package Core |
| | | */ |
| | | class rcmail |
| | | class rcmail extends rcube |
| | | { |
| | | /** |
| | | * Main tasks. |
| | |
| | | * @var array |
| | | */ |
| | | static public $main_tasks = array('mail','settings','addressbook','login','logout','utils','dummy'); |
| | | |
| | | /** |
| | | * Singleton instace of rcmail |
| | | * |
| | | * @var rcmail |
| | | */ |
| | | static private $instance; |
| | | |
| | | /** |
| | | * Stores instance of rcube_config. |
| | | * |
| | | * @var rcube_config |
| | | */ |
| | | public $config; |
| | | |
| | | /** |
| | | * Stores rcube_user instance. |
| | | * |
| | | * @var rcube_user |
| | | */ |
| | | public $user; |
| | | |
| | | /** |
| | | * Instace of database class. |
| | | * |
| | | * @var rcube_mdb2 |
| | | */ |
| | | public $db; |
| | | |
| | | /** |
| | | * Instace of Memcache class. |
| | | * |
| | | * @var rcube_mdb2 |
| | | */ |
| | | public $memcache; |
| | | |
| | | /** |
| | | * Instace of rcube_session class. |
| | | * |
| | | * @var rcube_session |
| | | */ |
| | | public $session; |
| | | |
| | | /** |
| | | * Instance of rcube_smtp class. |
| | | * |
| | | * @var rcube_smtp |
| | | */ |
| | | public $smtp; |
| | | |
| | | /** |
| | | * Instance of rcube_storage class. |
| | | * |
| | | * @var rcube_storage |
| | | */ |
| | | public $storage; |
| | | |
| | | /** |
| | | * Instance of rcube_template class. |
| | | * |
| | | * @var rcube_template |
| | | */ |
| | | public $output; |
| | | |
| | | /** |
| | | * Instance of rcube_plugin_api. |
| | | * |
| | | * @var rcube_plugin_api |
| | | */ |
| | | public $plugins; |
| | | |
| | | /** |
| | | * Current task. |
| | |
| | | public $action = ''; |
| | | public $comm_path = './'; |
| | | |
| | | private $texts; |
| | | private $address_books = array(); |
| | | private $caches = array(); |
| | | private $action_map = array(); |
| | | private $shutdown_functions = array(); |
| | | private $expunge_cache = false; |
| | | |
| | | |
| | | /** |
| | |
| | | */ |
| | | static function get_instance() |
| | | { |
| | | if (!self::$instance) { |
| | | if (!self::$instance || !is_a(self::$instance, 'rcmail')) { |
| | | self::$instance = new rcmail(); |
| | | self::$instance->startup(); // init AFTER object was linked with self::$instance |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * Private constructor |
| | | */ |
| | | private function __construct() |
| | | { |
| | | // load configuration |
| | | $this->config = new rcube_config(); |
| | | |
| | | register_shutdown_function(array($this, 'shutdown')); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initial startup function |
| | | * to register session, create database and imap connections |
| | | */ |
| | | private function startup() |
| | | protected function startup() |
| | | { |
| | | // initialize syslog |
| | | if ($this->config->get('log_driver') == 'syslog') { |
| | | $syslog_id = $this->config->get('syslog_id', 'roundcube'); |
| | | $syslog_facility = $this->config->get('syslog_facility', LOG_USER); |
| | | openlog($syslog_id, LOG_ODELAY, $syslog_facility); |
| | | } |
| | | |
| | | // connect to database |
| | | $this->get_dbh(); |
| | | $this->init(self::INIT_WITH_DB | self::INIT_WITH_PLUGINS); |
| | | |
| | | // start session |
| | | $this->session_init(); |
| | |
| | | $this->session_configure(); |
| | | |
| | | // set task and action properties |
| | | $this->set_task(get_input_value('_task', RCUBE_INPUT_GPC)); |
| | | $this->action = asciiwords(get_input_value('_action', RCUBE_INPUT_GPC)); |
| | | $this->set_task(rcube_ui::get_input_value('_task', rcube_ui::INPUT_GPC)); |
| | | $this->action = asciiwords(rcube_ui::get_input_value('_action', rcube_ui::INPUT_GPC)); |
| | | |
| | | // reset some session parameters when changing task |
| | | if ($this->task != 'utils') { |
| | |
| | | else |
| | | $GLOBALS['OUTPUT'] = $this->load_gui(!empty($_REQUEST['_framed'])); |
| | | |
| | | // create plugin API and load plugins |
| | | $this->plugins = rcube_plugin_api::get_instance(); |
| | | |
| | | // init plugins |
| | | $this->plugins->init(); |
| | | // load plugins |
| | | $this->plugins->init($this, $this->task); |
| | | $this->plugins->load_plugins((array)$this->config->get('plugins', array()), array('filesystem_attachments', 'jqueryui')); |
| | | } |
| | | |
| | | |
| | |
| | | |
| | | |
| | | /** |
| | | * Check the given string and return a valid language code |
| | | * |
| | | * @param string Language code |
| | | * @return string Valid language code |
| | | */ |
| | | private function language_prop($lang) |
| | | { |
| | | static $rcube_languages, $rcube_language_aliases; |
| | | |
| | | // user HTTP_ACCEPT_LANGUAGE if no language is specified |
| | | if (empty($lang) || $lang == 'auto') { |
| | | $accept_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']); |
| | | $lang = str_replace('-', '_', $accept_langs[0]); |
| | | } |
| | | |
| | | if (empty($rcube_languages)) { |
| | | @include(INSTALL_PATH . 'program/localization/index.inc'); |
| | | } |
| | | |
| | | // check if we have an alias for that language |
| | | if (!isset($rcube_languages[$lang]) && isset($rcube_language_aliases[$lang])) { |
| | | $lang = $rcube_language_aliases[$lang]; |
| | | } |
| | | // try the first two chars |
| | | else if (!isset($rcube_languages[$lang])) { |
| | | $short = substr($lang, 0, 2); |
| | | |
| | | // check if we have an alias for the short language code |
| | | if (!isset($rcube_languages[$short]) && isset($rcube_language_aliases[$short])) { |
| | | $lang = $rcube_language_aliases[$short]; |
| | | } |
| | | // expand 'nn' to 'nn_NN' |
| | | else if (!isset($rcube_languages[$short])) { |
| | | $lang = $short.'_'.strtoupper($short); |
| | | } |
| | | } |
| | | |
| | | if (!isset($rcube_languages[$lang]) || !is_dir(INSTALL_PATH . 'program/localization/' . $lang)) { |
| | | $lang = 'en_US'; |
| | | } |
| | | |
| | | return $lang; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get the current database connection |
| | | * |
| | | * @return rcube_mdb2 Database connection object |
| | | */ |
| | | public function get_dbh() |
| | | { |
| | | if (!$this->db) { |
| | | $config_all = $this->config->all(); |
| | | |
| | | $this->db = new rcube_mdb2($config_all['db_dsnw'], $config_all['db_dsnr'], $config_all['db_persistent']); |
| | | $this->db->sqlite_initials = INSTALL_PATH . 'SQL/sqlite.initial.sql'; |
| | | $this->db->set_debug((bool)$config_all['sql_debug']); |
| | | } |
| | | |
| | | return $this->db; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get global handle for memcache access |
| | | * |
| | | * @return object Memcache |
| | | */ |
| | | public function get_memcache() |
| | | { |
| | | if (!isset($this->memcache)) { |
| | | // no memcache support in PHP |
| | | if (!class_exists('Memcache')) { |
| | | $this->memcache = false; |
| | | return false; |
| | | } |
| | | |
| | | $this->memcache = new Memcache; |
| | | $this->mc_available = 0; |
| | | |
| | | // add alll configured hosts to pool |
| | | $pconnect = $this->config->get('memcache_pconnect', true); |
| | | foreach ($this->config->get('memcache_hosts', array()) as $host) { |
| | | list($host, $port) = explode(':', $host); |
| | | if (!$port) $port = 11211; |
| | | $this->mc_available += intval($this->memcache->addServer($host, $port, $pconnect, 1, 1, 15, false, array($this, 'memcache_failure'))); |
| | | } |
| | | |
| | | // test connection and failover (will result in $this->mc_available == 0 on complete failure) |
| | | $this->memcache->increment('__CONNECTIONTEST__', 1); // NOP if key doesn't exist |
| | | |
| | | if (!$this->mc_available) |
| | | $this->memcache = false; |
| | | } |
| | | |
| | | return $this->memcache; |
| | | } |
| | | |
| | | /** |
| | | * Callback for memcache failure |
| | | */ |
| | | public function memcache_failure($host, $port) |
| | | { |
| | | static $seen = array(); |
| | | |
| | | // only report once |
| | | if (!$seen["$host:$port"]++) { |
| | | $this->mc_available--; |
| | | raise_error(array('code' => 604, 'type' => 'db', |
| | | 'line' => __LINE__, 'file' => __FILE__, |
| | | 'message' => "Memcache failure on host $host:$port"), |
| | | true, false); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initialize and get cache object |
| | | * |
| | | * @param string $name Cache identifier |
| | | * @param string $type Cache type ('db', 'apc' or 'memcache') |
| | | * @param int $ttl Expiration time for cache items in seconds |
| | | * @param bool $packed Enables/disables data serialization |
| | | * |
| | | * @return rcube_cache Cache object |
| | | */ |
| | | public function get_cache($name, $type='db', $ttl=0, $packed=true) |
| | | { |
| | | if (!isset($this->caches[$name])) { |
| | | $this->caches[$name] = new rcube_cache($type, $_SESSION['user_id'], $name, $ttl, $packed); |
| | | } |
| | | |
| | | return $this->caches[$name]; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return instance of the internal address book class |
| | | * |
| | | * @param string Address book identifier |
| | |
| | | $contacts = new rcube_ldap($ldap_config[$id], $this->config->get('ldap_debug'), $this->config->mail_domain($_SESSION['storage_host'])); |
| | | } |
| | | else if ($id === '0') { |
| | | $contacts = new rcube_contacts($this->db, $this->user->ID); |
| | | $contacts = new rcube_contacts($this->db, $this->get_user_id()); |
| | | } |
| | | else { |
| | | $plugin = $this->plugins->exec_hook('addressbook_get', array('id' => $id, 'writeable' => $writeable)); |
| | |
| | | } |
| | | |
| | | if (!$contacts) { |
| | | raise_error(array( |
| | | self::raise_error(array( |
| | | 'code' => 700, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Addressbook source ($id) not found!"), |
| | |
| | | // We are using the DB address book |
| | | if ($abook_type != 'ldap') { |
| | | if (!isset($this->address_books['0'])) |
| | | $this->address_books['0'] = new rcube_contacts($this->db, $this->user->ID); |
| | | $this->address_books['0'] = new rcube_contacts($this->db, $this->get_user_id()); |
| | | $list['0'] = array( |
| | | 'id' => '0', |
| | | 'name' => rcube_label('personaladrbook'), |
| | | 'name' => $this->gettext('personaladrbook'), |
| | | 'groups' => $this->address_books['0']->groups, |
| | | 'readonly' => $this->address_books['0']->readonly, |
| | | 'autocomplete' => in_array('sql', $autocomplete), |
| | |
| | | * environment vars according to the current session and configuration |
| | | * |
| | | * @param boolean True if this request is loaded in a (i)frame |
| | | * @return rcube_template Reference to HTML output object |
| | | * @return rcube_output_html Reference to HTML output object |
| | | */ |
| | | public function load_gui($framed = false) |
| | | { |
| | | // init output page |
| | | if (!($this->output instanceof rcube_template)) |
| | | $this->output = new rcube_template($this->task, $framed); |
| | | if (!($this->output instanceof rcube_output_html)) |
| | | $this->output = new rcube_output_html($this->task, $framed); |
| | | |
| | | // set keep-alive/check-recent interval |
| | | if ($this->session && ($keep_alive = $this->session->get_keep_alive())) { |
| | |
| | | /** |
| | | * Create an output object for JSON responses |
| | | * |
| | | * @return rcube_json_output Reference to JSON output object |
| | | * @return rcube_output_json Reference to JSON output object |
| | | */ |
| | | public function json_init() |
| | | { |
| | | if (!($this->output instanceof rcube_json_output)) |
| | | $this->output = new rcube_json_output($this->task); |
| | | if (!($this->output instanceof rcube_output_json)) |
| | | $this->output = new rcube_output_json($this->task); |
| | | |
| | | return $this->output; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create SMTP object and connect to server |
| | | * |
| | | * @param boolean True if connection should be established |
| | | */ |
| | | public function smtp_init($connect = false) |
| | | { |
| | | $this->smtp = new rcube_smtp(); |
| | | |
| | | if ($connect) |
| | | $this->smtp->connect(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initialize and get storage object |
| | | * |
| | | * @return rcube_storage Storage object |
| | | */ |
| | | public function get_storage() |
| | | { |
| | | // already initialized |
| | | if (!is_object($this->storage)) { |
| | | $this->storage_init(); |
| | | } |
| | | |
| | | return $this->storage; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Connect to the IMAP server with stored session data. |
| | | * |
| | | * @return bool True on success, False on error |
| | | * @deprecated |
| | | */ |
| | | public function imap_connect() |
| | | { |
| | | return $this->storage_connect(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initialize IMAP object. |
| | | * |
| | | * @deprecated |
| | | */ |
| | | public function imap_init() |
| | | { |
| | | $this->storage_init(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initialize storage object |
| | | */ |
| | | public function storage_init() |
| | | { |
| | | // already initialized |
| | | if (is_object($this->storage)) { |
| | | return; |
| | | } |
| | | |
| | | $driver = $this->config->get('storage_driver', 'imap'); |
| | | $driver_class = "rcube_{$driver}"; |
| | | |
| | | if (!class_exists($driver_class)) { |
| | | raise_error(array( |
| | | 'code' => 700, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Storage driver class ($driver) not found!"), |
| | | true, true); |
| | | } |
| | | |
| | | // Initialize storage object |
| | | $this->storage = new $driver_class; |
| | | |
| | | // for backward compat. (deprecated, will be removed) |
| | | $this->imap = $this->storage; |
| | | |
| | | // enable caching of mail data |
| | | $storage_cache = $this->config->get("{$driver}_cache"); |
| | | $messages_cache = $this->config->get('messages_cache'); |
| | | // for backward compatybility |
| | | if ($storage_cache === null && $messages_cache === null && $this->config->get('enable_caching')) { |
| | | $storage_cache = 'db'; |
| | | $messages_cache = true; |
| | | } |
| | | |
| | | if ($storage_cache) |
| | | $this->storage->set_caching($storage_cache); |
| | | if ($messages_cache) |
| | | $this->storage->set_messages_caching(true); |
| | | |
| | | // set pagesize from config |
| | | $pagesize = $this->config->get('mail_pagesize'); |
| | | if (!$pagesize) { |
| | | $pagesize = $this->config->get('pagesize', 50); |
| | | } |
| | | $this->storage->set_pagesize($pagesize); |
| | | |
| | | // set class options |
| | | $options = array( |
| | | 'auth_type' => $this->config->get("{$driver}_auth_type", 'check'), |
| | | 'auth_cid' => $this->config->get("{$driver}_auth_cid"), |
| | | 'auth_pw' => $this->config->get("{$driver}_auth_pw"), |
| | | 'debug' => (bool) $this->config->get("{$driver}_debug"), |
| | | 'force_caps' => (bool) $this->config->get("{$driver}_force_caps"), |
| | | 'timeout' => (int) $this->config->get("{$driver}_timeout"), |
| | | 'skip_deleted' => (bool) $this->config->get('skip_deleted'), |
| | | 'driver' => $driver, |
| | | ); |
| | | |
| | | if (!empty($_SESSION['storage_host'])) { |
| | | $options['host'] = $_SESSION['storage_host']; |
| | | $options['user'] = $_SESSION['username']; |
| | | $options['port'] = $_SESSION['storage_port']; |
| | | $options['ssl'] = $_SESSION['storage_ssl']; |
| | | $options['password'] = $this->decrypt($_SESSION['password']); |
| | | // set 'imap_host' for backwards compatibility |
| | | $_SESSION[$driver.'_host'] = &$_SESSION['storage_host']; |
| | | } |
| | | |
| | | $options = $this->plugins->exec_hook("storage_init", $options); |
| | | |
| | | $this->storage->set_options($options); |
| | | $this->set_storage_prop(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Connect to the mail storage server with stored session data |
| | | * |
| | | * @return bool True on success, False on error |
| | | */ |
| | | public function storage_connect() |
| | | { |
| | | $storage = $this->get_storage(); |
| | | |
| | | if ($_SESSION['storage_host'] && !$storage->is_connected()) { |
| | | $host = $_SESSION['storage_host']; |
| | | $user = $_SESSION['username']; |
| | | $port = $_SESSION['storage_port']; |
| | | $ssl = $_SESSION['storage_ssl']; |
| | | $pass = $this->decrypt($_SESSION['password']); |
| | | |
| | | if (!$storage->connect($host, $user, $pass, $port, $ssl)) { |
| | | if ($this->output) |
| | | $this->output->show_message($storage->get_error_code() == -1 ? 'storageerror' : 'sessionerror', 'error'); |
| | | } |
| | | else { |
| | | $this->set_storage_prop(); |
| | | } |
| | | } |
| | | |
| | | return $storage->is_connected(); |
| | | } |
| | | |
| | | |
| | |
| | | ini_set('session.gc_maxlifetime', $lifetime * 2); |
| | | } |
| | | |
| | | ini_set('session.cookie_secure', rcube_https_check()); |
| | | ini_set('session.cookie_secure', rcube_ui::https_check()); |
| | | ini_set('session.name', $sess_name ? $sess_name : 'roundcube_sessid'); |
| | | ini_set('session.use_cookies', 1); |
| | | ini_set('session.use_only_cookies', 1); |
| | |
| | | // use database for storing session data |
| | | $this->session = new rcube_session($this->get_dbh(), $this->config); |
| | | |
| | | $this->session->register_gc_handler('rcmail_temp_gc'); |
| | | $this->session->register_gc_handler(array($this, 'temp_gc')); |
| | | $this->session->register_gc_handler(array($this, 'cache_gc')); |
| | | |
| | | // start PHP session (if not in CLI mode) |
| | |
| | | if (!$allowed) |
| | | return false; |
| | | } |
| | | else if (!empty($config['default_host']) && $host != rcube_parse_host($config['default_host'])) |
| | | else if (!empty($config['default_host']) && $host != self::parse_host($config['default_host'])) |
| | | return false; |
| | | |
| | | // parse $host URL |
| | |
| | | // Check if we need to add domain |
| | | if (!empty($config['username_domain']) && strpos($username, '@') === false) { |
| | | if (is_array($config['username_domain']) && isset($config['username_domain'][$host])) |
| | | $username .= '@'.rcube_parse_host($config['username_domain'][$host], $host); |
| | | $username .= '@'.self::parse_host($config['username_domain'][$host], $host); |
| | | else if (is_string($config['username_domain'])) |
| | | $username .= '@'.rcube_parse_host($config['username_domain'], $host); |
| | | $username .= '@'.self::parse_host($config['username_domain'], $host); |
| | | } |
| | | |
| | | // Convert username to lowercase. If storage backend |
| | |
| | | $user = $created; |
| | | } |
| | | else { |
| | | raise_error(array( |
| | | self::raise_error(array( |
| | | 'code' => 620, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Failed to create a user record. Maybe aborted by a plugin?" |
| | |
| | | } |
| | | } |
| | | else { |
| | | raise_error(array( |
| | | self::raise_error(array( |
| | | 'code' => 621, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Access denied for new user $username. 'auto_create_user' is disabled" |
| | |
| | | |
| | | |
| | | /** |
| | | * Set storage parameters. |
| | | * This must be done AFTER connecting to the server! |
| | | */ |
| | | private function set_storage_prop() |
| | | { |
| | | $storage = $this->get_storage(); |
| | | |
| | | $storage->set_charset($this->config->get('default_charset', RCMAIL_CHARSET)); |
| | | |
| | | if ($default_folders = $this->config->get('default_folders')) { |
| | | $storage->set_default_folders($default_folders); |
| | | } |
| | | if (isset($_SESSION['mbox'])) { |
| | | $storage->set_folder($_SESSION['mbox']); |
| | | } |
| | | if (isset($_SESSION['page'])) { |
| | | $storage->set_page($_SESSION['page']); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Auto-select IMAP host based on the posted login information |
| | | * |
| | | * @return string Selected IMAP host |
| | |
| | | $host = null; |
| | | |
| | | if (is_array($default_host)) { |
| | | $post_host = get_input_value('_host', RCUBE_INPUT_POST); |
| | | $post_host = rcube_ui::get_input_value('_host', rcube_ui::INPUT_POST); |
| | | |
| | | // direct match in default_host array |
| | | if ($default_host[$post_host] || in_array($post_host, array_values($default_host))) { |
| | |
| | | } |
| | | |
| | | // try to select host by mail domain |
| | | list($user, $domain) = explode('@', get_input_value('_user', RCUBE_INPUT_POST)); |
| | | list($user, $domain) = explode('@', rcube_ui::get_input_value('_user', rcube_ui::INPUT_POST)); |
| | | if (!empty($domain)) { |
| | | foreach ($default_host as $storage_host => $mail_domains) { |
| | | if (is_array($mail_domains) && in_array_nocase($domain, $mail_domains)) { |
| | |
| | | } |
| | | } |
| | | else if (empty($default_host)) { |
| | | $host = get_input_value('_host', RCUBE_INPUT_POST); |
| | | $host = rcube_ui::get_input_value('_host', rcube_ui::INPUT_POST); |
| | | } |
| | | else |
| | | $host = rcube_parse_host($default_host); |
| | | $host = self::parse_host($default_host); |
| | | |
| | | return $host; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get localized text in the desired language |
| | | * |
| | | * @param mixed $attrib Named parameters array or label name |
| | | * @param string $domain Label domain (plugin) name |
| | | * |
| | | * @return string Localized text |
| | | */ |
| | | public function gettext($attrib, $domain=null) |
| | | { |
| | | // load localization files if not done yet |
| | | if (empty($this->texts)) |
| | | $this->load_language(); |
| | | |
| | | // extract attributes |
| | | if (is_string($attrib)) |
| | | $attrib = array('name' => $attrib); |
| | | |
| | | $name = $attrib['name'] ? $attrib['name'] : ''; |
| | | |
| | | // attrib contain text values: use them from now |
| | | if (($setval = $attrib[strtolower($_SESSION['language'])]) || ($setval = $attrib['en_us'])) |
| | | $this->texts[$name] = $setval; |
| | | |
| | | // check for text with domain |
| | | if ($domain && ($text = $this->texts[$domain.'.'.$name])) |
| | | ; |
| | | // text does not exist |
| | | else if (!($text = $this->texts[$name])) { |
| | | return "[$name]"; |
| | | } |
| | | |
| | | // replace vars in text |
| | | if (is_array($attrib['vars'])) { |
| | | foreach ($attrib['vars'] as $var_key => $var_value) |
| | | $text = str_replace($var_key[0]!='$' ? '$'.$var_key : $var_key, $var_value, $text); |
| | | } |
| | | |
| | | // format output |
| | | if (($attrib['uppercase'] && strtolower($attrib['uppercase']=='first')) || $attrib['ucfirst']) |
| | | return ucfirst($text); |
| | | else if ($attrib['uppercase']) |
| | | return mb_strtoupper($text); |
| | | else if ($attrib['lowercase']) |
| | | return mb_strtolower($text); |
| | | |
| | | return strtr($text, array('\n' => "\n")); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Check if the given text label exists |
| | | * |
| | | * @param string $name Label name |
| | | * @param string $domain Label domain (plugin) name or '*' for all domains |
| | | * @param string $ref_domain Sets domain name if label is found |
| | | * |
| | | * @return boolean True if text exists (either in the current language or in en_US) |
| | | */ |
| | | public function text_exists($name, $domain = null, &$ref_domain = null) |
| | | { |
| | | // load localization files if not done yet |
| | | if (empty($this->texts)) |
| | | $this->load_language(); |
| | | |
| | | if (isset($this->texts[$name])) { |
| | | $ref_domain = ''; |
| | | return true; |
| | | } |
| | | |
| | | // any of loaded domains (plugins) |
| | | if ($domain == '*') { |
| | | foreach ($this->plugins->loaded_plugins() as $domain) |
| | | if (isset($this->texts[$domain.'.'.$name])) { |
| | | $ref_domain = $domain; |
| | | return true; |
| | | } |
| | | } |
| | | // specified domain |
| | | else if ($domain) { |
| | | $ref_domain = $domain; |
| | | return isset($this->texts[$domain.'.'.$name]); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * Load a localization package |
| | | * |
| | | * @param string Language ID |
| | | */ |
| | | public function load_language($lang = null, $add = array()) |
| | | { |
| | | $lang = $this->language_prop(($lang ? $lang : $_SESSION['language'])); |
| | | |
| | | // load localized texts |
| | | if (empty($this->texts) || $lang != $_SESSION['language']) { |
| | | $this->texts = array(); |
| | | |
| | | // handle empty lines after closing PHP tag in localization files |
| | | ob_start(); |
| | | |
| | | // get english labels (these should be complete) |
| | | @include(INSTALL_PATH . 'program/localization/en_US/labels.inc'); |
| | | @include(INSTALL_PATH . 'program/localization/en_US/messages.inc'); |
| | | |
| | | if (is_array($labels)) |
| | | $this->texts = $labels; |
| | | if (is_array($messages)) |
| | | $this->texts = array_merge($this->texts, $messages); |
| | | |
| | | // include user language files |
| | | if ($lang != 'en' && is_dir(INSTALL_PATH . 'program/localization/' . $lang)) { |
| | | include_once(INSTALL_PATH . 'program/localization/' . $lang . '/labels.inc'); |
| | | include_once(INSTALL_PATH . 'program/localization/' . $lang . '/messages.inc'); |
| | | |
| | | if (is_array($labels)) |
| | | $this->texts = array_merge($this->texts, $labels); |
| | | if (is_array($messages)) |
| | | $this->texts = array_merge($this->texts, $messages); |
| | | } |
| | | |
| | | ob_end_clean(); |
| | | |
| | | $_SESSION['language'] = $lang; |
| | | } |
| | | |
| | | // append additional texts (from plugin) |
| | | if (is_array($add) && !empty($add)) |
| | | $this->texts += $add; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Read directory program/localization and return a list of available languages |
| | | * |
| | | * @return array List of available localizations |
| | | */ |
| | | public function list_languages() |
| | | { |
| | | static $sa_languages = array(); |
| | | |
| | | if (!sizeof($sa_languages)) { |
| | | @include(INSTALL_PATH . 'program/localization/index.inc'); |
| | | |
| | | if ($dh = @opendir(INSTALL_PATH . 'program/localization')) { |
| | | while (($name = readdir($dh)) !== false) { |
| | | if ($name[0] == '.' || !is_dir(INSTALL_PATH . 'program/localization/' . $name)) |
| | | continue; |
| | | |
| | | if ($label = $rcube_languages[$name]) |
| | | $sa_languages[$name] = $label; |
| | | } |
| | | closedir($dh); |
| | | } |
| | | } |
| | | |
| | | return $sa_languages; |
| | | } |
| | | |
| | | |
| | |
| | | |
| | | |
| | | /** |
| | | * Function to be executed in script shutdown |
| | | * Registered with register_shutdown_function() |
| | | */ |
| | | public function shutdown() |
| | | { |
| | | foreach ($this->shutdown_functions as $function) |
| | | call_user_func($function); |
| | | |
| | | if (is_object($this->smtp)) |
| | | $this->smtp->disconnect(); |
| | | |
| | | foreach ($this->address_books as $book) { |
| | | if (is_object($book) && is_a($book, 'rcube_addressbook')) |
| | | $book->close(); |
| | | } |
| | | |
| | | foreach ($this->caches as $cache) { |
| | | if (is_object($cache)) |
| | | $cache->close(); |
| | | } |
| | | |
| | | if (is_object($this->storage)) { |
| | | if ($this->expunge_cache) |
| | | $this->storage->expunge_cache(); |
| | | $this->storage->close(); |
| | | } |
| | | |
| | | // before closing the database connection, write session data |
| | | if ($_SERVER['REMOTE_ADDR'] && is_object($this->session)) { |
| | | session_write_close(); |
| | | } |
| | | |
| | | // write performance stats to logs/console |
| | | if ($this->config->get('devel_mode')) { |
| | | if (function_exists('memory_get_usage')) |
| | | $mem = show_bytes(memory_get_usage()); |
| | | if (function_exists('memory_get_peak_usage')) |
| | | $mem .= '/'.show_bytes(memory_get_peak_usage()); |
| | | |
| | | $log = $this->task . ($this->action ? '/'.$this->action : '') . ($mem ? " [$mem]" : ''); |
| | | if (defined('RCMAIL_START')) |
| | | rcube_print_time(RCMAIL_START, $log); |
| | | else |
| | | console($log); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Registers shutdown function to be executed on shutdown. |
| | | * The functions will be executed before destroying any |
| | | * objects like smtp, imap, session, etc. |
| | | * |
| | | * @param callback Function callback |
| | | */ |
| | | public function add_shutdown_function($function) |
| | | { |
| | | $this->shutdown_functions[] = $function; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Garbage collector for cache entries. |
| | | * Set flag to expunge caches on shutdown |
| | | */ |
| | |
| | | { |
| | | $sess_id = $_COOKIE[ini_get('session.name')]; |
| | | if (!$sess_id) $sess_id = session_id(); |
| | | $plugin = $this->plugins->exec_hook('request_token', array('value' => md5('RT' . $this->user->ID . $this->config->get('des_key') . $sess_id))); |
| | | $plugin = $this->plugins->exec_hook('request_token', array('value' => md5('RT' . $this->get_user_id() . $this->config->get('des_key') . $sess_id))); |
| | | return $plugin['value']; |
| | | } |
| | | |
| | |
| | | * @param int Request method |
| | | * @return boolean True if request token is valid false if not |
| | | */ |
| | | public function check_request($mode = RCUBE_INPUT_POST) |
| | | public function check_request($mode = rcube_ui::INPUT_POST) |
| | | { |
| | | $token = get_input_value('_token', $mode); |
| | | $token = rcube_ui::get_input_value('_token', $mode); |
| | | $sess_id = $_COOKIE[ini_get('session.name')]; |
| | | return !empty($sess_id) && $token == $this->get_request_token(); |
| | | } |
| | |
| | | return md5($auth_string); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Encrypt using 3DES |
| | | * |
| | | * @param string $clear clear text input |
| | | * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key' |
| | | * @param boolean $base64 whether or not to base64_encode() the result before returning |
| | | * |
| | | * @return string encrypted text |
| | | */ |
| | | public function encrypt($clear, $key = 'des_key', $base64 = true) |
| | | { |
| | | if (!$clear) |
| | | return ''; |
| | | /*- |
| | | * Add a single canary byte to the end of the clear text, which |
| | | * will help find out how much of padding will need to be removed |
| | | * upon decryption; see http://php.net/mcrypt_generic#68082 |
| | | */ |
| | | $clear = pack("a*H2", $clear, "80"); |
| | | |
| | | if (function_exists('mcrypt_module_open') && |
| | | ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, ""))) |
| | | { |
| | | $iv = $this->create_iv(mcrypt_enc_get_iv_size($td)); |
| | | mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv); |
| | | $cipher = $iv . mcrypt_generic($td, $clear); |
| | | mcrypt_generic_deinit($td); |
| | | mcrypt_module_close($td); |
| | | } |
| | | else { |
| | | @include_once 'des.inc'; |
| | | |
| | | if (function_exists('des')) { |
| | | $des_iv_size = 8; |
| | | $iv = $this->create_iv($des_iv_size); |
| | | $cipher = $iv . des($this->config->get_crypto_key($key), $clear, 1, 1, $iv); |
| | | } |
| | | else { |
| | | raise_error(array( |
| | | 'code' => 500, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Could not perform encryption; make sure Mcrypt is installed or lib/des.inc is available" |
| | | ), true, true); |
| | | } |
| | | } |
| | | |
| | | return $base64 ? base64_encode($cipher) : $cipher; |
| | | } |
| | | |
| | | /** |
| | | * Decrypt 3DES-encrypted string |
| | | * |
| | | * @param string $cipher encrypted text |
| | | * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key' |
| | | * @param boolean $base64 whether or not input is base64-encoded |
| | | * |
| | | * @return string decrypted text |
| | | */ |
| | | public function decrypt($cipher, $key = 'des_key', $base64 = true) |
| | | { |
| | | if (!$cipher) |
| | | return ''; |
| | | |
| | | $cipher = $base64 ? base64_decode($cipher) : $cipher; |
| | | |
| | | if (function_exists('mcrypt_module_open') && |
| | | ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, ""))) |
| | | { |
| | | $iv_size = mcrypt_enc_get_iv_size($td); |
| | | $iv = substr($cipher, 0, $iv_size); |
| | | |
| | | // session corruption? (#1485970) |
| | | if (strlen($iv) < $iv_size) |
| | | return ''; |
| | | |
| | | $cipher = substr($cipher, $iv_size); |
| | | mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv); |
| | | $clear = mdecrypt_generic($td, $cipher); |
| | | mcrypt_generic_deinit($td); |
| | | mcrypt_module_close($td); |
| | | } |
| | | else { |
| | | @include_once 'des.inc'; |
| | | |
| | | if (function_exists('des')) { |
| | | $des_iv_size = 8; |
| | | $iv = substr($cipher, 0, $des_iv_size); |
| | | $cipher = substr($cipher, $des_iv_size); |
| | | $clear = des($this->config->get_crypto_key($key), $cipher, 0, 1, $iv); |
| | | } |
| | | else { |
| | | raise_error(array( |
| | | 'code' => 500, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Could not perform decryption; make sure Mcrypt is installed or lib/des.inc is available" |
| | | ), true, true); |
| | | } |
| | | } |
| | | |
| | | /*- |
| | | * Trim PHP's padding and the canary byte; see note in |
| | | * rcmail::encrypt() and http://php.net/mcrypt_generic#68082 |
| | | */ |
| | | $clear = substr(rtrim($clear, "\0"), 0, -1); |
| | | |
| | | return $clear; |
| | | } |
| | | |
| | | /** |
| | | * Generates encryption initialization vector (IV) |
| | | * |
| | | * @param int Vector size |
| | | * @return string Vector string |
| | | */ |
| | | private function create_iv($size) |
| | | { |
| | | // mcrypt_create_iv() can be slow when system lacks entrophy |
| | | // we'll generate IV vector manually |
| | | $iv = ''; |
| | | for ($i = 0; $i < $size; $i++) |
| | | $iv .= chr(mt_rand(0, 255)); |
| | | return $iv; |
| | | } |
| | | |
| | | /** |
| | | * Build a valid URL to this instance of Roundcube |
| | |
| | | |
| | | |
| | | /** |
| | | * Construct shell command, execute it and return output as string. |
| | | * Keywords {keyword} are replaced with arguments |
| | | * |
| | | * @param $cmd Format string with {keywords} to be replaced |
| | | * @param $values (zero, one or more arrays can be passed) |
| | | * @return output of command. shell errors not detectable |
| | | * Function to be executed in script shutdown |
| | | */ |
| | | public static function exec(/* $cmd, $values1 = array(), ... */) |
| | | public function shutdown() |
| | | { |
| | | $args = func_get_args(); |
| | | $cmd = array_shift($args); |
| | | $values = $replacements = array(); |
| | | parent::shutdown(); |
| | | |
| | | // merge values into one array |
| | | foreach ($args as $arg) |
| | | $values += (array)$arg; |
| | | |
| | | preg_match_all('/({(-?)([a-z]\w*)})/', $cmd, $matches, PREG_SET_ORDER); |
| | | foreach ($matches as $tags) { |
| | | list(, $tag, $option, $key) = $tags; |
| | | $parts = array(); |
| | | |
| | | if ($option) { |
| | | foreach ((array)$values["-$key"] as $key => $value) { |
| | | if ($value === true || $value === false || $value === null) |
| | | $parts[] = $value ? $key : ""; |
| | | else foreach ((array)$value as $val) |
| | | $parts[] = "$key " . escapeshellarg($val); |
| | | } |
| | | } |
| | | else { |
| | | foreach ((array)$values[$key] as $value) |
| | | $parts[] = escapeshellarg($value); |
| | | } |
| | | |
| | | $replacements[$tag] = join(" ", $parts); |
| | | foreach ($this->address_books as $book) { |
| | | if (is_object($book) && is_a($book, 'rcube_addressbook')) |
| | | $book->close(); |
| | | } |
| | | |
| | | // use strtr behaviour of going through source string once |
| | | $cmd = strtr($cmd, $replacements); |
| | | // before closing the database connection, write session data |
| | | if ($_SERVER['REMOTE_ADDR'] && is_object($this->session)) { |
| | | session_write_close(); |
| | | } |
| | | |
| | | return (string)shell_exec($cmd); |
| | | // write performance stats to logs/console |
| | | if ($this->config->get('devel_mode')) { |
| | | if (function_exists('memory_get_usage')) |
| | | $mem = rcube_ui::show_bytes(memory_get_usage()); |
| | | if (function_exists('memory_get_peak_usage')) |
| | | $mem .= '/'.rcube_ui::show_bytes(memory_get_peak_usage()); |
| | | |
| | | $log = $this->task . ($this->action ? '/'.$this->action : '') . ($mem ? " [$mem]" : ''); |
| | | if (defined('RCMAIL_START')) |
| | | self::print_timer(RCMAIL_START, $log); |
| | | else |
| | | self::console($log); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Helper method to set a cookie with the current path and host settings |
| | |
| | | $cookie = session_get_cookie_params(); |
| | | |
| | | setcookie($name, $value, $exp, $cookie['path'], $cookie['domain'], |
| | | rcube_https_check(), true); |
| | | rcube_ui::https_check(), true); |
| | | } |
| | | |
| | | /** |
| | |
| | | $this->set_storage_prop(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Overwrite action variable |
| | | * |
| | | * @param string New action value |
| | | */ |
| | | public function overwrite_action($action) |
| | | { |
| | | $this->action = $action; |
| | | $this->output->set_env('action', $action); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 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. |
| | | */ |
| | | public function deliver_message(&$message, $from, $mailto, &$smtp_error, &$body_file = null, $smtp_opts = null) |
| | | { |
| | | $headers = $message->headers(); |
| | | |
| | | // send thru SMTP server using custom SMTP library |
| | | if ($this->config->get('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 = $this->config->get('temp_dir'); |
| | | $body_file = tempnam($temp_dir, 'rcmMsg'); |
| | | if (PEAR::isError($mime_result = $message->saveMessageBody($body_file))) { |
| | | self::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($this->smtp)) { |
| | | $this->smtp_init(true); |
| | | } |
| | | |
| | | $sent = $this->smtp->send_mail($from, $a_recipients, $smtp_headers, $msg_body, $smtp_opts); |
| | | $smtp_response = $this->smtp->get_response(); |
| | | $smtp_error = $this->smtp->get_error(); |
| | | |
| | | // log error |
| | | if (!$sent) { |
| | | self::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)) { |
| | | self::raise_error(array('code' => 650, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Could not create message: ".$msg_body->getMessage()), |
| | | TRUE, FALSE); |
| | | } |
| | | else { |
| | | $delim = $this->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) { |
| | | $this->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 ($this->config->get('smtp_log')) { |
| | | self::write_log('sendmail', sprintf("User %s [%s]; Message for %s; %s", |
| | | $this->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; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Unique Message-ID generator. |
| | | * |
| | | * @return string Message-ID |
| | | */ |
| | | public function gen_message_id() |
| | | { |
| | | $local_part = md5(uniqid('rcmail'.mt_rand(),true)); |
| | | $domain_part = $this->user->get_username('domain'); |
| | | |
| | | // Try to find FQDN, some spamfilters doesn't like 'localhost' (#1486924) |
| | | if (!preg_match('/\.[a-z]+$/i', $domain_part)) { |
| | | foreach (array($_SERVER['HTTP_HOST'], $_SERVER['SERVER_NAME']) as $host) { |
| | | $host = preg_replace('/:[0-9]+$/', '', $host); |
| | | if ($host && 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 |
| | | * |
| | | * @return string Date |
| | | */ |
| | | public function user_date() |
| | | { |
| | | // get user's timezone |
| | | try { |
| | | $tz = new DateTimeZone($this->config->get('timezone')); |
| | | $date = new DateTime('now', $tz); |
| | | } |
| | | catch (Exception $e) { |
| | | $date = new DateTime(); |
| | | } |
| | | |
| | | return $date->format('r'); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * E-mail address validation. |
| | | * |
| | | * @param string $email Email address |
| | | * @param boolean $dns_check True to check dns |
| | | * |
| | | * @return boolean True on success, False if address is invalid |
| | | */ |
| | | public function check_email($email, $dns_check=true) |
| | | { |
| | | // Check for invalid characters |
| | | if (preg_match('/[\x00-\x1F\x7F-\xFF]/', $email)) { |
| | | return false; |
| | | } |
| | | |
| | | // Check for length limit specified by RFC 5321 (#1486453) |
| | | if (strlen($email) > 254) { |
| | | return false; |
| | | } |
| | | |
| | | $email_array = explode('@', $email); |
| | | |
| | | // Check that there's one @ symbol |
| | | if (count($email_array) < 2) { |
| | | return false; |
| | | } |
| | | |
| | | $domain_part = array_pop($email_array); |
| | | $local_part = implode('@', $email_array); |
| | | |
| | | // from PEAR::Validate |
| | | $regexp = '&^(?: |
| | | ("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+")| #1 quoted name |
| | | ([-\w!\#\$%\&\'*+~/^`|{}=]+(?:\.[-\w!\#\$%\&\'*+~/^`|{}=]+)*)) #2 OR dot-atom (RFC5322) |
| | | $&xi'; |
| | | |
| | | if (!preg_match($regexp, $local_part)) { |
| | | return false; |
| | | } |
| | | |
| | | // Check domain part |
| | | if (preg_match('/^\[*(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}\]*$/', $domain_part)) { |
| | | return true; // IP address |
| | | } |
| | | else { |
| | | // If not an IP address |
| | | $domain_array = explode('.', $domain_part); |
| | | // Not enough parts to be a valid domain |
| | | if (sizeof($domain_array) < 2) { |
| | | return false; |
| | | } |
| | | |
| | | foreach ($domain_array as $part) { |
| | | if (!preg_match('/^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]))$/', $part)) { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | if (!$dns_check || !$this->config->get('email_dns_check')) { |
| | | return true; |
| | | } |
| | | |
| | | if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' && version_compare(PHP_VERSION, '5.3.0', '<')) { |
| | | $lookup = array(); |
| | | @exec("nslookup -type=MX " . escapeshellarg($domain_part) . " 2>&1", $lookup); |
| | | foreach ($lookup as $line) { |
| | | if (strpos($line, 'MX preference')) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | // find MX record(s) |
| | | if (getmxrr($domain_part, $mx_records)) { |
| | | return true; |
| | | } |
| | | |
| | | // find any DNS record |
| | | if (checkdnsrr($domain_part, 'ANY')) { |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Write login data (name, ID, IP address) to the 'userlogins' log file. |
| | | */ |
| | | public function log_login() |
| | | { |
| | | if (!$this->config->get('log_logins')) { |
| | | return; |
| | | } |
| | | |
| | | $user_name = $this->get_user_name(); |
| | | $user_id = $this->get_user_id(); |
| | | |
| | | if (!$user_id) { |
| | | return; |
| | | } |
| | | |
| | | self::write_log('userlogins', |
| | | sprintf('Successful login for %s (ID: %d) from %s in session %s', |
| | | $user_name, $user_id, self::remote_ip(), session_id())); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Check whether the HTTP referer matches the current request |
| | | * |
| | | * @return boolean True if referer is the same host+path, false if not |
| | | */ |
| | | public static function check_referer() |
| | | { |
| | | $uri = parse_url($_SERVER['REQUEST_URI']); |
| | | $referer = parse_url(rcube_request_header('Referer')); |
| | | return $referer['host'] == rcube_request_header('Host') && $referer['path'] == $uri['path']; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Garbage collector function for temp files. |
| | | * Remove temp files older than two days |
| | | */ |
| | | public function temp_gc() |
| | | { |
| | | $tmp = unslashify($this->config->get('temp_dir')); |
| | | $expire = mktime() - 172800; // expire in 48 hours |
| | | |
| | | if ($dir = opendir($tmp)) { |
| | | while (($fname = readdir($dir)) !== false) { |
| | | if ($fname{0} == '.') { |
| | | continue; |
| | | } |
| | | |
| | | if (filemtime($tmp.'/'.$fname) < $expire) { |
| | | @unlink($tmp.'/'.$fname); |
| | | } |
| | | } |
| | | |
| | | closedir($dir); |
| | | } |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /* |
| | | +-----------------------------------------------------------------------+ |
| | | | program/include/rcmail.php | |
| | | | | |
| | | | This file is part of the Roundcube Webmail client | |
| | | | Copyright (C) 2008-2012, The Roundcube Dev Team | |
| | | | Copyright (C) 2011-2012, Kolab Systems AG | |
| | | | | |
| | | | Licensed under the GNU General Public License version 3 or | |
| | | | any later version with exceptions for skins & plugins. | |
| | | | See the README file for a full license statement. | |
| | | | | |
| | | | PURPOSE: | |
| | | | Framework base class providing core functions and holding | |
| | | | instances of all 'global' objects like db- and storage-connections | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Thomas Bruederli <roundcube@gmail.com> | |
| | | +-----------------------------------------------------------------------+ |
| | | |
| | | $Id$ |
| | | |
| | | */ |
| | | |
| | | |
| | | /** |
| | | * Base class of the Roundcube Framework |
| | | * implemented as singleton |
| | | * |
| | | * @package Core |
| | | */ |
| | | class rcube |
| | | { |
| | | const INIT_WITH_DB = 1; |
| | | const INIT_WITH_PLUGINS = 2; |
| | | |
| | | /** |
| | | * Singleton instace of rcmail |
| | | * |
| | | * @var rcmail |
| | | */ |
| | | static protected $instance; |
| | | |
| | | /** |
| | | * Stores instance of rcube_config. |
| | | * |
| | | * @var rcube_config |
| | | */ |
| | | public $config; |
| | | |
| | | /** |
| | | * Instace of database class. |
| | | * |
| | | * @var rcube_mdb2 |
| | | */ |
| | | public $db; |
| | | |
| | | /** |
| | | * Instace of Memcache class. |
| | | * |
| | | * @var rcube_mdb2 |
| | | */ |
| | | public $memcache; |
| | | |
| | | /** |
| | | * Instace of rcube_session class. |
| | | * |
| | | * @var rcube_session |
| | | */ |
| | | public $session; |
| | | |
| | | /** |
| | | * Instance of rcube_smtp class. |
| | | * |
| | | * @var rcube_smtp |
| | | */ |
| | | public $smtp; |
| | | |
| | | /** |
| | | * Instance of rcube_storage class. |
| | | * |
| | | * @var rcube_storage |
| | | */ |
| | | public $storage; |
| | | |
| | | /** |
| | | * Instance of rcube_output class. |
| | | * |
| | | * @var rcube_output |
| | | */ |
| | | public $output; |
| | | |
| | | /** |
| | | * Instance of rcube_plugin_api. |
| | | * |
| | | * @var rcube_plugin_api |
| | | */ |
| | | public $plugins; |
| | | |
| | | |
| | | /* private/protected vars */ |
| | | protected $texts; |
| | | protected $caches = array(); |
| | | protected $shutdown_functions = array(); |
| | | protected $expunge_cache = false; |
| | | |
| | | |
| | | /** |
| | | * This implements the 'singleton' design pattern |
| | | * |
| | | * @return rcmail The one and only instance |
| | | */ |
| | | static function get_instance() |
| | | { |
| | | if (!self::$instance) { |
| | | self::$instance = new rcube(); |
| | | } |
| | | |
| | | return self::$instance; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Private constructor |
| | | */ |
| | | protected function __construct() |
| | | { |
| | | // load configuration |
| | | $this->config = new rcube_config(); |
| | | $this->plugins = new rcube_dummy_plugin_api; |
| | | |
| | | register_shutdown_function(array($this, 'shutdown')); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initial startup function |
| | | */ |
| | | protected function init($mode = 0) |
| | | { |
| | | // initialize syslog |
| | | if ($this->config->get('log_driver') == 'syslog') { |
| | | $syslog_id = $this->config->get('syslog_id', 'roundcube'); |
| | | $syslog_facility = $this->config->get('syslog_facility', LOG_USER); |
| | | openlog($syslog_id, LOG_ODELAY, $syslog_facility); |
| | | } |
| | | |
| | | // connect to database |
| | | if ($mode & self::INIT_WITH_DB) { |
| | | $this->get_dbh(); |
| | | } |
| | | |
| | | // create plugin API and load plugins |
| | | if ($mode & self::INIT_WITH_PLUGINS) { |
| | | $this->plugins = rcube_plugin_api::get_instance(); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get the current database connection |
| | | * |
| | | * @return rcube_mdb2 Database connection object |
| | | */ |
| | | public function get_dbh() |
| | | { |
| | | if (!$this->db) { |
| | | $config_all = $this->config->all(); |
| | | |
| | | $this->db = new rcube_mdb2($config_all['db_dsnw'], $config_all['db_dsnr'], $config_all['db_persistent']); |
| | | $this->db->sqlite_initials = INSTALL_PATH . 'SQL/sqlite.initial.sql'; |
| | | $this->db->set_debug((bool)$config_all['sql_debug']); |
| | | } |
| | | |
| | | return $this->db; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get global handle for memcache access |
| | | * |
| | | * @return object Memcache |
| | | */ |
| | | public function get_memcache() |
| | | { |
| | | if (!isset($this->memcache)) { |
| | | // no memcache support in PHP |
| | | if (!class_exists('Memcache')) { |
| | | $this->memcache = false; |
| | | return false; |
| | | } |
| | | |
| | | $this->memcache = new Memcache; |
| | | $this->mc_available = 0; |
| | | |
| | | // add alll configured hosts to pool |
| | | $pconnect = $this->config->get('memcache_pconnect', true); |
| | | foreach ($this->config->get('memcache_hosts', array()) as $host) { |
| | | list($host, $port) = explode(':', $host); |
| | | if (!$port) $port = 11211; |
| | | $this->mc_available += intval($this->memcache->addServer($host, $port, $pconnect, 1, 1, 15, false, array($this, 'memcache_failure'))); |
| | | } |
| | | |
| | | // test connection and failover (will result in $this->mc_available == 0 on complete failure) |
| | | $this->memcache->increment('__CONNECTIONTEST__', 1); // NOP if key doesn't exist |
| | | |
| | | if (!$this->mc_available) |
| | | $this->memcache = false; |
| | | } |
| | | |
| | | return $this->memcache; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Callback for memcache failure |
| | | */ |
| | | public function memcache_failure($host, $port) |
| | | { |
| | | static $seen = array(); |
| | | |
| | | // only report once |
| | | if (!$seen["$host:$port"]++) { |
| | | $this->mc_available--; |
| | | self::raise_error(array('code' => 604, 'type' => 'db', |
| | | 'line' => __LINE__, 'file' => __FILE__, |
| | | 'message' => "Memcache failure on host $host:$port"), |
| | | true, false); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initialize and get cache object |
| | | * |
| | | * @param string $name Cache identifier |
| | | * @param string $type Cache type ('db', 'apc' or 'memcache') |
| | | * @param int $ttl Expiration time for cache items in seconds |
| | | * @param bool $packed Enables/disables data serialization |
| | | * |
| | | * @return rcube_cache Cache object |
| | | */ |
| | | public function get_cache($name, $type='db', $ttl=0, $packed=true) |
| | | { |
| | | if (!isset($this->caches[$name])) { |
| | | $this->caches[$name] = new rcube_cache($type, $_SESSION['user_id'], $name, $ttl, $packed); |
| | | } |
| | | |
| | | return $this->caches[$name]; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create SMTP object and connect to server |
| | | * |
| | | * @param boolean True if connection should be established |
| | | */ |
| | | public function smtp_init($connect = false) |
| | | { |
| | | $this->smtp = new rcube_smtp(); |
| | | |
| | | if ($connect) |
| | | $this->smtp->connect(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initialize and get storage object |
| | | * |
| | | * @return rcube_storage Storage object |
| | | */ |
| | | public function get_storage() |
| | | { |
| | | // already initialized |
| | | if (!is_object($this->storage)) { |
| | | $this->storage_init(); |
| | | } |
| | | |
| | | return $this->storage; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initialize storage object |
| | | */ |
| | | public function storage_init() |
| | | { |
| | | // already initialized |
| | | if (is_object($this->storage)) { |
| | | return; |
| | | } |
| | | |
| | | $driver = $this->config->get('storage_driver', 'imap'); |
| | | $driver_class = "rcube_{$driver}"; |
| | | |
| | | if (!class_exists($driver_class)) { |
| | | self::raise_error(array( |
| | | 'code' => 700, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Storage driver class ($driver) not found!"), |
| | | true, true); |
| | | } |
| | | |
| | | // Initialize storage object |
| | | $this->storage = new $driver_class; |
| | | |
| | | // for backward compat. (deprecated, will be removed) |
| | | $this->imap = $this->storage; |
| | | |
| | | // enable caching of mail data |
| | | $storage_cache = $this->config->get("{$driver}_cache"); |
| | | $messages_cache = $this->config->get('messages_cache'); |
| | | // for backward compatybility |
| | | if ($storage_cache === null && $messages_cache === null && $this->config->get('enable_caching')) { |
| | | $storage_cache = 'db'; |
| | | $messages_cache = true; |
| | | } |
| | | |
| | | if ($storage_cache) |
| | | $this->storage->set_caching($storage_cache); |
| | | if ($messages_cache) |
| | | $this->storage->set_messages_caching(true); |
| | | |
| | | // set pagesize from config |
| | | $pagesize = $this->config->get('mail_pagesize'); |
| | | if (!$pagesize) { |
| | | $pagesize = $this->config->get('pagesize', 50); |
| | | } |
| | | $this->storage->set_pagesize($pagesize); |
| | | |
| | | // set class options |
| | | $options = array( |
| | | 'auth_type' => $this->config->get("{$driver}_auth_type", 'check'), |
| | | 'auth_cid' => $this->config->get("{$driver}_auth_cid"), |
| | | 'auth_pw' => $this->config->get("{$driver}_auth_pw"), |
| | | 'debug' => (bool) $this->config->get("{$driver}_debug"), |
| | | 'force_caps' => (bool) $this->config->get("{$driver}_force_caps"), |
| | | 'timeout' => (int) $this->config->get("{$driver}_timeout"), |
| | | 'skip_deleted' => (bool) $this->config->get('skip_deleted'), |
| | | 'driver' => $driver, |
| | | ); |
| | | |
| | | if (!empty($_SESSION['storage_host'])) { |
| | | $options['host'] = $_SESSION['storage_host']; |
| | | $options['user'] = $_SESSION['username']; |
| | | $options['port'] = $_SESSION['storage_port']; |
| | | $options['ssl'] = $_SESSION['storage_ssl']; |
| | | $options['password'] = $this->decrypt($_SESSION['password']); |
| | | } |
| | | |
| | | $options = $this->plugins->exec_hook("storage_init", $options); |
| | | |
| | | // for backward compat. (deprecated, to be removed) |
| | | $options = $this->plugins->exec_hook("imap_init", $options); |
| | | |
| | | $this->storage->set_options($options); |
| | | $this->set_storage_prop(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Connect to the mail storage server with stored session data |
| | | * |
| | | * @return bool True on success, False on error |
| | | */ |
| | | public function storage_connect() |
| | | { |
| | | $storage = $this->get_storage(); |
| | | |
| | | if ($_SESSION['storage_host'] && !$storage->is_connected()) { |
| | | $host = $_SESSION['storage_host']; |
| | | $user = $_SESSION['username']; |
| | | $port = $_SESSION['storage_port']; |
| | | $ssl = $_SESSION['storage_ssl']; |
| | | $pass = $this->decrypt($_SESSION['password']); |
| | | |
| | | if (!$storage->connect($host, $user, $pass, $port, $ssl)) { |
| | | if (is_object($this->output)) |
| | | $this->output->show_message($storage->get_error_code() == -1 ? 'storageerror' : 'sessionerror', 'error'); |
| | | } |
| | | else { |
| | | $this->set_storage_prop(); |
| | | return $storage->is_connected(); |
| | | } |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * Set storage parameters. |
| | | * This must be done AFTER connecting to the server! |
| | | */ |
| | | protected function set_storage_prop() |
| | | { |
| | | $storage = $this->get_storage(); |
| | | |
| | | $storage->set_charset($this->config->get('default_charset', RCMAIL_CHARSET)); |
| | | |
| | | if ($default_folders = $this->config->get('default_folders')) { |
| | | $storage->set_default_folders($default_folders); |
| | | } |
| | | if (isset($_SESSION['mbox'])) { |
| | | $storage->set_folder($_SESSION['mbox']); |
| | | } |
| | | if (isset($_SESSION['page'])) { |
| | | $storage->set_page($_SESSION['page']); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get localized text in the desired language |
| | | * |
| | | * @param mixed $attrib Named parameters array or label name |
| | | * @param string $domain Label domain (plugin) name |
| | | * |
| | | * @return string Localized text |
| | | */ |
| | | public function gettext($attrib, $domain=null) |
| | | { |
| | | // load localization files if not done yet |
| | | if (empty($this->texts)) |
| | | $this->load_language(); |
| | | |
| | | // extract attributes |
| | | if (is_string($attrib)) |
| | | $attrib = array('name' => $attrib); |
| | | |
| | | $name = $attrib['name'] ? $attrib['name'] : ''; |
| | | |
| | | // attrib contain text values: use them from now |
| | | if (($setval = $attrib[strtolower($_SESSION['language'])]) || ($setval = $attrib['en_us'])) |
| | | $this->texts[$name] = $setval; |
| | | |
| | | // check for text with domain |
| | | if ($domain && ($text = $this->texts[$domain.'.'.$name])) |
| | | ; |
| | | // text does not exist |
| | | else if (!($text = $this->texts[$name])) { |
| | | return "[$name]"; |
| | | } |
| | | |
| | | // replace vars in text |
| | | if (is_array($attrib['vars'])) { |
| | | foreach ($attrib['vars'] as $var_key => $var_value) |
| | | $text = str_replace($var_key[0]!='$' ? '$'.$var_key : $var_key, $var_value, $text); |
| | | } |
| | | |
| | | // format output |
| | | if (($attrib['uppercase'] && strtolower($attrib['uppercase']=='first')) || $attrib['ucfirst']) |
| | | return ucfirst($text); |
| | | else if ($attrib['uppercase']) |
| | | return mb_strtoupper($text); |
| | | else if ($attrib['lowercase']) |
| | | return mb_strtolower($text); |
| | | |
| | | return strtr($text, array('\n' => "\n")); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Check if the given text label exists |
| | | * |
| | | * @param string $name Label name |
| | | * @param string $domain Label domain (plugin) name or '*' for all domains |
| | | * @param string $ref_domain Sets domain name if label is found |
| | | * |
| | | * @return boolean True if text exists (either in the current language or in en_US) |
| | | */ |
| | | public function text_exists($name, $domain = null, &$ref_domain = null) |
| | | { |
| | | // load localization files if not done yet |
| | | if (empty($this->texts)) |
| | | $this->load_language(); |
| | | |
| | | if (isset($this->texts[$name])) { |
| | | $ref_domain = ''; |
| | | return true; |
| | | } |
| | | |
| | | // any of loaded domains (plugins) |
| | | if ($domain == '*') { |
| | | foreach ($this->plugins->loaded_plugins() as $domain) |
| | | if (isset($this->texts[$domain.'.'.$name])) { |
| | | $ref_domain = $domain; |
| | | return true; |
| | | } |
| | | } |
| | | // specified domain |
| | | else if ($domain) { |
| | | $ref_domain = $domain; |
| | | return isset($this->texts[$domain.'.'.$name]); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * Load a localization package |
| | | * |
| | | * @param string Language ID |
| | | */ |
| | | public function load_language($lang = null, $add = array()) |
| | | { |
| | | $lang = $this->language_prop(($lang ? $lang : $_SESSION['language'])); |
| | | |
| | | // load localized texts |
| | | if (empty($this->texts) || $lang != $_SESSION['language']) { |
| | | $this->texts = array(); |
| | | |
| | | // handle empty lines after closing PHP tag in localization files |
| | | ob_start(); |
| | | |
| | | // get english labels (these should be complete) |
| | | @include(INSTALL_PATH . 'program/localization/en_US/labels.inc'); |
| | | @include(INSTALL_PATH . 'program/localization/en_US/messages.inc'); |
| | | |
| | | if (is_array($labels)) |
| | | $this->texts = $labels; |
| | | if (is_array($messages)) |
| | | $this->texts = array_merge($this->texts, $messages); |
| | | |
| | | // include user language files |
| | | if ($lang != 'en' && is_dir(INSTALL_PATH . 'program/localization/' . $lang)) { |
| | | include_once(INSTALL_PATH . 'program/localization/' . $lang . '/labels.inc'); |
| | | include_once(INSTALL_PATH . 'program/localization/' . $lang . '/messages.inc'); |
| | | |
| | | if (is_array($labels)) |
| | | $this->texts = array_merge($this->texts, $labels); |
| | | if (is_array($messages)) |
| | | $this->texts = array_merge($this->texts, $messages); |
| | | } |
| | | |
| | | ob_end_clean(); |
| | | |
| | | $_SESSION['language'] = $lang; |
| | | } |
| | | |
| | | // append additional texts (from plugin) |
| | | if (is_array($add) && !empty($add)) |
| | | $this->texts += $add; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Check the given string and return a valid language code |
| | | * |
| | | * @param string Language code |
| | | * @return string Valid language code |
| | | */ |
| | | protected function language_prop($lang) |
| | | { |
| | | static $rcube_languages, $rcube_language_aliases; |
| | | |
| | | // user HTTP_ACCEPT_LANGUAGE if no language is specified |
| | | if (empty($lang) || $lang == 'auto') { |
| | | $accept_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']); |
| | | $lang = str_replace('-', '_', $accept_langs[0]); |
| | | } |
| | | |
| | | if (empty($rcube_languages)) { |
| | | @include(INSTALL_PATH . 'program/localization/index.inc'); |
| | | } |
| | | |
| | | // check if we have an alias for that language |
| | | if (!isset($rcube_languages[$lang]) && isset($rcube_language_aliases[$lang])) { |
| | | $lang = $rcube_language_aliases[$lang]; |
| | | } |
| | | // try the first two chars |
| | | else if (!isset($rcube_languages[$lang])) { |
| | | $short = substr($lang, 0, 2); |
| | | |
| | | // check if we have an alias for the short language code |
| | | if (!isset($rcube_languages[$short]) && isset($rcube_language_aliases[$short])) { |
| | | $lang = $rcube_language_aliases[$short]; |
| | | } |
| | | // expand 'nn' to 'nn_NN' |
| | | else if (!isset($rcube_languages[$short])) { |
| | | $lang = $short.'_'.strtoupper($short); |
| | | } |
| | | } |
| | | |
| | | if (!isset($rcube_languages[$lang]) || !is_dir(INSTALL_PATH . 'program/localization/' . $lang)) { |
| | | $lang = 'en_US'; |
| | | } |
| | | |
| | | return $lang; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Read directory program/localization and return a list of available languages |
| | | * |
| | | * @return array List of available localizations |
| | | */ |
| | | public function list_languages() |
| | | { |
| | | static $sa_languages = array(); |
| | | |
| | | if (!sizeof($sa_languages)) { |
| | | @include(INSTALL_PATH . 'program/localization/index.inc'); |
| | | |
| | | if ($dh = @opendir(INSTALL_PATH . 'program/localization')) { |
| | | while (($name = readdir($dh)) !== false) { |
| | | if ($name[0] == '.' || !is_dir(INSTALL_PATH . 'program/localization/' . $name)) |
| | | continue; |
| | | |
| | | if ($label = $rcube_languages[$name]) |
| | | $sa_languages[$name] = $label; |
| | | } |
| | | closedir($dh); |
| | | } |
| | | } |
| | | |
| | | return $sa_languages; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Encrypt using 3DES |
| | | * |
| | | * @param string $clear clear text input |
| | | * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key' |
| | | * @param boolean $base64 whether or not to base64_encode() the result before returning |
| | | * |
| | | * @return string encrypted text |
| | | */ |
| | | public function encrypt($clear, $key = 'des_key', $base64 = true) |
| | | { |
| | | if (!$clear) |
| | | return ''; |
| | | |
| | | /*- |
| | | * Add a single canary byte to the end of the clear text, which |
| | | * will help find out how much of padding will need to be removed |
| | | * upon decryption; see http://php.net/mcrypt_generic#68082 |
| | | */ |
| | | $clear = pack("a*H2", $clear, "80"); |
| | | |
| | | if (function_exists('mcrypt_module_open') && |
| | | ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, ""))) { |
| | | $iv = $this->create_iv(mcrypt_enc_get_iv_size($td)); |
| | | mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv); |
| | | $cipher = $iv . mcrypt_generic($td, $clear); |
| | | mcrypt_generic_deinit($td); |
| | | mcrypt_module_close($td); |
| | | } |
| | | else { |
| | | @include_once 'des.inc'; |
| | | |
| | | if (function_exists('des')) { |
| | | $des_iv_size = 8; |
| | | $iv = $this->create_iv($des_iv_size); |
| | | $cipher = $iv . des($this->config->get_crypto_key($key), $clear, 1, 1, $iv); |
| | | } |
| | | else { |
| | | self::raise_error(array( |
| | | 'code' => 500, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Could not perform encryption; make sure Mcrypt is installed or lib/des.inc is available" |
| | | ), true, true); |
| | | } |
| | | } |
| | | |
| | | return $base64 ? base64_encode($cipher) : $cipher; |
| | | } |
| | | |
| | | /** |
| | | * Decrypt 3DES-encrypted string |
| | | * |
| | | * @param string $cipher encrypted text |
| | | * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key' |
| | | * @param boolean $base64 whether or not input is base64-encoded |
| | | * |
| | | * @return string decrypted text |
| | | */ |
| | | public function decrypt($cipher, $key = 'des_key', $base64 = true) |
| | | { |
| | | if (!$cipher) |
| | | return ''; |
| | | |
| | | $cipher = $base64 ? base64_decode($cipher) : $cipher; |
| | | |
| | | if (function_exists('mcrypt_module_open') && |
| | | ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, ""))) { |
| | | $iv_size = mcrypt_enc_get_iv_size($td); |
| | | $iv = substr($cipher, 0, $iv_size); |
| | | |
| | | // session corruption? (#1485970) |
| | | if (strlen($iv) < $iv_size) |
| | | return ''; |
| | | |
| | | $cipher = substr($cipher, $iv_size); |
| | | mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv); |
| | | $clear = mdecrypt_generic($td, $cipher); |
| | | mcrypt_generic_deinit($td); |
| | | mcrypt_module_close($td); |
| | | } |
| | | else { |
| | | @include_once 'des.inc'; |
| | | |
| | | if (function_exists('des')) { |
| | | $des_iv_size = 8; |
| | | $iv = substr($cipher, 0, $des_iv_size); |
| | | $cipher = substr($cipher, $des_iv_size); |
| | | $clear = des($this->config->get_crypto_key($key), $cipher, 0, 1, $iv); |
| | | } |
| | | else { |
| | | self::raise_error(array( |
| | | 'code' => 500, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Could not perform decryption; make sure Mcrypt is installed or lib/des.inc is available" |
| | | ), true, true); |
| | | } |
| | | } |
| | | |
| | | /*- |
| | | * Trim PHP's padding and the canary byte; see note in |
| | | * rcmail::encrypt() and http://php.net/mcrypt_generic#68082 |
| | | */ |
| | | $clear = substr(rtrim($clear, "\0"), 0, -1); |
| | | |
| | | return $clear; |
| | | } |
| | | |
| | | /** |
| | | * Generates encryption initialization vector (IV) |
| | | * |
| | | * @param int Vector size |
| | | * @return string Vector string |
| | | */ |
| | | private function create_iv($size) |
| | | { |
| | | // mcrypt_create_iv() can be slow when system lacks entrophy |
| | | // we'll generate IV vector manually |
| | | $iv = ''; |
| | | for ($i = 0; $i < $size; $i++) |
| | | $iv .= chr(mt_rand(0, 255)); |
| | | return $iv; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Build a valid URL to this instance of Roundcube |
| | | * |
| | | * @param mixed Either a string with the action or url parameters as key-value pairs |
| | | * @return string Valid application URL |
| | | */ |
| | | public function url($p) |
| | | { |
| | | // STUB: should be overloaded by the application |
| | | return ''; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Function to be executed in script shutdown |
| | | * Registered with register_shutdown_function() |
| | | */ |
| | | public function shutdown() |
| | | { |
| | | foreach ($this->shutdown_functions as $function) |
| | | call_user_func($function); |
| | | |
| | | if (is_object($this->smtp)) |
| | | $this->smtp->disconnect(); |
| | | |
| | | foreach ($this->caches as $cache) { |
| | | if (is_object($cache)) |
| | | $cache->close(); |
| | | } |
| | | |
| | | if (is_object($this->storage)) { |
| | | if ($this->expunge_cache) |
| | | $this->storage->expunge_cache(); |
| | | $this->storage->close(); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Registers shutdown function to be executed on shutdown. |
| | | * The functions will be executed before destroying any |
| | | * objects like smtp, imap, session, etc. |
| | | * |
| | | * @param callback Function callback |
| | | */ |
| | | public function add_shutdown_function($function) |
| | | { |
| | | $this->shutdown_functions[] = $function; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Use imagemagick or GD lib to read image properties |
| | | * |
| | | * @param string Absolute file path |
| | | * @return mixed Hash array with image props like type, width, height or False on error |
| | | */ |
| | | public static function imageprops($filepath) |
| | | { |
| | | $rcube = self::get_instance(); |
| | | if ($cmd = $rcube->config->get('im_identify_path', false)) { |
| | | list(, $type, $size) = explode(' ', strtolower(self::exec($cmd. ' 2>/dev/null {in}', array('in' => $filepath)))); |
| | | if ($size) |
| | | list($width, $height) = explode('x', $size); |
| | | } |
| | | else if (function_exists('getimagesize')) { |
| | | $imsize = @getimagesize($filepath); |
| | | $width = $imsize[0]; |
| | | $height = $imsize[1]; |
| | | $type = preg_replace('!image/!', '', $imsize['mime']); |
| | | } |
| | | |
| | | return $type ? array('type' => $type, 'width' => $width, 'height' => $height) : false; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Convert an image to a given size and type using imagemagick (ensures input is an image) |
| | | * |
| | | * @param $p['in'] Input filename (mandatory) |
| | | * @param $p['out'] Output filename (mandatory) |
| | | * @param $p['size'] Width x height of resulting image, e.g. "160x60" |
| | | * @param $p['type'] Output file type, e.g. "jpg" |
| | | * @param $p['-opts'] Custom command line options to ImageMagick convert |
| | | * @return Success of convert as true/false |
| | | */ |
| | | public static function imageconvert($p) |
| | | { |
| | | $result = false; |
| | | $rcube = self::get_instance(); |
| | | $convert = $rcube->config->get('im_convert_path', false); |
| | | $identify = $rcube->config->get('im_identify_path', false); |
| | | |
| | | // imagemagick is required for this |
| | | if (!$convert) |
| | | return false; |
| | | |
| | | if (!(($imagetype = @exif_imagetype($p['in'])) && ($type = image_type_to_extension($imagetype, false)))) |
| | | list(, $type) = explode(' ', strtolower(self::exec($identify . ' 2>/dev/null {in}', $p))); # for things like eps |
| | | |
| | | $type = strtr($type, array("jpeg" => "jpg", "tiff" => "tif", "ps" => "eps", "ept" => "eps")); |
| | | $p += array('type' => $type, 'types' => "bmp,eps,gif,jp2,jpg,png,svg,tif", 'quality' => 75); |
| | | $p['-opts'] = array('-resize' => $p['size'].'>') + (array)$p['-opts']; |
| | | |
| | | if (in_array($type, explode(',', $p['types']))) # Valid type? |
| | | $result = self::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace RGB -quality {quality} {-opts} {in} {type}:{out}', $p) === ""; |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Construct shell command, execute it and return output as string. |
| | | * Keywords {keyword} are replaced with arguments |
| | | * |
| | | * @param $cmd Format string with {keywords} to be replaced |
| | | * @param $values (zero, one or more arrays can be passed) |
| | | * @return output of command. shell errors not detectable |
| | | */ |
| | | public static function exec(/* $cmd, $values1 = array(), ... */) |
| | | { |
| | | $args = func_get_args(); |
| | | $cmd = array_shift($args); |
| | | $values = $replacements = array(); |
| | | |
| | | // merge values into one array |
| | | foreach ($args as $arg) |
| | | $values += (array)$arg; |
| | | |
| | | preg_match_all('/({(-?)([a-z]\w*)})/', $cmd, $matches, PREG_SET_ORDER); |
| | | foreach ($matches as $tags) { |
| | | list(, $tag, $option, $key) = $tags; |
| | | $parts = array(); |
| | | |
| | | if ($option) { |
| | | foreach ((array)$values["-$key"] as $key => $value) { |
| | | if ($value === true || $value === false || $value === null) |
| | | $parts[] = $value ? $key : ""; |
| | | else foreach ((array)$value as $val) |
| | | $parts[] = "$key " . escapeshellarg($val); |
| | | } |
| | | } |
| | | else { |
| | | foreach ((array)$values[$key] as $value) |
| | | $parts[] = escapeshellarg($value); |
| | | } |
| | | |
| | | $replacements[$tag] = join(" ", $parts); |
| | | } |
| | | |
| | | // use strtr behaviour of going through source string once |
| | | $cmd = strtr($cmd, $replacements); |
| | | |
| | | return (string)shell_exec($cmd); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Replaces hostname variables. |
| | | * |
| | | * @param string $name Hostname |
| | | * @param string $host Optional IMAP hostname |
| | | * |
| | | * @return string Hostname |
| | | */ |
| | | public static function parse_host($name, $host = '') |
| | | { |
| | | // %n - host |
| | | $n = preg_replace('/:\d+$/', '', $_SERVER['SERVER_NAME']); |
| | | // %d - domain name without first part, e.g. %n=mail.domain.tld, %d=domain.tld |
| | | $d = preg_replace('/^[^\.]+\./', '', $n); |
| | | // %h - IMAP host |
| | | $h = $_SESSION['storage_host'] ? $_SESSION['storage_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_ui::get_input_value('_user', rcube_ui::INPUT_POST); |
| | | $user_email = rcube_idn_convert($user_email, true); |
| | | $matches = preg_match('/(.*)@([a-z0-9\.\-\[\]\:]+)/i', $user_email, $s); |
| | | if ($matches < 1 || filter_var($s[1]."@".$s[2], FILTER_VALIDATE_EMAIL) === false) { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | $name = str_replace(array('%n', '%d', '%h', '%z', '%s'), array($n, $d, $h, $z, $s[2]), $name); |
| | | return $name; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Print or write debug messages |
| | | * |
| | | * @param mixed Debug message or data |
| | | */ |
| | | public static function console() |
| | | { |
| | | $args = func_get_args(); |
| | | |
| | | if (class_exists('rcmail', false)) { |
| | | $rcube = self::get_instance(); |
| | | if (is_object($rcube->plugins)) { |
| | | $plugin = $rcube->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; |
| | | } |
| | | |
| | | self::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 |
| | | */ |
| | | public static function write_log($name, $line) |
| | | { |
| | | if (!is_string($line)) { |
| | | $line = var_export($line, true); |
| | | } |
| | | |
| | | $date_format = self::$instance ? self::$instance->config->get('log_date_format') : null; |
| | | $log_driver = self::$instance ? self::$instance->config->get('log_driver') : null; |
| | | |
| | | if (empty($date_format)) { |
| | | $date_format = 'd-M-Y H:i:s O'; |
| | | } |
| | | |
| | | $date = date($date_format); |
| | | |
| | | // trigger logging hook |
| | | if (is_object(self::$instance) && is_object(self::$instance->plugins)) { |
| | | $log = self::$instance->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 ($log_driver == 'syslog') { |
| | | $prio = $name == 'errors' ? LOG_ERR : LOG_INFO; |
| | | syslog($prio, $line); |
| | | return true; |
| | | } |
| | | |
| | | // log_driver == 'file' is assumed here |
| | | |
| | | $line = sprintf("[%s]: %s\n", $date, $line); |
| | | $log_dir = self::$instance ? self::$instance->config->get('log_dir') : null; |
| | | |
| | | if (empty($log_dir)) { |
| | | $log_dir = INSTALL_PATH . 'logs'; |
| | | } |
| | | |
| | | // try to open specific log file for writing |
| | | $logfile = $log_dir.'/'.$name; |
| | | |
| | | if ($fp = @fopen($logfile, 'a')) { |
| | | fwrite($fp, $line); |
| | | fflush($fp); |
| | | fclose($fp); |
| | | return true; |
| | | } |
| | | |
| | | trigger_error("Error writing to log file $logfile; Please check permissions", E_USER_WARNING); |
| | | return false; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Throw system error (and show error page). |
| | | * |
| | | * @param array Named parameters |
| | | * - code: Error code (required) |
| | | * - type: Error type [php|db|imap|javascript] (required) |
| | | * - message: Error message |
| | | * - file: File where error occured |
| | | * - line: Line where error occured |
| | | * @param boolean True to log the error |
| | | * @param boolean Terminate script execution |
| | | */ |
| | | public static function raise_error($arg = array(), $log = false, $terminate = false) |
| | | { |
| | | // installer |
| | | if (class_exists('rcube_install', false)) { |
| | | $rci = rcube_install::get_instance(); |
| | | $rci->raise_error($arg); |
| | | return; |
| | | } |
| | | |
| | | if ($log && $arg['type'] && $arg['message']) { |
| | | self::log_bug($arg); |
| | | } |
| | | |
| | | // display error page and terminate script |
| | | if ($terminate && is_object(self::$instance->output)) { |
| | | self::$instance->output->raise_error($arg['code'], $arg['message']); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Report error according to configured debug_level |
| | | * |
| | | * @param array Named parameters |
| | | * @see self::raise_error() |
| | | */ |
| | | public static function log_bug($arg_arr) |
| | | { |
| | | $program = strtoupper($arg_arr['type']); |
| | | $level = self::get_instance()->config->get('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 ($level & 1) { |
| | | if ($_SERVER['REQUEST_METHOD'] == 'POST') { |
| | | $post_query = '?_task='.urlencode($_POST['_task']).'&_action='.urlencode($_POST['_action']); |
| | | } |
| | | else { |
| | | $post_query = ''; |
| | | } |
| | | |
| | | $log_entry = sprintf("%s Error: %s%s (%s %s)", |
| | | $program, |
| | | $arg_arr['message'], |
| | | $arg_arr['file'] ? sprintf(' in %s on line %d', $arg_arr['file'], $arg_arr['line']) : '', |
| | | $_SERVER['REQUEST_METHOD'], |
| | | $_SERVER['REQUEST_URI'] . $post_query); |
| | | |
| | | if (!self::write_log('errors', $log_entry)) { |
| | | // send error to PHPs error handler if write_log didn't succeed |
| | | trigger_error($arg_arr['message']); |
| | | } |
| | | } |
| | | |
| | | // 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 ($level & 4) { |
| | | print "<b>$program Error"; |
| | | |
| | | if (!empty($arg_arr['file']) && !empty($arg_arr['line'])) { |
| | | print " in $arg_arr[file] ($arg_arr[line])"; |
| | | } |
| | | |
| | | print ':</b> '; |
| | | print nl2br($arg_arr['message']); |
| | | print '<br />'; |
| | | flush(); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns remote IP address and forwarded addresses if found |
| | | * |
| | | * @return string Remote IP address(es) |
| | | */ |
| | | public static function 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; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns current time (with microseconds). |
| | | * |
| | | * @return float Current time in seconds since the Unix |
| | | */ |
| | | public static function timer() |
| | | { |
| | | return microtime(true); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Logs time difference according to provided timer |
| | | * |
| | | * @param float $timer Timer (self::timer() result) |
| | | * @param string $label Log line prefix |
| | | * @param string $dest Log file name |
| | | * |
| | | * @see self::timer() |
| | | */ |
| | | public static function print_timer($timer, $label = 'Timer', $dest = 'console') |
| | | { |
| | | static $print_count = 0; |
| | | |
| | | $print_count++; |
| | | $now = self::timer(); |
| | | $diff = $now - $timer; |
| | | |
| | | if (empty($label)) { |
| | | $label = 'Timer '.$print_count; |
| | | } |
| | | |
| | | self::write_log($dest, sprintf("%s: %0.4f sec", $label, $diff)); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Getter for logged user ID. |
| | | * |
| | | * @return mixed User identifier |
| | | */ |
| | | public function get_user_id() |
| | | { |
| | | if (is_object($this->user)) { |
| | | return $this->user->ID; |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Getter for logged user name. |
| | | * |
| | | * @return string User name |
| | | */ |
| | | public function get_user_name() |
| | | { |
| | | if (is_object($this->user)) { |
| | | return $this->user->get_username(); |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Lightweight plugin API class serving as a dummy if plugins are not enabled |
| | | * |
| | | * @package Core |
| | | */ |
| | | class rcube_dummy_plugin_api |
| | | { |
| | | /** |
| | | * Triggers a plugin hook. |
| | | * @see rcube_plugin_api::exec_hook() |
| | | */ |
| | | public function exec_hook($hook, $args = array()) |
| | | { |
| | | return $args; |
| | | } |
| | | } |
| | | |
| | |
| | | */ |
| | | public function validate(&$save_data, $autofix = false) |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | |
| | | // check validity of email addresses |
| | | foreach ($this->get_col_values('email', $save_data, true) as $email) { |
| | | if (strlen($email)) { |
| | | if (!check_email(rcube_idn_to_ascii($email))) { |
| | | $this->set_error(self::ERROR_VALIDATE, rcube_label(array('name' => 'emailformaterror', 'vars' => array('email' => $email)))); |
| | | if (!$rcmail->check_email(rcube_idn_to_ascii($email))) { |
| | | $error = $rcmail->gettext(array('name' => 'emailformaterror', 'vars' => array('email' => $email))); |
| | | $this->set_error(self::ERROR_VALIDATE, $error); |
| | | return false; |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /* |
| | | +-----------------------------------------------------------------------+ |
| | | | program/include/rcube_base_replacer.php | |
| | | | | |
| | | | This file is part of the Roundcube Webmail client | |
| | | | Copyright (C) 2005-2012, The Roundcube Dev Team | |
| | | | | |
| | | | Licensed under the GNU General Public License version 3 or | |
| | | | any later version with exceptions for skins & plugins. | |
| | | | See the README file for a full license statement. | |
| | | | | |
| | | | PURPOSE: | |
| | | | Provide basic functions for base URL replacement | |
| | | | | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Thomas Bruederli <roundcube@gmail.com> | |
| | | +-----------------------------------------------------------------------+ |
| | | |
| | | $Id$ |
| | | |
| | | */ |
| | | |
| | | /** |
| | | * Helper class to turn relative urls into absolute ones |
| | | * using a predefined base |
| | | * |
| | | * @package Core |
| | | * @author Thomas Bruederli <roundcube@gmail.com> |
| | | */ |
| | | class rcube_base_replacer |
| | | { |
| | | private $base_url; |
| | | |
| | | |
| | | public function __construct($base) |
| | | { |
| | | $this->base_url = $base; |
| | | } |
| | | |
| | | |
| | | public function callback($matches) |
| | | { |
| | | 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; |
| | | } |
| | | } |
| | |
| | | */ |
| | | function __construct($type, $userid, $prefix='', $ttl=0, $packed=true) |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | $rcmail = rcube::get_instance(); |
| | | $type = strtolower($type); |
| | | |
| | | if ($type == 'memcache') { |
| | |
| | | { |
| | | if ($this->type == 'db' && $this->db) { |
| | | $this->db->query( |
| | | "DELETE FROM ".get_table_name('cache'). |
| | | "DELETE FROM ".$this->db->table_name('cache'). |
| | | " WHERE user_id = ?". |
| | | " AND cache_key LIKE ?". |
| | | " AND " . $this->db->unixtimestamp('created')." < ?", |
| | |
| | | else { |
| | | $sql_result = $this->db->limitquery( |
| | | "SELECT cache_id, data, cache_key". |
| | | " FROM ".get_table_name('cache'). |
| | | " FROM ".$this->db->table_name('cache'). |
| | | " WHERE user_id = ?". |
| | | " AND cache_key = ?". |
| | | // for better performance we allow more records for one key |
| | |
| | | // Remove NULL rows (here we don't need to check if the record exist) |
| | | if ($data == 'N;') { |
| | | $this->db->query( |
| | | "DELETE FROM ".get_table_name('cache'). |
| | | "DELETE FROM ".$this->db->table_name('cache'). |
| | | " WHERE user_id = ?". |
| | | " AND cache_key = ?", |
| | | $this->userid, $key); |
| | |
| | | // update existing cache record |
| | | if ($key_exists) { |
| | | $result = $this->db->query( |
| | | "UPDATE ".get_table_name('cache'). |
| | | "UPDATE ".$this->db->table_name('cache'). |
| | | " SET created = ". $this->db->now().", data = ?". |
| | | " WHERE user_id = ?". |
| | | " AND cache_key = ?", |
| | |
| | | // for better performance we allow more records for one key |
| | | // so, no need to check if record exist (see rcube_cache::read_record()) |
| | | $result = $this->db->query( |
| | | "INSERT INTO ".get_table_name('cache'). |
| | | "INSERT INTO ".$this->db->table_name('cache'). |
| | | " (created, user_id, cache_key, data)". |
| | | " VALUES (".$this->db->now().", ?, ?, ?)", |
| | | $this->userid, $key, $data); |
| | |
| | | } |
| | | |
| | | $this->db->query( |
| | | "DELETE FROM ".get_table_name('cache'). |
| | | "DELETE FROM ".$this->db->table_name('cache'). |
| | | " WHERE user_id = ?" . $where, |
| | | $this->userid); |
| | | } |
| | |
| | | |
| | | // fix default imap folders encoding |
| | | foreach (array('drafts_mbox', 'junk_mbox', 'sent_mbox', 'trash_mbox') as $folder) |
| | | $this->prop[$folder] = rcube_charset_convert($this->prop[$folder], RCMAIL_CHARSET, 'UTF7-IMAP'); |
| | | $this->prop[$folder] = rcube_charset::convert($this->prop[$folder], RCMAIL_CHARSET, 'UTF7-IMAP'); |
| | | |
| | | if (!empty($this->prop['default_folders'])) |
| | | foreach ($this->prop['default_folders'] as $n => $folder) |
| | | $this->prop['default_folders'][$n] = rcube_charset_convert($folder, RCMAIL_CHARSET, 'UTF7-IMAP'); |
| | | $this->prop['default_folders'][$n] = rcube_charset::convert($folder, RCMAIL_CHARSET, 'UTF7-IMAP'); |
| | | |
| | | // set PHP error logging according to config |
| | | if ($this->prop['debug_level'] & 1) { |
| | |
| | | $result = $def; |
| | | } |
| | | |
| | | $rcmail = rcmail::get_instance(); |
| | | $rcmail = rcube::get_instance(); |
| | | |
| | | if ($name == 'timezone' && isset($this->prop['_timezone_value'])) |
| | | $result = $this->prop['_timezone_value']; |
| | |
| | | { |
| | | // Bomb out if the requested key does not exist |
| | | if (!array_key_exists($key, $this->prop)) { |
| | | raise_error(array( |
| | | rcube::raise_error(array( |
| | | 'code' => 500, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Request for unconfigured crypto key \"$key\"" |
| | |
| | | |
| | | // Bomb out if the configured key is not exactly 24 bytes long |
| | | if (strlen($key) != 24) { |
| | | raise_error(array( |
| | | rcube::raise_error(array( |
| | | 'code' => 500, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Configured crypto key '$key' is not exactly 24 bytes long" |
| | |
| | | if ($delim == "\n" || $delim == "\r\n") |
| | | return $delim; |
| | | else |
| | | raise_error(array( |
| | | rcube::raise_error(array( |
| | | 'code' => 500, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Invalid mail_header_delimiter setting" |
| | |
| | | $domain = $this->prop['mail_domain'][$host]; |
| | | } |
| | | else if (!empty($this->prop['mail_domain'])) |
| | | $domain = rcube_parse_host($this->prop['mail_domain']); |
| | | $domain = rcmail::parse_host($this->prop['mail_domain']); |
| | | |
| | | if ($encode) |
| | | $domain = rcube_idn_to_ascii($domain); |
| | |
| | | $sql_filter = $search ? " AND " . $this->db->ilike('name', '%'.$search.'%') : ''; |
| | | |
| | | $sql_result = $this->db->query( |
| | | "SELECT * FROM ".get_table_name($this->db_groups). |
| | | "SELECT * FROM ".$this->db->table_name($this->db_groups). |
| | | " WHERE del<>1". |
| | | " AND user_id=?". |
| | | $sql_filter. |
| | |
| | | function get_group($group_id) |
| | | { |
| | | $sql_result = $this->db->query( |
| | | "SELECT * FROM ".get_table_name($this->db_groups). |
| | | "SELECT * FROM ".$this->db->table_name($this->db_groups). |
| | | " WHERE del<>1". |
| | | " AND contactgroup_id=?". |
| | | " AND user_id=?", |
| | |
| | | $length = $subset != 0 ? abs($subset) : $this->page_size; |
| | | |
| | | if ($this->group_id) |
| | | $join = " LEFT JOIN ".get_table_name($this->db_groupmembers)." AS m". |
| | | $join = " LEFT JOIN ".$this->db->table_name($this->db_groupmembers)." AS m". |
| | | " ON (m.contact_id = c.".$this->primary_key.")"; |
| | | |
| | | $order_col = (in_array($this->sort_col, $this->table_cols) ? $this->sort_col : 'name'); |
| | |
| | | $order_cols[] = 'c.email'; |
| | | |
| | | $sql_result = $this->db->limitquery( |
| | | "SELECT * FROM ".get_table_name($this->db_name)." AS c" . |
| | | "SELECT * FROM ".$this->db->table_name($this->db_name)." AS c" . |
| | | $join . |
| | | " WHERE c.del<>1" . |
| | | " AND c.user_id=?" . |
| | |
| | | private function _count() |
| | | { |
| | | if ($this->group_id) |
| | | $join = " LEFT JOIN ".get_table_name($this->db_groupmembers)." AS m". |
| | | $join = " LEFT JOIN ".$this->db->table_name($this->db_groupmembers)." AS m". |
| | | " ON (m.contact_id=c.".$this->primary_key.")"; |
| | | |
| | | // count contacts for this user |
| | | $sql_result = $this->db->query( |
| | | "SELECT COUNT(c.contact_id) AS rows". |
| | | " FROM ".get_table_name($this->db_name)." AS c". |
| | | " FROM ".$this->db->table_name($this->db_name)." AS c". |
| | | $join. |
| | | " WHERE c.del<>1". |
| | | " AND c.user_id=?". |
| | |
| | | return $assoc ? $first : $this->result; |
| | | |
| | | $this->db->query( |
| | | "SELECT * FROM ".get_table_name($this->db_name). |
| | | "SELECT * FROM ".$this->db->table_name($this->db_name). |
| | | " WHERE contact_id=?". |
| | | " AND user_id=?". |
| | | " AND del<>1", |
| | |
| | | return $results; |
| | | |
| | | $sql_result = $this->db->query( |
| | | "SELECT cgm.contactgroup_id, cg.name FROM " . get_table_name($this->db_groupmembers) . " AS cgm" . |
| | | " LEFT JOIN " . get_table_name($this->db_groups) . " AS cg ON (cgm.contactgroup_id = cg.contactgroup_id AND cg.del<>1)" . |
| | | "SELECT cgm.contactgroup_id, cg.name FROM " . $this->db->table_name($this->db_groupmembers) . " AS cgm" . |
| | | " LEFT JOIN " . $this->db->table_name($this->db_groups) . " AS cg ON (cgm.contactgroup_id = cg.contactgroup_id AND cg.del<>1)" . |
| | | " WHERE cgm.contact_id=?", |
| | | $id |
| | | ); |
| | |
| | | |
| | | if (!$existing->count && !empty($a_insert_cols)) { |
| | | $this->db->query( |
| | | "INSERT INTO ".get_table_name($this->db_name). |
| | | "INSERT INTO ".$this->db->table_name($this->db_name). |
| | | " (user_id, changed, del, ".join(', ', $a_insert_cols).")". |
| | | " VALUES (".intval($this->user_id).", ".$this->db->now().", 0, ".join(', ', $a_insert_values).")" |
| | | ); |
| | |
| | | |
| | | if (!empty($write_sql)) { |
| | | $this->db->query( |
| | | "UPDATE ".get_table_name($this->db_name). |
| | | "UPDATE ".$this->db->table_name($this->db_name). |
| | | " SET changed=".$this->db->now().", ".join(', ', $write_sql). |
| | | " WHERE contact_id=?". |
| | | " AND user_id=?". |
| | |
| | | |
| | | // flag record as deleted (always) |
| | | $this->db->query( |
| | | "UPDATE ".get_table_name($this->db_name). |
| | | "UPDATE ".$this->db->table_name($this->db_name). |
| | | " SET del=1, changed=".$this->db->now(). |
| | | " WHERE user_id=?". |
| | | " AND contact_id IN ($ids)", |
| | |
| | | |
| | | // clear deleted flag |
| | | $this->db->query( |
| | | "UPDATE ".get_table_name($this->db_name). |
| | | "UPDATE ".$this->db->table_name($this->db_name). |
| | | " SET del=0, changed=".$this->db->now(). |
| | | " WHERE user_id=?". |
| | | " AND contact_id IN ($ids)", |
| | |
| | | { |
| | | $this->cache = null; |
| | | |
| | | $this->db->query("UPDATE ".get_table_name($this->db_name). |
| | | $this->db->query("UPDATE ".$this->db->table_name($this->db_name). |
| | | " SET del=1, changed=".$this->db->now(). |
| | | " WHERE user_id = ?", $this->user_id); |
| | | |
| | |
| | | $name = $this->unique_groupname($name); |
| | | |
| | | $this->db->query( |
| | | "INSERT INTO ".get_table_name($this->db_groups). |
| | | "INSERT INTO ".$this->db->table_name($this->db_groups). |
| | | " (user_id, changed, name)". |
| | | " VALUES (".intval($this->user_id).", ".$this->db->now().", ".$this->db->quote($name).")" |
| | | ); |
| | |
| | | { |
| | | // flag group record as deleted |
| | | $sql_result = $this->db->query( |
| | | "UPDATE ".get_table_name($this->db_groups). |
| | | "UPDATE ".$this->db->table_name($this->db_groups). |
| | | " SET del=1, changed=".$this->db->now(). |
| | | " WHERE contactgroup_id=?". |
| | | " AND user_id=?", |
| | |
| | | $name = $this->unique_groupname($newname); |
| | | |
| | | $sql_result = $this->db->query( |
| | | "UPDATE ".get_table_name($this->db_groups). |
| | | "UPDATE ".$this->db->table_name($this->db_groups). |
| | | " SET name=?, changed=".$this->db->now(). |
| | | " WHERE contactgroup_id=?". |
| | | " AND user_id=?", |
| | |
| | | |
| | | // get existing assignments ... |
| | | $sql_result = $this->db->query( |
| | | "SELECT contact_id FROM ".get_table_name($this->db_groupmembers). |
| | | "SELECT contact_id FROM ".$this->db->table_name($this->db_groupmembers). |
| | | " WHERE contactgroup_id=?". |
| | | " AND contact_id IN (".$this->db->array2list($ids, 'integer').")", |
| | | $group_id |
| | |
| | | |
| | | foreach ($ids as $contact_id) { |
| | | $this->db->query( |
| | | "INSERT INTO ".get_table_name($this->db_groupmembers). |
| | | "INSERT INTO ".$this->db->table_name($this->db_groupmembers). |
| | | " (contactgroup_id, contact_id, created)". |
| | | " VALUES (?, ?, ".$this->db->now().")", |
| | | $group_id, |
| | |
| | | $ids = $this->db->array2list($ids, 'integer'); |
| | | |
| | | $sql_result = $this->db->query( |
| | | "DELETE FROM ".get_table_name($this->db_groupmembers). |
| | | "DELETE FROM ".$this->db->table_name($this->db_groupmembers). |
| | | " WHERE contactgroup_id=?". |
| | | " AND contact_id IN ($ids)", |
| | | $group_id |
| | |
| | | |
| | | do { |
| | | $sql_result = $this->db->query( |
| | | "SELECT 1 FROM ".get_table_name($this->db_groups). |
| | | "SELECT 1 FROM ".$this->db->table_name($this->db_groups). |
| | | " WHERE del<>1". |
| | | " AND user_id=?". |
| | | " AND name=?", |
| | |
| | | $this->options['ssl_mode'] = $use_ssl == 'imaps' ? 'ssl' : $use_ssl; |
| | | } |
| | | else if ($use_ssl) { |
| | | raise_error(array('code' => 403, 'type' => 'imap', |
| | | rcube::raise_error(array('code' => 403, 'type' => 'imap', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "OpenSSL not available"), true, false); |
| | | $port = 143; |
| | |
| | | |
| | | $attempt = 0; |
| | | do { |
| | | $data = rcmail::get_instance()->plugins->exec_hook('imap_connect', |
| | | $data = rcube::get_instance()->plugins->exec_hook('imap_connect', |
| | | array_merge($this->options, array('host' => $host, 'user' => $user, |
| | | 'attempt' => ++$attempt))); |
| | | |
| | |
| | | else if ($this->conn->error) { |
| | | if ($pass && $user) { |
| | | $message = sprintf("Login failed for %s from %s. %s", |
| | | $user, rcmail_remote_ip(), $this->conn->error); |
| | | $user, rcmail::remote_ip(), $this->conn->error); |
| | | |
| | | raise_error(array('code' => 403, 'type' => 'imap', |
| | | rcube::raise_error(array('code' => 403, 'type' => 'imap', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => $message), true, false); |
| | | } |
| | |
| | | return; |
| | | } |
| | | |
| | | $config = rcmail::get_instance()->config; |
| | | $config = rcube::get_instance()->config; |
| | | $imap_personal = $config->get('imap_ns_personal'); |
| | | $imap_other = $config->get('imap_ns_other'); |
| | | $imap_shared = $config->get('imap_ns_shared'); |
| | |
| | | $folder = $this->folder; |
| | | } |
| | | |
| | | return $this->messagecount($folder, $mode, $force, $status); |
| | | return $this->countmessages($folder, $mode, $force, $status); |
| | | } |
| | | |
| | | |
| | |
| | | * @return int Number of messages |
| | | * @see rcube_imap::count() |
| | | */ |
| | | protected function messagecount($folder, $mode='ALL', $force=false, $status=true) |
| | | protected function countmessages($folder, $mode='ALL', $force=false, $status=true) |
| | | { |
| | | $mode = strtoupper($mode); |
| | | |
| | |
| | | * protected method for setting threaded messages flags: |
| | | * depth, has_children and unread_children |
| | | * |
| | | * @param array $headers Reference to headers array indexed by message UID |
| | | * @param rcube_imap_result $threads Threads data object |
| | | * @param array $headers Reference to headers array indexed by message UID |
| | | * @param rcube_result_thread $threads Threads data object |
| | | * |
| | | * @return array Message headers array indexed by message UID |
| | | */ |
| | |
| | | |
| | | if ($sort) { |
| | | // use this class for message sorting |
| | | $sorter = new rcube_header_sorter(); |
| | | $sorter = new rcube_message_header_sorter(); |
| | | $sorter->set_index($msgs); |
| | | $sorter->sort_headers($a_msg_headers); |
| | | } |
| | |
| | | $old = $this->get_folder_stats($folder); |
| | | |
| | | // refresh message count -> will update |
| | | $this->messagecount($folder, 'ALL', true); |
| | | $this->countmessages($folder, 'ALL', true); |
| | | |
| | | $result = 0; |
| | | |
| | |
| | | foreach ($matches[1] as $m) { |
| | | $string_offset = $m[1] + strlen($m[0]) + 4; // {}\r\n |
| | | $string = substr($str, $string_offset - 1, $m[0]); |
| | | $string = rcube_charset_convert($string, $charset, $dest_charset); |
| | | $string = rcube_charset::convert($string, $charset, $dest_charset); |
| | | if ($string === false) { |
| | | continue; |
| | | } |
| | |
| | | * @param string $folder Folder to read from |
| | | * @param bool $force True to skip cache |
| | | * |
| | | * @return rcube_mail_header Message headers |
| | | * @return rcube_message_header Message headers |
| | | */ |
| | | public function get_message_headers($uid, $folder = null, $force = false) |
| | | { |
| | |
| | | * @param int $uid Message UID to fetch |
| | | * @param string $folder Folder to read from |
| | | * |
| | | * @return object rcube_mail_header Message data |
| | | * @return object rcube_message_header Message data |
| | | */ |
| | | public function get_message($uid, $folder = null) |
| | | { |
| | |
| | | $charset = $this->struct_charset; |
| | | } |
| | | else { |
| | | $charset = rc_detect_encoding($filename_mime, $this->default_charset); |
| | | $charset = rcube_charset::detect($filename_mime, $this->default_charset); |
| | | } |
| | | |
| | | $part->filename = rcube_mime::decode_mime_string($filename_mime, $charset); |
| | |
| | | $filename_encoded = $fmatches[2]; |
| | | } |
| | | |
| | | $part->filename = rcube_charset_convert(urldecode($filename_encoded), $filename_charset); |
| | | $part->filename = rcube_charset::convert(urldecode($filename_encoded), $filename_charset); |
| | | } |
| | | } |
| | | |
| | |
| | | $o_part->charset = $this->default_charset; |
| | | } |
| | | } |
| | | $body = rcube_charset_convert($body, $o_part->charset); |
| | | $body = rcube_charset::convert($body, $o_part->charset); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | $config = rcmail::get_instance()->config; |
| | | $config = rcube::get_instance()->config; |
| | | $to_trash = $to_mbox == $config->get('trash_mbox'); |
| | | |
| | | // flag messages as read before moving them |
| | |
| | | $a_defaults = $a_out = array(); |
| | | |
| | | // Give plugins a chance to provide a list of folders |
| | | $data = rcmail::get_instance()->plugins->exec_hook('storage_folders', |
| | | $data = rcube::get_instance()->plugins->exec_hook('storage_folders', |
| | | array('root' => $root, 'name' => $name, 'filter' => $filter, 'mode' => 'LSUB')); |
| | | |
| | | if (isset($data['folders'])) { |
| | |
| | | } |
| | | else { |
| | | // Server supports LIST-EXTENDED, we can use selection options |
| | | $config = rcmail::get_instance()->config; |
| | | $config = rcube::get_instance()->config; |
| | | // #1486225: Some dovecot versions returns wrong result using LIST-EXTENDED |
| | | if (!$config->get('imap_force_lsub') && $this->get_capability('LIST-EXTENDED')) { |
| | | // This will also set folder options, LSUB doesn't do that |
| | |
| | | protected function get_cache_engine() |
| | | { |
| | | if ($this->caching && !$this->cache) { |
| | | $rcmail = rcmail::get_instance(); |
| | | $rcmail = rcube::get_instance(); |
| | | $ttl = $rcmail->config->get('message_cache_lifetime', '10d') - mktime(); |
| | | $this->cache = $rcmail->get_cache('IMAP', $this->caching, $ttl); |
| | | } |
| | |
| | | $this->mcache->expunge($ttl); |
| | | } |
| | | |
| | | if ($this->cache) |
| | | if ($this->cache) { |
| | | $this->cache->expunge(); |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | protected function get_mcache_engine() |
| | | { |
| | | if ($this->messages_caching && !$this->mcache) { |
| | | $rcmail = rcmail::get_instance(); |
| | | $rcmail = rcube::get_instance(); |
| | | if ($dbh = $rcmail->get_dbh()) { |
| | | $this->mcache = new rcube_imap_cache( |
| | | $dbh, $this, $rcmail->user->ID, $this->options['skip_deleted']); |
| | | $dbh, $this, $rcmail->get_user_id(), $this->options['skip_deleted']); |
| | | } |
| | | } |
| | | |
| | |
| | | $a_defaults[$p] = $folder; |
| | | } |
| | | else { |
| | | $folders[$folder] = rcube_charset_convert($folder, 'UTF7-IMAP'); |
| | | $folders[$folder] = rcube_charset::convert($folder, 'UTF7-IMAP'); |
| | | } |
| | | } |
| | | |
| | |
| | | */ |
| | | public function debug_handler(&$imap, $message) |
| | | { |
| | | write_log('imap', $message); |
| | | rcmail::write_log('imap', $message); |
| | | } |
| | | |
| | | |
| | |
| | | { |
| | | $this->db = $db; |
| | | $this->imap = $imap; |
| | | $this->userid = (int)$userid; |
| | | $this->userid = $userid; |
| | | $this->skip_deleted = $skip_deleted; |
| | | } |
| | | |
| | |
| | | * @param string $mailbox Folder name |
| | | * @param array $msgs Message UIDs |
| | | * |
| | | * @return array The list of messages (rcube_mail_header) indexed by UID |
| | | * @return array The list of messages (rcube_message_header) indexed by UID |
| | | */ |
| | | function get_messages($mailbox, $msgs = array()) |
| | | { |
| | |
| | | // Fetch messages from cache |
| | | $sql_result = $this->db->query( |
| | | "SELECT uid, data, flags" |
| | | ." FROM ".get_table_name('cache_messages') |
| | | ." FROM ".$this->db->table_name('cache_messages') |
| | | ." WHERE user_id = ?" |
| | | ." AND mailbox = ?" |
| | | ." AND uid IN (".$this->db->array2list($msgs, 'integer').")", |
| | |
| | | * from IMAP server |
| | | * @param bool $no_cache Enables internal cache usage |
| | | * |
| | | * @return rcube_mail_header Message data |
| | | * @return rcube_message_header Message data |
| | | */ |
| | | function get_message($mailbox, $uid, $update = true, $cache = true) |
| | | { |
| | |
| | | |
| | | $sql_result = $this->db->query( |
| | | "SELECT flags, data" |
| | | ." FROM ".get_table_name('cache_messages') |
| | | ." FROM ".$this->db->table_name('cache_messages') |
| | | ." WHERE user_id = ?" |
| | | ." AND mailbox = ?" |
| | | ." AND uid = ?", |
| | |
| | | /** |
| | | * Saves the message in cache. |
| | | * |
| | | * @param string $mailbox Folder name |
| | | * @param rcube_mail_header $message Message data |
| | | * @param bool $force Skips message in-cache existance check |
| | | * @param string $mailbox Folder name |
| | | * @param rcube_message_header $message Message data |
| | | * @param bool $force Skips message in-cache existance check |
| | | */ |
| | | function add_message($mailbox, $message, $force = false) |
| | | { |
| | |
| | | // here will work as select, assume row exist if affected_rows=0) |
| | | if (!$force) { |
| | | $res = $this->db->query( |
| | | "UPDATE ".get_table_name('cache_messages') |
| | | "UPDATE ".$this->db->table_name('cache_messages') |
| | | ." SET flags = ?, data = ?, changed = ".$this->db->now() |
| | | ." WHERE user_id = ?" |
| | | ." AND mailbox = ?" |
| | |
| | | |
| | | // insert new record |
| | | $this->db->query( |
| | | "INSERT INTO ".get_table_name('cache_messages') |
| | | "INSERT INTO ".$this->db->table_name('cache_messages') |
| | | ." (user_id, mailbox, uid, flags, changed, data)" |
| | | ." VALUES (?, ?, ?, ?, ".$this->db->now().", ?)", |
| | | $this->userid, $mailbox, (int) $message->uid, $flags, $msg); |
| | |
| | | } |
| | | |
| | | $this->db->query( |
| | | "UPDATE ".get_table_name('cache_messages') |
| | | "UPDATE ".$this->db->table_name('cache_messages') |
| | | ." SET changed = ".$this->db->now() |
| | | .", flags = flags ".($enabled ? "+ $idx" : "- $idx") |
| | | ." WHERE user_id = ?" |
| | |
| | | { |
| | | if (!strlen($mailbox)) { |
| | | $this->db->query( |
| | | "DELETE FROM ".get_table_name('cache_messages') |
| | | "DELETE FROM ".$this->db->table_name('cache_messages') |
| | | ." WHERE user_id = ?", |
| | | $this->userid); |
| | | } |
| | |
| | | } |
| | | |
| | | $this->db->query( |
| | | "DELETE FROM ".get_table_name('cache_messages') |
| | | "DELETE FROM ".$this->db->table_name('cache_messages') |
| | | ." WHERE user_id = ?" |
| | | ." AND mailbox = ".$this->db->quote($mailbox) |
| | | ." AND mailbox = ?" |
| | | .($uids !== null ? " AND uid IN (".$this->db->array2list((array)$uids, 'integer').")" : ""), |
| | | $this->userid); |
| | | $this->userid, $mailbox); |
| | | } |
| | | |
| | | } |
| | |
| | | // otherwise use 'valid' flag to not loose HIGHESTMODSEQ value |
| | | if ($remove) { |
| | | $this->db->query( |
| | | "DELETE FROM ".get_table_name('cache_index') |
| | | ." WHERE user_id = ".intval($this->userid) |
| | | .(strlen($mailbox) ? " AND mailbox = ".$this->db->quote($mailbox) : "") |
| | | "DELETE FROM ".$this->db->table_name('cache_index') |
| | | ." WHERE user_id = ?" |
| | | .(strlen($mailbox) ? " AND mailbox = ".$this->db->quote($mailbox) : ""), |
| | | $this->userid |
| | | ); |
| | | } |
| | | else { |
| | | $this->db->query( |
| | | "UPDATE ".get_table_name('cache_index') |
| | | "UPDATE ".$this->db->table_name('cache_index') |
| | | ." SET valid = 0" |
| | | ." WHERE user_id = ".intval($this->userid) |
| | | .(strlen($mailbox) ? " AND mailbox = ".$this->db->quote($mailbox) : "") |
| | | ." WHERE user_id = ?" |
| | | .(strlen($mailbox) ? " AND mailbox = ".$this->db->quote($mailbox) : ""), |
| | | $this->userid |
| | | ); |
| | | } |
| | | |
| | |
| | | function remove_thread($mailbox = null) |
| | | { |
| | | $this->db->query( |
| | | "DELETE FROM ".get_table_name('cache_thread') |
| | | ." WHERE user_id = ".intval($this->userid) |
| | | .(strlen($mailbox) ? " AND mailbox = ".$this->db->quote($mailbox) : "") |
| | | "DELETE FROM ".$this->db->table_name('cache_thread') |
| | | ." WHERE user_id = ?" |
| | | .(strlen($mailbox) ? " AND mailbox = ".$this->db->quote($mailbox) : ""), |
| | | $this->userid |
| | | ); |
| | | |
| | | if (strlen($mailbox)) { |
| | |
| | | // Get index from DB |
| | | $sql_result = $this->db->query( |
| | | "SELECT data, valid" |
| | | ." FROM ".get_table_name('cache_index') |
| | | ." FROM ".$this->db->table_name('cache_index') |
| | | ." WHERE user_id = ?" |
| | | ." AND mailbox = ?", |
| | | $this->userid, $mailbox); |
| | |
| | | // Get thread from DB |
| | | $sql_result = $this->db->query( |
| | | "SELECT data" |
| | | ." FROM ".get_table_name('cache_thread') |
| | | ." FROM ".$this->db->table_name('cache_thread') |
| | | ." WHERE user_id = ?" |
| | | ." AND mailbox = ?", |
| | | $this->userid, $mailbox); |
| | |
| | | |
| | | if ($exists) { |
| | | $sql_result = $this->db->query( |
| | | "UPDATE ".get_table_name('cache_index') |
| | | "UPDATE ".$this->db->table_name('cache_index') |
| | | ." SET data = ?, valid = 1, changed = ".$this->db->now() |
| | | ." WHERE user_id = ?" |
| | | ." AND mailbox = ?", |
| | |
| | | } |
| | | else { |
| | | $sql_result = $this->db->query( |
| | | "INSERT INTO ".get_table_name('cache_index') |
| | | "INSERT INTO ".$this->db->table_name('cache_index') |
| | | ." (user_id, mailbox, data, valid, changed)" |
| | | ." VALUES (?, ?, ?, 1, ".$this->db->now().")", |
| | | $this->userid, $mailbox, $data); |
| | |
| | | |
| | | if ($exists) { |
| | | $sql_result = $this->db->query( |
| | | "UPDATE ".get_table_name('cache_thread') |
| | | "UPDATE ".$this->db->table_name('cache_thread') |
| | | ." SET data = ?, changed = ".$this->db->now() |
| | | ." WHERE user_id = ?" |
| | | ." AND mailbox = ?", |
| | |
| | | } |
| | | else { |
| | | $sql_result = $this->db->query( |
| | | "INSERT INTO ".get_table_name('cache_thread') |
| | | "INSERT INTO ".$this->db->table_name('cache_thread') |
| | | ." (user_id, mailbox, data, changed)" |
| | | ." VALUES (?, ?, ?, ".$this->db->now().")", |
| | | $this->userid, $mailbox, $data); |
| | |
| | | $uids = array(); |
| | | $sql_result = $this->db->query( |
| | | "SELECT uid" |
| | | ." FROM ".get_table_name('cache_messages') |
| | | ." FROM ".$this->db->table_name('cache_messages') |
| | | ." WHERE user_id = ?" |
| | | ." AND mailbox = ?", |
| | | $this->userid, $mailbox); |
| | |
| | | } |
| | | |
| | | $this->db->query( |
| | | "UPDATE ".get_table_name('cache_messages') |
| | | "UPDATE ".$this->db->table_name('cache_messages') |
| | | ." SET flags = ?, changed = ".$this->db->now() |
| | | ." WHERE user_id = ?" |
| | | ." AND mailbox = ?" |
| | |
| | | * |
| | | * @param array $sql_arr Message row data |
| | | * |
| | | * @return rcube_mail_header Message object |
| | | * @return rcube_message_header Message object |
| | | */ |
| | | private function build_message($sql_arr) |
| | | { |
| | |
| | | |
| | | */ |
| | | |
| | | /** |
| | | * Struct representing an e-mail message header |
| | | * |
| | | * @package Mail |
| | | * @author Aleksander Machniak <alec@alec.pl> |
| | | */ |
| | | class rcube_mail_header |
| | | { |
| | | public $id; |
| | | public $uid; |
| | | public $subject; |
| | | public $from; |
| | | public $to; |
| | | public $cc; |
| | | public $replyto; |
| | | public $in_reply_to; |
| | | public $date; |
| | | public $messageID; |
| | | public $size; |
| | | public $encoding; |
| | | public $charset; |
| | | public $ctype; |
| | | public $timestamp; |
| | | public $bodystructure; |
| | | public $internaldate; |
| | | public $references; |
| | | public $priority; |
| | | public $mdn_to; |
| | | public $others = array(); |
| | | public $flags = array(); |
| | | } |
| | | // for backward copat. |
| | | class rcube_mail_header extends rcube_message_header { } |
| | | |
| | | // For backward compatibility with cached messages (#1486602) |
| | | class iilBasicHeader extends rcube_mail_header |
| | | { |
| | | } |
| | | |
| | | /** |
| | | * PHP based wrapper class to connect to an IMAP server |
| | |
| | | */ |
| | | function sort($mailbox, $field, $add='', $return_uid=false, $encoding = 'US-ASCII') |
| | | { |
| | | require_once dirname(__FILE__) . '/rcube_result_index.php'; |
| | | |
| | | $field = strtoupper($field); |
| | | if ($field == 'INTERNALDATE') { |
| | | $field = 'ARRIVAL'; |
| | |
| | | */ |
| | | function thread($mailbox, $algorithm='REFERENCES', $criteria='', $return_uid=false, $encoding='US-ASCII') |
| | | { |
| | | require_once dirname(__FILE__) . '/rcube_result_thread.php'; |
| | | |
| | | $old_sel = $this->selected; |
| | | |
| | | if (!$this->select($mailbox)) { |
| | |
| | | */ |
| | | function search($mailbox, $criteria, $return_uid=false, $items=array()) |
| | | { |
| | | require_once dirname(__FILE__) . '/rcube_result_index.php'; |
| | | |
| | | $old_sel = $this->selected; |
| | | |
| | | if (!$this->select($mailbox)) { |
| | |
| | | function index($mailbox, $message_set, $index_field='', $skip_deleted=true, |
| | | $uidfetch=false, $return_uid=false) |
| | | { |
| | | require_once dirname(__FILE__) . '/rcube_result_index.php'; |
| | | |
| | | $msg_index = $this->fetchHeaderIndex($mailbox, $message_set, |
| | | $index_field, $skip_deleted, $uidfetch, $return_uid); |
| | | |
| | |
| | | * @param string $mod_seq Modification sequence for CHANGEDSINCE (RFC4551) query |
| | | * @param bool $vanished Enables VANISHED parameter (RFC5162) for CHANGEDSINCE query |
| | | * |
| | | * @return array List of rcube_mail_header elements, False on error |
| | | * @return array List of rcube_message_header elements, False on error |
| | | * @since 0.6 |
| | | */ |
| | | function fetch($mailbox, $message_set, $is_uid = false, $query_items = array(), |
| | |
| | | if (preg_match('/^\* ([0-9]+) FETCH/', $line, $m)) { |
| | | $id = intval($m[1]); |
| | | |
| | | $result[$id] = new rcube_mail_header; |
| | | $result[$id] = new rcube_message_header; |
| | | $result[$id]->id = $id; |
| | | $result[$id]->subject = ''; |
| | | $result[$id]->messageID = 'mid:' . $id; |
| | |
| | | /** |
| | | * Object constructor |
| | | * |
| | | * @param array LDAP connection properties |
| | | * @param boolean Enables debug mode |
| | | * @param string Current user mail domain name |
| | | * @param integer User-ID |
| | | * @param array $p LDAP connection properties |
| | | * @param boolean $debug Enables debug mode |
| | | * @param string $mail_domain Current user mail domain name |
| | | */ |
| | | function __construct($p, $debug=false, $mail_domain=NULL) |
| | | function __construct($p, $debug = false, $mail_domain = null) |
| | | { |
| | | $this->prop = $p; |
| | | |
| | |
| | | */ |
| | | private function _connect() |
| | | { |
| | | global $RCMAIL; |
| | | $RCMAIL = rcmail::get_instance(); |
| | | |
| | | if (!function_exists('ldap_connect')) |
| | | raise_error(array('code' => 100, 'type' => 'ldap', |
| | | rcube::raise_error(array('code' => 100, 'type' => 'ldap', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "No ldap support in this installation of PHP"), |
| | | true, true); |
| | |
| | | |
| | | foreach ($this->prop['hosts'] as $host) |
| | | { |
| | | $host = idn_to_ascii(rcube_parse_host($host)); |
| | | $host = idn_to_ascii(rcmail::parse_host($host)); |
| | | $hostname = $host.($this->prop['port'] ? ':'.$this->prop['port'] : ''); |
| | | |
| | | $this->_debug("C: Connect [$hostname] [{$this->prop['name']}]"); |
| | |
| | | } |
| | | |
| | | if (!is_resource($this->conn)) { |
| | | raise_error(array('code' => 100, 'type' => 'ldap', |
| | | rcube::raise_error(array('code' => 100, 'type' => 'ldap', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Could not connect to any LDAP server, last tried $hostname"), true); |
| | | |
| | |
| | | } |
| | | |
| | | // Get the pieces needed for variable replacement. |
| | | if ($fu = $RCMAIL->user->get_username()) |
| | | if ($fu = $RCMAIL->get_user_name()) |
| | | list($u, $d) = explode('@', $fu); |
| | | else |
| | | $d = $this->mail_domain; |
| | |
| | | if (!empty($this->prop['search_dn_default'])) |
| | | $replaces['%dn'] = $this->prop['search_dn_default']; |
| | | else { |
| | | raise_error(array( |
| | | rcube::raise_error(array( |
| | | 'code' => 100, 'type' => 'ldap', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "DN not found using LDAP search."), true); |
| | |
| | | } |
| | | |
| | | if (!function_exists('ldap_sasl_bind')) { |
| | | raise_error(array('code' => 100, 'type' => 'ldap', |
| | | rcube::raise_error(array('code' => 100, 'type' => 'ldap', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Unable to bind: ldap_sasl_bind() not exists"), |
| | | true, true); |
| | |
| | | |
| | | $this->_debug("S: ".ldap_error($this->conn)); |
| | | |
| | | raise_error(array( |
| | | rcube::raise_error(array( |
| | | 'code' => ldap_errno($this->conn), 'type' => 'ldap', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Bind failed for authcid=$authc ".ldap_error($this->conn)), |
| | |
| | | |
| | | $this->_debug("S: ".ldap_error($this->conn)); |
| | | |
| | | raise_error(array( |
| | | rcube::raise_error(array( |
| | | 'code' => ldap_errno($this->conn), 'type' => 'ldap', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Bind failed for dn=$dn: ".ldap_error($this->conn)), |
| | |
| | | */ |
| | | private function _debug($str) |
| | | { |
| | | if ($this->debug) |
| | | write_log('ldap', $str); |
| | | if ($this->debug) { |
| | | rcmail::write_log('ldap', $str); |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | * @param string $db_dsnw DSN for read/write operations |
| | | * @param string $db_dsnr Optional DSN for read only operations |
| | | */ |
| | | function __construct($db_dsnw, $db_dsnr='', $pconn=false) |
| | | public function __construct($db_dsnw, $db_dsnr='', $pconn=false) |
| | | { |
| | | if (empty($db_dsnr)) |
| | | if (empty($db_dsnr)) { |
| | | $db_dsnr = $db_dsnw; |
| | | } |
| | | |
| | | $this->db_dsnw = $db_dsnw; |
| | | $this->db_dsnr = $db_dsnr; |
| | |
| | | 'emulate_prepared' => $this->debug_mode, |
| | | 'debug' => $this->debug_mode, |
| | | 'debug_handler' => array($this, 'debug_handler'), |
| | | 'portability' => MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_EMPTY_TO_NULL); |
| | | 'portability' => MDB2_PORTABILITY_ALL ^ MDB2_PORTABILITY_EMPTY_TO_NULL, |
| | | ); |
| | | |
| | | if ($this->db_provider == 'pgsql') { |
| | | $db_options['disable_smart_seqname'] = true; |
| | |
| | | $this->db_error = true; |
| | | $this->db_error_msg = $dbh->getMessage(); |
| | | |
| | | raise_error(array('code' => 500, 'type' => 'db', |
| | | rcube::raise_error(array('code' => 500, 'type' => 'db', |
| | | 'line' => __LINE__, 'file' => __FILE__, |
| | | 'message' => $dbh->getUserInfo()), true, false); |
| | | } |
| | | else if ($this->db_provider == 'sqlite') { |
| | | $dsn_array = MDB2::parseDSN($dsn); |
| | | if (!filesize($dsn_array['database']) && !empty($this->sqlite_initials)) |
| | | $this->_sqlite_create_database($dbh, $this->sqlite_initials); |
| | | if (!filesize($dsn_array['database']) && !empty($this->sqlite_initials)) { |
| | | $this->sqlite_create_database($dbh, $this->sqlite_initials); |
| | | } |
| | | } |
| | | else if ($this->db_provider!='mssql' && $this->db_provider!='sqlsrv') |
| | | else if ($this->db_provider != 'mssql' && $this->db_provider != 'sqlsrv') { |
| | | $dbh->setCharset('utf8'); |
| | | } |
| | | |
| | | return $dbh; |
| | | } |
| | |
| | | * Connect to appropiate database depending on the operation |
| | | * |
| | | * @param string $mode Connection mode (r|w) |
| | | * @access public |
| | | */ |
| | | function db_connect($mode) |
| | | public function db_connect($mode) |
| | | { |
| | | // previous connection failed, don't attempt to connect again |
| | | if ($this->conn_failure) { |
| | |
| | | $this->db_connected = !PEAR::isError($this->db_handle); |
| | | } |
| | | |
| | | if ($this->db_connected) |
| | | if ($this->db_connected) { |
| | | $this->db_mode = $mode; |
| | | else |
| | | } |
| | | else { |
| | | $this->conn_failure = true; |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | * Activate/deactivate debug mode |
| | | * |
| | | * @param boolean $dbg True if SQL queries should be logged |
| | | * @access public |
| | | */ |
| | | function set_debug($dbg = true) |
| | | public function set_debug($dbg = true) |
| | | { |
| | | $this->debug_mode = $dbg; |
| | | if ($this->db_connected) { |
| | |
| | | * Getter for error state |
| | | * |
| | | * @param boolean True on error |
| | | * @access public |
| | | */ |
| | | function is_error() |
| | | public function is_error() |
| | | { |
| | | return $this->db_error ? $this->db_error_msg : false; |
| | | } |
| | |
| | | * Connection state checker |
| | | * |
| | | * @param boolean True if in connected state |
| | | * @access public |
| | | */ |
| | | function is_connected() |
| | | public function is_connected() |
| | | { |
| | | return PEAR::isError($this->db_handle) ? false : $this->db_connected; |
| | | } |
| | |
| | | * Is database replication configured? |
| | | * This returns true if dsnw != dsnr |
| | | */ |
| | | function is_replicated() |
| | | public function is_replicated() |
| | | { |
| | | return !empty($this->db_dsnr) && $this->db_dsnw != $this->db_dsnr; |
| | | } |
| | |
| | | * |
| | | * @param string SQL query to execute |
| | | * @param mixed Values to be inserted in query |
| | | * |
| | | * @return number Query handle identifier |
| | | * @access public |
| | | */ |
| | | function query() |
| | | public function query() |
| | | { |
| | | $params = func_get_args(); |
| | | $query = array_shift($params); |
| | | |
| | | // Support one argument of type array, instead of n arguments |
| | | if (count($params) == 1 && is_array($params[0])) |
| | | if (count($params) == 1 && is_array($params[0])) { |
| | | $params = $params[0]; |
| | | } |
| | | |
| | | return $this->_query($query, 0, 0, $params); |
| | | } |
| | |
| | | * @param number Offset for LIMIT statement |
| | | * @param number Number of rows for LIMIT statement |
| | | * @param mixed Values to be inserted in query |
| | | * |
| | | * @return number Query handle identifier |
| | | * @access public |
| | | */ |
| | | function limitquery() |
| | | public function limitquery() |
| | | { |
| | | $params = func_get_args(); |
| | | $query = array_shift($params); |
| | |
| | | $this->db_connect($mode); |
| | | |
| | | // check connection before proceeding |
| | | if (!$this->is_connected()) |
| | | if (!$this->is_connected()) { |
| | | return null; |
| | | } |
| | | |
| | | if ($this->db_provider == 'sqlite') |
| | | $this->_sqlite_prepare(); |
| | | if ($this->db_provider == 'sqlite') { |
| | | $this->sqlite_prepare(); |
| | | } |
| | | |
| | | if ($numrows || $offset) |
| | | if ($numrows || $offset) { |
| | | $result = $this->db_handle->setLimit($numrows,$offset); |
| | | } |
| | | |
| | | if (empty($params)) |
| | | if (empty($params)) { |
| | | $result = $mode == 'r' ? $this->db_handle->query($query) : $this->db_handle->exec($query); |
| | | } |
| | | else { |
| | | $params = (array)$params; |
| | | $q = $this->db_handle->prepare($query, null, $mode=='w' ? MDB2_PREPARE_MANIP : null); |
| | |
| | | $this->db_error = true; |
| | | $this->db_error_msg = $q->userinfo; |
| | | |
| | | raise_error(array('code' => 500, 'type' => 'db', |
| | | rcube::raise_error(array('code' => 500, 'type' => 'db', |
| | | 'line' => __LINE__, 'file' => __FILE__, |
| | | 'message' => $this->db_error_msg), true, false); |
| | | |
| | |
| | | * |
| | | * @param number $res_id Optional query handle identifier |
| | | * @return mixed Number of rows or false on failure |
| | | * @access public |
| | | */ |
| | | function num_rows($res_id=null) |
| | | public function num_rows($res_id=null) |
| | | { |
| | | if (!$this->db_connected) |
| | | if (!$this->db_connected) { |
| | | return false; |
| | | } |
| | | |
| | | if ($result = $this->_get_result($res_id)) |
| | | if ($result = $this->_get_result($res_id)) { |
| | | return $result->numRows(); |
| | | else |
| | | return false; |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | |
| | |
| | | * |
| | | * @param number $res_id Optional query handle identifier |
| | | * @return mixed Number of rows or false on failure |
| | | * @access public |
| | | */ |
| | | function affected_rows($res_id = null) |
| | | public function affected_rows($res_id = null) |
| | | { |
| | | if (!$this->db_connected) |
| | | if (!$this->db_connected) { |
| | | return false; |
| | | } |
| | | |
| | | return $this->_get_result($res_id); |
| | | } |
| | |
| | | * For Postgres databases, a sequence name is required |
| | | * |
| | | * @param string $table Table name (to find the incremented sequence) |
| | | * |
| | | * @return mixed ID or false on failure |
| | | * @access public |
| | | */ |
| | | function insert_id($table = '') |
| | | public function insert_id($table = '') |
| | | { |
| | | if (!$this->db_connected || $this->db_mode == 'r') |
| | | if (!$this->db_connected || $this->db_mode == 'r') { |
| | | return false; |
| | | } |
| | | |
| | | if ($table) { |
| | | if ($this->db_provider == 'pgsql') |
| | | if ($this->db_provider == 'pgsql') { |
| | | // find sequence name |
| | | $table = get_sequence_name($table); |
| | | else |
| | | $table = $this->sequence_name($table); |
| | | } |
| | | else { |
| | | // resolve table name |
| | | $table = get_table_name($table); |
| | | $table = $this->table_name($table); |
| | | } |
| | | } |
| | | |
| | | $id = $this->db_handle->lastInsertID($table); |
| | |
| | | * If no query handle is specified, the last query will be taken as reference |
| | | * |
| | | * @param number $res_id Optional query handle identifier |
| | | * |
| | | * @return mixed Array with col values or false on failure |
| | | * @access public |
| | | */ |
| | | function fetch_assoc($res_id=null) |
| | | public function fetch_assoc($res_id = null) |
| | | { |
| | | $result = $this->_get_result($res_id); |
| | | return $this->_fetch_row($result, MDB2_FETCHMODE_ASSOC); |
| | |
| | | * If no query handle is specified, the last query will be taken as reference |
| | | * |
| | | * @param number $res_id Optional query handle identifier |
| | | * |
| | | * @return mixed Array with col values or false on failure |
| | | * @access public |
| | | */ |
| | | function fetch_array($res_id=null) |
| | | public function fetch_array($res_id = null) |
| | | { |
| | | $result = $this->_get_result($res_id); |
| | | return $this->_fetch_row($result, MDB2_FETCHMODE_ORDERED); |
| | |
| | | * |
| | | * @param MDB2_Result_Common Query $result result handle |
| | | * @param number $mode Fetch mode identifier |
| | | * @return mixed Array with col values or false on failure |
| | | * @access private |
| | | * |
| | | * @return mixed Array with col values or false on failure |
| | | */ |
| | | private function _fetch_row($result, $mode) |
| | | { |
| | | if ($result === false || PEAR::isError($result) || !$this->is_connected()) |
| | | if ($result === false || PEAR::isError($result) || !$this->is_connected()) { |
| | | return false; |
| | | } |
| | | |
| | | return $result->fetchRow($mode); |
| | | } |
| | |
| | | * Wrapper for the SHOW TABLES command |
| | | * |
| | | * @return array List of all tables of the current database |
| | | * @access public |
| | | * @since 0.4-beta |
| | | */ |
| | | function list_tables() |
| | | public function list_tables() |
| | | { |
| | | // get tables if not cached |
| | | if (!$this->tables) { |
| | | $this->db_handle->loadModule('Manager'); |
| | | if (!PEAR::isError($result = $this->db_handle->listTables())) |
| | | if (!PEAR::isError($result = $this->db_handle->listTables())) { |
| | | $this->tables = $result; |
| | | else |
| | | } |
| | | else { |
| | | $this->tables = array(); |
| | | } |
| | | } |
| | | |
| | | return $this->tables; |
| | |
| | | * Wrapper for SHOW COLUMNS command |
| | | * |
| | | * @param string Table name |
| | | * |
| | | * @return array List of table cols |
| | | */ |
| | | function list_cols($table) |
| | | public function list_cols($table) |
| | | { |
| | | $this->db_handle->loadModule('Manager'); |
| | | if (!PEAR::isError($result = $this->db_handle->listTableFields($table))) { |
| | |
| | | * |
| | | * @param mixed $input Value to quote |
| | | * @param string $type Type of data |
| | | * |
| | | * @return string Quoted/converted string for use in query |
| | | * @access public |
| | | */ |
| | | function quote($input, $type = null) |
| | | public function quote($input, $type = null) |
| | | { |
| | | // handle int directly for better performance |
| | | if ($type == 'integer') |
| | | if ($type == 'integer') { |
| | | return intval($input); |
| | | } |
| | | |
| | | // create DB handle if not available |
| | | if (!$this->db_handle) |
| | | if (!$this->db_handle) { |
| | | $this->db_connect('r'); |
| | | } |
| | | |
| | | return $this->db_connected ? $this->db_handle->quote($input, $type) : addslashes($input); |
| | | } |
| | |
| | | * Quotes a string so it can be safely used as a table or column name |
| | | * |
| | | * @param string $str Value to quote |
| | | * |
| | | * @return string Quoted string for use in query |
| | | * @deprecated Replaced by rcube_MDB2::quote_identifier |
| | | * @see rcube_mdb2::quote_identifier |
| | | * @access public |
| | | */ |
| | | function quoteIdentifier($str) |
| | | public function quoteIdentifier($str) |
| | | { |
| | | return $this->quote_identifier($str); |
| | | } |
| | |
| | | * Quotes a string so it can be safely used as a table or column name |
| | | * |
| | | * @param string $str Value to quote |
| | | * |
| | | * @return string Quoted string for use in query |
| | | * @access public |
| | | */ |
| | | function quote_identifier($str) |
| | | public function quote_identifier($str) |
| | | { |
| | | if (!$this->db_handle) |
| | | if (!$this->db_handle) { |
| | | $this->db_connect('r'); |
| | | } |
| | | |
| | | return $this->db_connected ? $this->db_handle->quoteIdentifier($str) : $str; |
| | | } |
| | |
| | | * Escapes a string |
| | | * |
| | | * @param string $str The string to be escaped |
| | | * |
| | | * @return string The escaped string |
| | | * @access public |
| | | * @since 0.1.1 |
| | | */ |
| | | function escapeSimple($str) |
| | | public function escapeSimple($str) |
| | | { |
| | | if (!$this->db_handle) |
| | | if (!$this->db_handle) { |
| | | $this->db_connect('r'); |
| | | } |
| | | |
| | | return $this->db_handle->escape($str); |
| | | } |
| | |
| | | * Return SQL function for current time and date |
| | | * |
| | | * @return string SQL function to use in query |
| | | * @access public |
| | | */ |
| | | function now() |
| | | public function now() |
| | | { |
| | | switch ($this->db_provider) { |
| | | case 'mssql': |
| | |
| | | * |
| | | * @param array $arr Input array |
| | | * @param string $type Type of data |
| | | * |
| | | * @return string Comma-separated list of quoted values for use in query |
| | | * @access public |
| | | */ |
| | | function array2list($arr, $type = null) |
| | | public function array2list($arr, $type = null) |
| | | { |
| | | if (!is_array($arr)) |
| | | if (!is_array($arr)) { |
| | | return $this->quote($arr, $type); |
| | | } |
| | | |
| | | foreach ($arr as $idx => $item) |
| | | foreach ($arr as $idx => $item) { |
| | | $arr[$idx] = $this->quote($item, $type); |
| | | } |
| | | |
| | | return implode(',', $arr); |
| | | } |
| | |
| | | * of timestamp functions in Mysql (year 2038 problem) |
| | | * |
| | | * @param string $field Field name |
| | | * |
| | | * @return string SQL statement to use in query |
| | | * @deprecated |
| | | */ |
| | | function unixtimestamp($field) |
| | | public function unixtimestamp($field) |
| | | { |
| | | switch($this->db_provider) { |
| | | case 'pgsql': |
| | |
| | | * Return SQL statement to convert from a unix timestamp |
| | | * |
| | | * @param string $timestamp Field name |
| | | * |
| | | * @return string SQL statement to use in query |
| | | * @access public |
| | | */ |
| | | function fromunixtime($timestamp) |
| | | public function fromunixtime($timestamp) |
| | | { |
| | | return date("'Y-m-d H:i:s'", $timestamp); |
| | | } |
| | |
| | | * |
| | | * @param string $column Field name |
| | | * @param string $value Search value |
| | | * |
| | | * @return string SQL statement to use in query |
| | | * @access public |
| | | */ |
| | | function ilike($column, $value) |
| | | public function ilike($column, $value) |
| | | { |
| | | // TODO: use MDB2's matchPattern() function |
| | | switch($this->db_provider) { |
| | | switch ($this->db_provider) { |
| | | case 'pgsql': |
| | | return $this->quote_identifier($column).' ILIKE '.$this->quote($value); |
| | | default: |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Abstract SQL statement for value concatenation |
| | | * |
| | | * @return string SQL statement to be used in query |
| | | * @access public |
| | | */ |
| | | function concat(/* col1, col2, ... */) |
| | | public function concat(/* col1, col2, ... */) |
| | | { |
| | | $func = ''; |
| | | $args = func_get_args(); |
| | | if (is_array($args[0])) |
| | | $args = $args[0]; |
| | | |
| | | switch($this->db_provider) { |
| | | switch ($this->db_provider) { |
| | | case 'mysql': |
| | | case 'mysqli': |
| | | $func = 'CONCAT'; |
| | |
| | | * Encodes non-UTF-8 characters in string/array/object (recursive) |
| | | * |
| | | * @param mixed $input Data to fix |
| | | * |
| | | * @return mixed Properly UTF-8 encoded data |
| | | * @access public |
| | | */ |
| | | function encode($input) |
| | | public static function encode($input) |
| | | { |
| | | if (is_object($input)) { |
| | | foreach (get_object_vars($input) as $idx => $value) |
| | | $input->$idx = $this->encode($value); |
| | | foreach (get_object_vars($input) as $idx => $value) { |
| | | $input->$idx = self::encode($value); |
| | | } |
| | | return $input; |
| | | } |
| | | else if (is_array($input)) { |
| | | foreach ($input as $idx => $value) |
| | | $input[$idx] = $this->encode($value); |
| | | return $input; |
| | | foreach ($input as $idx => $value) { |
| | | $input[$idx] = self::encode($value); |
| | | } |
| | | return $input; |
| | | } |
| | | |
| | | return utf8_encode($input); |
| | |
| | | * Decodes encoded UTF-8 string/object/array (recursive) |
| | | * |
| | | * @param mixed $input Input data |
| | | * |
| | | * @return mixed Decoded data |
| | | * @access public |
| | | */ |
| | | function decode($input) |
| | | public static function decode($input) |
| | | { |
| | | if (is_object($input)) { |
| | | foreach (get_object_vars($input) as $idx => $value) |
| | | $input->$idx = $this->decode($value); |
| | | foreach (get_object_vars($input) as $idx => $value) { |
| | | $input->$idx = self::decode($value); |
| | | } |
| | | return $input; |
| | | } |
| | | else if (is_array($input)) { |
| | | foreach ($input as $idx => $value) |
| | | $input[$idx] = $this->decode($value); |
| | | return $input; |
| | | foreach ($input as $idx => $value) { |
| | | $input[$idx] = self::decode($value); |
| | | } |
| | | return $input; |
| | | } |
| | | |
| | | return utf8_decode($input); |
| | |
| | | * Adds a query result and returns a handle ID |
| | | * |
| | | * @param object $res Query handle |
| | | * |
| | | * @return mixed Handle ID |
| | | * @access private |
| | | */ |
| | | private function _add_result($res) |
| | | { |
| | |
| | | if (PEAR::isError($res)) { |
| | | $this->db_error = true; |
| | | $this->db_error_msg = $res->getMessage(); |
| | | raise_error(array('code' => 500, 'type' => 'db', |
| | | rcube::raise_error(array('code' => 500, 'type' => 'db', |
| | | 'line' => __LINE__, 'file' => __FILE__, |
| | | 'message' => $res->getMessage() . " Query: " |
| | | . substr(preg_replace('/[\r\n]+\s*/', ' ', $res->userinfo), 0, 512)), |
| | |
| | | * If no ID is specified, the last resource handle will be returned |
| | | * |
| | | * @param number $res_id Handle ID |
| | | * |
| | | * @return mixed Resource handle or false on failure |
| | | * @access private |
| | | */ |
| | | private function _get_result($res_id = null) |
| | | { |
| | | if ($res_id == null) |
| | | if ($res_id == null) { |
| | | $res_id = $this->last_res_id; |
| | | } |
| | | |
| | | if (isset($this->a_query_results[$res_id])) |
| | | if (!PEAR::isError($this->a_query_results[$res_id])) |
| | | if (isset($this->a_query_results[$res_id])) { |
| | | if (!PEAR::isError($this->a_query_results[$res_id])) { |
| | | return $this->a_query_results[$res_id]; |
| | | } |
| | | } |
| | | |
| | | return false; |
| | | } |
| | |
| | | * |
| | | * @param MDB2 $dbh SQLite database handle |
| | | * @param string $file_name File path to use for DB creation |
| | | * @access private |
| | | */ |
| | | private function _sqlite_create_database($dbh, $file_name) |
| | | private function sqlite_create_database($dbh, $file_name) |
| | | { |
| | | if (empty($file_name) || !is_string($file_name)) |
| | | if (empty($file_name) || !is_string($file_name)) { |
| | | return; |
| | | } |
| | | |
| | | $data = file_get_contents($file_name); |
| | | |
| | | if (strlen($data)) |
| | | if (!sqlite_exec($dbh->connection, $data, $error) || MDB2::isError($dbh)) |
| | | raise_error(array('code' => 500, 'type' => 'db', |
| | | if (strlen($data)) { |
| | | if (!sqlite_exec($dbh->connection, $data, $error) || MDB2::isError($dbh)) { |
| | | rcube::raise_error(array('code' => 500, 'type' => 'db', |
| | | 'line' => __LINE__, 'file' => __FILE__, |
| | | 'message' => $error), true, false); |
| | | 'message' => $error), true, false); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Add some proprietary database functions to the current SQLite handle |
| | | * in order to make it MySQL compatible |
| | | * |
| | | * @access private |
| | | */ |
| | | private function _sqlite_prepare() |
| | | private function sqlite_prepare() |
| | | { |
| | | include_once(INSTALL_PATH . 'program/include/rcube_sqlite.inc'); |
| | | |
| | | // we emulate via callback some missing MySQL function |
| | | // we emulate via callback some missing MySQL functions |
| | | sqlite_create_function($this->db_handle->connection, |
| | | 'from_unixtime', 'rcube_sqlite_from_unixtime'); |
| | | 'unix_timestamp', array('rcube_mdb2', 'sqlite_unix_timestamp')); |
| | | sqlite_create_function($this->db_handle->connection, |
| | | 'unix_timestamp', 'rcube_sqlite_unix_timestamp'); |
| | | sqlite_create_function($this->db_handle->connection, |
| | | 'now', 'rcube_sqlite_now'); |
| | | sqlite_create_function($this->db_handle->connection, |
| | | 'md5', 'rcube_sqlite_md5'); |
| | | 'now', array('rcube_mdb2', 'sqlite_now')); |
| | | } |
| | | |
| | | |
| | |
| | | if ($scope != 'prepare') { |
| | | $debug_output = sprintf('%s(%d): %s;', |
| | | $scope, $db->db_index, rtrim($message, ';')); |
| | | write_log('sql', $debug_output); |
| | | rcmail::write_log('sql', $debug_output); |
| | | } |
| | | } |
| | | |
| | | } // end class rcube_db |
| | | |
| | | /** |
| | | * Return correct name for a specific database table |
| | | * |
| | | * @param string $table Table name |
| | | * |
| | | * @return string Translated table name |
| | | */ |
| | | public function table_name($table) |
| | | { |
| | | $rcmail = rcube::get_instance(); |
| | | |
| | | // return table name if configured |
| | | $config_key = 'db_table_'.$table; |
| | | |
| | | if ($name = $rcmail->config->get($config_key)) { |
| | | return $name; |
| | | } |
| | | |
| | | return $table; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return correct name for a specific database sequence |
| | | * (used for Postgres only) |
| | | * |
| | | * @param string $sequence Secuence name |
| | | * |
| | | * @return string Translated sequence name |
| | | */ |
| | | public function sequence_name($sequence) |
| | | { |
| | | $rcmail = rcube::get_instance(); |
| | | |
| | | // return sequence name if configured |
| | | $config_key = 'db_sequence_'.$sequence; |
| | | |
| | | if ($name = $rcmail->config->get($config_key)) { |
| | | return $name; |
| | | } |
| | | |
| | | return $sequence; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Callback for sqlite: unix_timestamp() |
| | | */ |
| | | public static function sqlite_unix_timestamp($timestamp = '') |
| | | { |
| | | $timestamp = trim($timestamp); |
| | | if (!$timestamp) { |
| | | $ret = time(); |
| | | } |
| | | else if (!preg_match('/^[0-9]+$/s', $timestamp)) { |
| | | $ret = strtotime($timestamp); |
| | | } |
| | | else { |
| | | $ret = $timestamp; |
| | | } |
| | | |
| | | return $ret; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Callback for sqlite: now() |
| | | */ |
| | | public static function sqlite_now() |
| | | { |
| | | return date("Y-m-d H:i:s"); |
| | | } |
| | | |
| | | } |
| | |
| | | function __construct($uid) |
| | | { |
| | | $this->uid = $uid; |
| | | $this->app = rcmail::get_instance(); |
| | | $this->app = rcube::get_instance(); |
| | | $this->storage = $this->app->get_storage(); |
| | | $this->storage->set_options(array('all_headers' => true)); |
| | | |
| | |
| | | $this->opt = array( |
| | | 'safe' => $this->is_safe, |
| | | 'prefer_html' => $this->app->config->get('prefer_html'), |
| | | 'get_url' => rcmail_url('get', array( |
| | | '_mbox' => $this->storage->get_folder(), '_uid' => $uid)) |
| | | 'get_url' => $this->app->url(array( |
| | | 'action' => 'get', |
| | | 'mbox' => $this->storage->get_folder(), |
| | | 'uid' => $uid)) |
| | | ); |
| | | |
| | | if (!empty($this->headers->structure)) { |
| | |
| | | $c->type = 'content'; |
| | | $c->ctype_primary = 'text'; |
| | | $c->ctype_secondary = 'plain'; |
| | | $c->body = rcube_label('htmlmessage'); |
| | | $c->mimetype = 'text/plain'; |
| | | $c->realtype = 'text/html'; |
| | | |
| | | $this->parts[] = $c; |
| | | } |
| | |
| | | // add html part as attachment |
| | | if ($html_part !== null && $structure->parts[$html_part] !== $print_part) { |
| | | $html_part = &$structure->parts[$html_part]; |
| | | $html_part->filename = rcube_label('htmlmessage'); |
| | | $html_part->mimetype = 'text/html'; |
| | | |
| | | $this->attachments[] = $html_part; |
| | |
| | | $p->type = 'content'; |
| | | $p->ctype_primary = 'text'; |
| | | $p->ctype_secondary = 'plain'; |
| | | $p->body = rcube_label('encryptedmessage'); |
| | | $p->size = strlen($p->body); |
| | | $p->mimetype = 'text/plain'; |
| | | $p->realtype = 'multipart/encrypted'; |
| | | |
| | | $this->parts[] = $p; |
| | | } |
| | |
| | | $uupart->size = strlen($uupart->body); |
| | | $uupart->mime_id = 'uu.' . $part->mime_id . '.' . $pid; |
| | | |
| | | $ctype = rc_mime_content_type($uupart->body, $uupart->filename, 'application/octet-stream', true); |
| | | $ctype = rcube_mime::content_type($uupart->body, $uupart->filename, 'application/octet-stream', true); |
| | | $uupart->mimetype = $ctype; |
| | | list($uupart->ctype_primary, $uupart->ctype_secondary) = explode('/', $ctype); |
| | | |
New file |
| | |
| | | <?php |
| | | |
| | | /** |
| | | +-----------------------------------------------------------------------+ |
| | | | program/include/rcube_message_header.php | |
| | | | | |
| | | | This file is part of the Roundcube Webmail client | |
| | | | Copyright (C) 2005-2012, The Roundcube Dev Team | |
| | | | Copyright (C) 2011-2012, Kolab Systems AG | |
| | | | | |
| | | | Licensed under the GNU General Public License version 3 or | |
| | | | any later version with exceptions for skins & plugins. | |
| | | | See the README file for a full license statement. | |
| | | | | |
| | | | PURPOSE: | |
| | | | E-mail message headers representation | |
| | | | | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Aleksander Machniak <alec@alec.pl> | |
| | | +-----------------------------------------------------------------------+ |
| | | |
| | | $Id$ |
| | | |
| | | */ |
| | | |
| | | /** |
| | | * Struct representing an e-mail message header |
| | | * |
| | | * @package Mail |
| | | * @author Aleksander Machniak <alec@alec.pl> |
| | | */ |
| | | class rcube_message_header |
| | | { |
| | | /** |
| | | * Message sequence number |
| | | * |
| | | * @var int |
| | | */ |
| | | public $id; |
| | | |
| | | /** |
| | | * Message unique identifier |
| | | * |
| | | * @var int |
| | | */ |
| | | public $uid; |
| | | |
| | | /** |
| | | * Message subject |
| | | * |
| | | * @var string |
| | | */ |
| | | public $subject; |
| | | |
| | | /** |
| | | * Message sender (From) |
| | | * |
| | | * @var string |
| | | */ |
| | | public $from; |
| | | |
| | | /** |
| | | * Message recipient (To) |
| | | * |
| | | * @var string |
| | | */ |
| | | public $to; |
| | | |
| | | /** |
| | | * Message additional recipients (Cc) |
| | | * |
| | | * @var string |
| | | */ |
| | | public $cc; |
| | | |
| | | /** |
| | | * Message Reply-To header |
| | | * |
| | | * @var string |
| | | */ |
| | | public $replyto; |
| | | |
| | | /** |
| | | * Message In-Reply-To header |
| | | * |
| | | * @var string |
| | | */ |
| | | public $in_reply_to; |
| | | |
| | | /** |
| | | * Message date (Date) |
| | | * |
| | | * @var string |
| | | */ |
| | | public $date; |
| | | |
| | | /** |
| | | * Message identifier (Message-ID) |
| | | * |
| | | * @var string |
| | | */ |
| | | public $messageID; |
| | | |
| | | /** |
| | | * Message size |
| | | * |
| | | * @var int |
| | | */ |
| | | public $size; |
| | | |
| | | /** |
| | | * Message encoding |
| | | * |
| | | * @var string |
| | | */ |
| | | public $encoding; |
| | | |
| | | /** |
| | | * Message charset |
| | | * |
| | | * @var string |
| | | */ |
| | | public $charset; |
| | | |
| | | /** |
| | | * Message Content-type |
| | | * |
| | | * @var string |
| | | */ |
| | | public $ctype; |
| | | |
| | | /** |
| | | * Message timestamp (based on message date) |
| | | * |
| | | * @var int |
| | | */ |
| | | public $timestamp; |
| | | |
| | | /** |
| | | * IMAP bodystructure string |
| | | * |
| | | * @var string |
| | | */ |
| | | public $bodystructure; |
| | | |
| | | /** |
| | | * IMAP internal date |
| | | * |
| | | * @var string |
| | | */ |
| | | public $internaldate; |
| | | |
| | | /** |
| | | * Message References header |
| | | * |
| | | * @var string |
| | | */ |
| | | public $references; |
| | | |
| | | /** |
| | | * Message priority (X-Priority) |
| | | * |
| | | * @var int |
| | | */ |
| | | public $priority; |
| | | |
| | | /** |
| | | * Message receipt recipient |
| | | * |
| | | * @var string |
| | | */ |
| | | public $mdn_to; |
| | | |
| | | /** |
| | | * Other message headers |
| | | * |
| | | * @var array |
| | | */ |
| | | public $others = array(); |
| | | |
| | | /** |
| | | * Message flags |
| | | * |
| | | * @var array |
| | | */ |
| | | public $flags = array(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Class for sorting an array of rcube_message_header objects in a predetermined order. |
| | | * |
| | | * @package Mail |
| | | * @author Aleksander Machniak <alec@alec.pl> |
| | | */ |
| | | class rcube_message_header_sorter |
| | | { |
| | | private $uids = array(); |
| | | |
| | | |
| | | /** |
| | | * Set the predetermined sort order. |
| | | * |
| | | * @param array $index Numerically indexed array of IMAP UIDs |
| | | */ |
| | | function set_index($index) |
| | | { |
| | | $index = array_flip($index); |
| | | |
| | | $this->uids = $index; |
| | | } |
| | | |
| | | /** |
| | | * Sort the array of header objects |
| | | * |
| | | * @param array $headers Array of rcube_message_header objects indexed by UID |
| | | */ |
| | | function sort_headers(&$headers) |
| | | { |
| | | uksort($headers, array($this, "compare_uids")); |
| | | } |
| | | |
| | | /** |
| | | * Sort method called by uksort() |
| | | * |
| | | * @param int $a Array key (UID) |
| | | * @param int $b Array key (UID) |
| | | */ |
| | | function compare_uids($a, $b) |
| | | { |
| | | // then find each sequence number in my ordered list |
| | | $posa = isset($this->uids[$a]) ? intval($this->uids[$a]) : -1; |
| | | $posb = isset($this->uids[$b]) ? intval($this->uids[$b]) : -1; |
| | | |
| | | // return the relative position as the comparison value |
| | | return $posa - $posb; |
| | | } |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /* |
| | | +-----------------------------------------------------------------------+ |
| | | | program/include/rcube_message_part.php | |
| | | | | |
| | | | This file is part of the Roundcube Webmail client | |
| | | | Copyright (C) 2005-2012, The Roundcube Dev Team | |
| | | | Copyright (C) 2011-2012, Kolab Systems AG | |
| | | | | |
| | | | Licensed under the GNU General Public License version 3 or | |
| | | | any later version with exceptions for skins & plugins. | |
| | | | See the README file for a full license statement. | |
| | | | | |
| | | | PURPOSE: | |
| | | | Class representing a message part | |
| | | | | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Thomas Bruederli <roundcube@gmail.com> | |
| | | | Author: Aleksander Machniak <alec@alec.pl> | |
| | | +-----------------------------------------------------------------------+ |
| | | |
| | | $Id$ |
| | | |
| | | */ |
| | | |
| | | |
| | | /** |
| | | * Class representing a message part |
| | | * |
| | | * @package Mail |
| | | * @author Thomas Bruederli <roundcube@gmail.com> |
| | | * @author Aleksander Machniak <alec@alec.pl> |
| | | * @version 2.0 |
| | | */ |
| | | class rcube_message_part |
| | | { |
| | | /** |
| | | * Part MIME identifier |
| | | * |
| | | * @var string |
| | | */ |
| | | public $mime_id = ''; |
| | | |
| | | /** |
| | | * Content main type |
| | | * |
| | | * @var string |
| | | */ |
| | | public $ctype_primary = 'text'; |
| | | |
| | | /** |
| | | * Content subtype |
| | | * |
| | | * @var string |
| | | */ |
| | | public $ctype_secondary = 'plain'; |
| | | |
| | | /** |
| | | * Complete content type |
| | | * |
| | | * @var string |
| | | */ |
| | | public $mimetype = 'text/plain'; |
| | | |
| | | public $disposition = ''; |
| | | public $filename = ''; |
| | | public $encoding = '8bit'; |
| | | public $charset = ''; |
| | | |
| | | /** |
| | | * Part size in bytes |
| | | * |
| | | * @var int |
| | | */ |
| | | public $size = 0; |
| | | |
| | | /** |
| | | * Part headers |
| | | * |
| | | * @var array |
| | | */ |
| | | public $headers = array(); |
| | | |
| | | public $d_parameters = array(); |
| | | public $ctype_parameters = array(); |
| | | |
| | | |
| | | /** |
| | | * Clone handler. |
| | | */ |
| | | function __clone() |
| | | { |
| | | if (isset($this->parts)) { |
| | | foreach ($this->parts as $idx => $part) { |
| | | if (is_object($part)) { |
| | | $this->parts[$idx] = clone $part; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /* |
| | | +-----------------------------------------------------------------------+ |
| | | | program/include/rcube_output.php | |
| | | | | |
| | | | This file is part of the Roundcube PHP suite | |
| | | | Copyright (C) 2005-2012 The Roundcube Dev Team | |
| | | | | |
| | | | Licensed under the GNU General Public License version 3 or | |
| | | | any later version with exceptions for skins & plugins. | |
| | | | See the README file for a full license statement. | |
| | | | CONTENTS: | |
| | | | Abstract class for output generation | |
| | | | | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Thomas Bruederli <roundcube@gmail.com> | |
| | | | Author: Aleksander Machniak <alec@alec.pl> | |
| | | +-----------------------------------------------------------------------+ |
| | | |
| | | $Id$ |
| | | |
| | | */ |
| | | |
| | | /** |
| | | * Class for output generation |
| | | * |
| | | * @package HTML |
| | | */ |
| | | abstract class rcube_output |
| | | { |
| | | public $browser; |
| | | public $type = 'html'; |
| | | public $ajax_call = false; |
| | | public $framed = false; |
| | | |
| | | protected $app; |
| | | protected $config; |
| | | protected $charset = RCMAIL_CHARSET; |
| | | protected $env = array(); |
| | | protected $pagetitle = ''; |
| | | protected $object_handlers = array(); |
| | | |
| | | |
| | | /** |
| | | * Object constructor |
| | | */ |
| | | public function __construct($task = null, $framed = false) |
| | | { |
| | | $this->app = rcmail::get_instance(); |
| | | $this->config = $this->app->config; |
| | | $this->browser = new rcube_browser(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Setter for page title |
| | | * |
| | | * @param string $title Page title |
| | | */ |
| | | public function set_pagetitle($title) |
| | | { |
| | | $this->pagetitle = $title; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Setter for output charset. |
| | | * To be specified in a meta tag and sent as http-header |
| | | * |
| | | * @param string $charset Charset name |
| | | */ |
| | | public function set_charset($charset) |
| | | { |
| | | $this->charset = $charset; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Getter for output charset |
| | | * |
| | | * @return string Output charset name |
| | | */ |
| | | public function get_charset() |
| | | { |
| | | return $this->charset; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Getter for the current skin path property |
| | | */ |
| | | public function get_skin_path() |
| | | { |
| | | return $this->config->get('skin_path'); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Set environment variable |
| | | * |
| | | * @param string $name Property name |
| | | * @param mixed $value Property value |
| | | */ |
| | | public function set_env($name, $value) |
| | | { |
| | | $this->env[$name] = $value; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Environment variable getter. |
| | | * |
| | | * @param string $name Property name |
| | | * |
| | | * @return mixed Property value |
| | | */ |
| | | public function get_env($name) |
| | | { |
| | | return $this->env[$name]; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Delete all stored env variables and commands |
| | | */ |
| | | public function reset() |
| | | { |
| | | $this->env = array(); |
| | | $this->object_handlers = array(); |
| | | $this->pagetitle = ''; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Call a client method |
| | | * |
| | | * @param string Method to call |
| | | * @param ... Additional arguments |
| | | */ |
| | | abstract function command(); |
| | | |
| | | |
| | | /** |
| | | * Add a localized label to the client environment |
| | | */ |
| | | abstract function add_label(); |
| | | |
| | | |
| | | /** |
| | | * Invoke display_message command |
| | | * |
| | | * @param string $message Message to display |
| | | * @param string $type Message type [notice|confirm|error] |
| | | * @param array $vars Key-value pairs to be replaced in localized text |
| | | * @param boolean $override Override last set message |
| | | * @param int $timeout Message displaying time in seconds |
| | | */ |
| | | abstract function show_message($message, $type = 'notice', $vars = null, $override = true, $timeout = 0); |
| | | |
| | | |
| | | /** |
| | | * Redirect to a certain url. |
| | | * |
| | | * @param mixed $p Either a string with the action or url parameters as key-value pairs |
| | | * @param int $delay Delay in seconds |
| | | */ |
| | | abstract function redirect($p = array(), $delay = 1); |
| | | |
| | | |
| | | /** |
| | | * Send output to the client. |
| | | */ |
| | | abstract function send(); |
| | | |
| | | |
| | | /** |
| | | * Register a template object handler |
| | | * |
| | | * @param string Object name |
| | | * @param string Function name to call |
| | | * @return void |
| | | */ |
| | | public function add_handler($obj, $func) |
| | | { |
| | | $this->object_handlers[$obj] = $func; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Register a list of template object handlers |
| | | * |
| | | * @param array Hash array with object=>handler pairs |
| | | * @return void |
| | | */ |
| | | public function add_handlers($arr) |
| | | { |
| | | $this->object_handlers = array_merge($this->object_handlers, $arr); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Send HTTP headers to prevent caching a page |
| | | */ |
| | | public function nocacheing_headers() |
| | | { |
| | | if (headers_sent()) { |
| | | return; |
| | | } |
| | | |
| | | header("Expires: ".gmdate("D, d M Y H:i:s")." GMT"); |
| | | header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); |
| | | |
| | | // Request browser to disable DNS prefetching (CVE-2010-0464) |
| | | header("X-DNS-Prefetch-Control: off"); |
| | | |
| | | // We need to set the following headers to make downloads work using IE in HTTPS mode. |
| | | if ($this->browser->ie && rcube_ui::https_check()) { |
| | | header('Pragma: private'); |
| | | header("Cache-Control: private, must-revalidate"); |
| | | } |
| | | else { |
| | | header("Cache-Control: private, no-cache, must-revalidate, post-check=0, pre-check=0"); |
| | | header("Pragma: no-cache"); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Show error page and terminate script execution |
| | | * |
| | | * @param int $code Error code |
| | | * @param string $message Error message |
| | | */ |
| | | public function raise_error($code, $message) |
| | | { |
| | | // STUB: to be overloaded by specific output classes |
| | | fputs(STDERR, "Error $code: $message\n"); |
| | | exit(-1); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Convert a variable into a javascript object notation |
| | | * |
| | | * @param mixed Input value |
| | | * |
| | | * @return string Serialized JSON string |
| | | */ |
| | | public static function json_serialize($input) |
| | | { |
| | | $input = rcube_charset::clean($input); |
| | | |
| | | // sometimes even using rcube_charset::clean() the input contains invalid UTF-8 sequences |
| | | // that's why we have @ here |
| | | return @json_encode($input); |
| | | } |
| | | |
| | | } |
File was renamed from program/include/rcube_template.php |
| | |
| | | |
| | | /* |
| | | +-----------------------------------------------------------------------+ |
| | | | program/include/rcube_template.php | |
| | | | program/include/rcubeoutput_html.php | |
| | | | | |
| | | | This file is part of the Roundcube Webmail client | |
| | | | Copyright (C) 2006-2011, The Roundcube Dev Team | |
| | | | Copyright (C) 2006-2012, The Roundcube Dev Team | |
| | | | | |
| | | | Licensed under the GNU General Public License version 3 or | |
| | | | any later version with exceptions for skins & plugins. | |
| | |
| | | | | |
| | | | PURPOSE: | |
| | | | Class to handle HTML page output using a skin template. | |
| | | | Extends rcube_html_page class from rcube_shared.inc | |
| | | | | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Thomas Bruederli <roundcube@gmail.com> | |
| | |
| | | * Class to create HTML page output using a skin template |
| | | * |
| | | * @package View |
| | | * @todo Documentation |
| | | * @uses rcube_html_page |
| | | */ |
| | | class rcube_template extends rcube_html_page |
| | | class rcube_output_html extends rcube_output |
| | | { |
| | | private $app; |
| | | private $config; |
| | | private $pagetitle = ''; |
| | | private $message = null; |
| | | private $js_env = array(); |
| | | private $js_labels = array(); |
| | | private $js_commands = array(); |
| | | private $object_handlers = array(); |
| | | private $plugin_skin_path; |
| | | private $template_name; |
| | | |
| | | public $browser; |
| | | public $framed = false; |
| | | public $env = array(); |
| | | public $type = 'html'; |
| | | public $ajax_call = false; |
| | | |
| | | protected $message = null; |
| | | protected $js_env = array(); |
| | | protected $js_labels = array(); |
| | | protected $js_commands = array(); |
| | | protected $plugin_skin_path; |
| | | protected $template_name; |
| | | protected $scripts_path = ''; |
| | | protected $script_files = array(); |
| | | protected $css_files = array(); |
| | | protected $scripts = array(); |
| | | protected $default_template = "<html>\n<head><title></title></head>\n<body></body>\n</html>"; |
| | | protected $header = ''; |
| | | protected $footer = ''; |
| | | protected $body = ''; |
| | | protected $base_path = ''; |
| | | |
| | | // deprecated names of templates used before 0.5 |
| | | private $deprecated_templates = array( |
| | | 'contact' => 'showcontact', |
| | | 'contactadd' => 'addcontact', |
| | | 'contactedit' => 'editcontact', |
| | | protected $deprecated_templates = array( |
| | | 'contact' => 'showcontact', |
| | | 'contactadd' => 'addcontact', |
| | | 'contactedit' => 'editcontact', |
| | | 'identityedit' => 'editidentity', |
| | | 'messageprint' => 'printmessage', |
| | | ); |
| | |
| | | * |
| | | * @todo Replace $this->config with the real rcube_config object |
| | | */ |
| | | public function __construct($task, $framed = false) |
| | | public function __construct($task = null, $framed = false) |
| | | { |
| | | parent::__construct(); |
| | | |
| | | $this->app = rcmail::get_instance(); |
| | | $this->config = $this->app->config->all(); |
| | | $this->browser = new rcube_browser(); |
| | | |
| | | //$this->framed = $framed; |
| | | $this->set_env('task', $task); |
| | | $this->set_env('x_frame_options', $this->app->config->get('x_frame_options', 'sameorigin')); |
| | | $this->set_env('x_frame_options', $this->config->get('x_frame_options', 'sameorigin')); |
| | | |
| | | // load the correct skin (in case user-defined) |
| | | $this->set_skin($this->config['skin']); |
| | | $this->set_skin($this->config->get('skin')); |
| | | |
| | | // add common javascripts |
| | | $this->add_script('var '.JS_OBJECT_NAME.' = new rcube_webmail();', 'head_top'); |
| | |
| | | )); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Set environment variable |
| | | * |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Set page title variable |
| | | */ |
| | | public function set_pagetitle($title) |
| | | { |
| | | $this->pagetitle = $title; |
| | | } |
| | | |
| | | /** |
| | | * Getter for the current page title |
| | | * |
| | | * @return string The page title |
| | | */ |
| | | public function get_pagetitle() |
| | | protected function get_pagetitle() |
| | | { |
| | | if (!empty($this->pagetitle)) { |
| | | $title = $this->pagetitle; |
| | | } |
| | | else if ($this->env['task'] == 'login') { |
| | | $title = rcube_label(array('name' => 'welcome', 'vars' => array('product' => $this->config['product_name']))); |
| | | $title = $this->app->gettext(array( |
| | | 'name' => 'welcome', |
| | | 'vars' => array('product' => $this->config->get('product_name') |
| | | ))); |
| | | } |
| | | else { |
| | | $title = ucfirst($this->env['task']); |
| | |
| | | |
| | | return $title; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Set skin |
| | |
| | | $valid = true; |
| | | } |
| | | else { |
| | | $skin_path = $this->config['skin_path'] ? $this->config['skin_path'] : 'skins/default'; |
| | | $skin_path = $this->config->get('skin_path'); |
| | | if (!$skin_path) { |
| | | $skin_path = 'skins/default'; |
| | | } |
| | | $valid = !$skin; |
| | | } |
| | | |
| | | $this->app->config->set('skin_path', $skin_path); |
| | | $this->config['skin_path'] = $skin_path; |
| | | $this->config->set('skin_path', $skin_path); |
| | | |
| | | return $valid; |
| | | } |
| | | |
| | | /** |
| | | * Getter for the current skin path property |
| | | */ |
| | | public function get_skin_path() |
| | | { |
| | | return $this->config['skin_path']; |
| | | } |
| | | |
| | | /** |
| | | * Check if a specific template exists |
| | |
| | | */ |
| | | public function template_exists($name) |
| | | { |
| | | $filename = $this->config['skin_path'] . '/templates/' . $name . '.html'; |
| | | $filename = $this->config->get('skin_path') . '/templates/' . $name . '.html'; |
| | | return (is_file($filename) && is_readable($filename)) || ($this->deprecated_templates[$name] && $this->template_exists($this->deprecated_templates[$name])); |
| | | } |
| | | |
| | | /** |
| | | * Register a template object handler |
| | | * |
| | | * @param string Object name |
| | | * @param string Function name to call |
| | | * @return void |
| | | */ |
| | | public function add_handler($obj, $func) |
| | | { |
| | | $this->object_handlers[$obj] = $func; |
| | | } |
| | | |
| | | /** |
| | | * Register a list of template object handlers |
| | | * |
| | | * @param array Hash array with object=>handler pairs |
| | | * @return void |
| | | */ |
| | | public function add_handlers($arr) |
| | | { |
| | | $this->object_handlers = array_merge($this->object_handlers, $arr); |
| | | } |
| | | |
| | | /** |
| | | * Register a GUI object to the client script |
| | |
| | | { |
| | | $this->add_script(JS_OBJECT_NAME.".gui_object('$obj', '$id');"); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Call a client method |
| | |
| | | $this->js_commands[] = $cmd; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Add a localized label to the client environment |
| | | */ |
| | |
| | | $args = $args[0]; |
| | | |
| | | foreach ($args as $name) { |
| | | $this->js_labels[$name] = rcube_label($name); |
| | | $this->js_labels[$name] = $this->app->gettext($name); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Invoke display_message command |
| | |
| | | public function show_message($message, $type='notice', $vars=null, $override=true, $timeout=0) |
| | | { |
| | | if ($override || !$this->message) { |
| | | if (rcube_label_exists($message)) { |
| | | if ($this->app->text_exists($message)) { |
| | | if (!empty($vars)) |
| | | $vars = array_map('Q', $vars); |
| | | $msgtext = rcube_label(array('name' => $message, 'vars' => $vars)); |
| | | $msgtext = $this->app->gettext(array('name' => $message, 'vars' => $vars)); |
| | | } |
| | | else |
| | | $msgtext = $message; |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Delete all stored env variables and commands |
| | | * |
| | | * @return void |
| | | * @uses rcube_html::reset() |
| | | * @uses self::$env |
| | | * @uses self::$js_env |
| | | * @uses self::$js_commands |
| | | * @uses self::$object_handlers |
| | | */ |
| | | public function reset() |
| | | { |
| | | $this->env = array(); |
| | | parent::reset(); |
| | | $this->js_env = array(); |
| | | $this->js_labels = array(); |
| | | $this->js_commands = array(); |
| | | $this->object_handlers = array(); |
| | | parent::reset(); |
| | | $this->script_files = array(); |
| | | $this->scripts = array(); |
| | | $this->header = ''; |
| | | $this->footer = ''; |
| | | $this->body = ''; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Redirect to a certain url |
| | | * |
| | | * @param mixed Either a string with the action or url parameters as key-value pairs |
| | | * @see rcmail::url() |
| | | * @param mixed $p Either a string with the action or url parameters as key-value pairs |
| | | * @param int $delay Delay in seconds |
| | | */ |
| | | public function redirect($p = array()) |
| | | public function redirect($p = array(), $delay = 1) |
| | | { |
| | | $location = $this->app->url($p); |
| | | header('Location: ' . $location); |
| | | exit; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Send the request output to the client. |
| | |
| | | if ($templ != 'iframe') { |
| | | // prevent from endless loops |
| | | if ($exit != 'recur' && $this->app->plugins->is_processing('render_page')) { |
| | | raise_error(array('code' => 505, 'type' => 'php', |
| | | rcube::raise_error(array('code' => 505, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => 'Recursion alert: ignoring output->send()'), true, false); |
| | | return; |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Process template and write to stdOut |
| | | * |
| | | * @param string HTML template |
| | | * @see rcube_html_page::write() |
| | | * @override |
| | | * @param string $template HTML template content |
| | | */ |
| | | public function write($template = '') |
| | | { |
| | |
| | | header('X-Frame-Options: ' . ($iframe && $xframe == 'deny' ? 'sameorigin' : $xframe)); |
| | | |
| | | // call super method |
| | | parent::write($template, $this->config['skin_path']); |
| | | $this->_write($template, $this->config->get('skin_path')); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Parse a specific skin template and deliver to stdout (or return) |
| | |
| | | */ |
| | | function parse($name = 'main', $exit = true, $write = true) |
| | | { |
| | | $skin_path = $this->config['skin_path']; |
| | | $skin_path = $this->config->get('skin_path'); |
| | | $plugin = false; |
| | | $realname = $name; |
| | | $temp = explode('.', $name, 2); |
| | |
| | | if (count($temp) > 1) { |
| | | $plugin = $temp[0]; |
| | | $name = $temp[1]; |
| | | $skin_dir = $plugin . '/skins/' . $this->config['skin']; |
| | | $skin_dir = $plugin . '/skins/' . $this->config->get('skin'); |
| | | $skin_path = $this->plugin_skin_path = $this->app->plugins->dir . $skin_dir; |
| | | |
| | | // fallback to default skin |
| | |
| | | if (!is_readable($path) && $this->deprecated_templates[$realname]) { |
| | | $path = "$skin_path/templates/".$this->deprecated_templates[$realname].".html"; |
| | | if (is_readable($path)) |
| | | raise_error(array('code' => 502, 'type' => 'php', |
| | | rcube::raise_error(array('code' => 502, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Using deprecated template '".$this->deprecated_templates[$realname] |
| | | ."' in ".$this->config['skin_path']."/templates. Please rename to '".$realname."'"), |
| | | ."' in $skin_path/templates. Please rename to '".$realname."'"), |
| | | true, false); |
| | | } |
| | | |
| | | // read template file |
| | | if (($templ = @file_get_contents($path)) === false) { |
| | | raise_error(array( |
| | | rcube::raise_error(array( |
| | | 'code' => 501, |
| | | 'type' => 'php', |
| | | 'line' => __LINE__, |
| | |
| | | |
| | | if ($write) { |
| | | // add debug console |
| | | if ($realname != 'error' && ($this->config['debug_level'] & 8)) { |
| | | if ($realname != 'error' && ($this->config->get('debug_level') & 8)) { |
| | | $this->add_footer('<div id="console" style="position:absolute;top:5px;left:5px;width:405px;padding:2px;background:white;z-index:9000;display:none"> |
| | | <a href="#toggle" onclick="con=$(\'#dbgconsole\');con[con.is(\':visible\')?\'hide\':\'show\']();return false">console</a> |
| | | <textarea name="console" id="dbgconsole" rows="20" cols="40" style="display:none;width:400px;border:none;font-size:10px" spellcheck="false"></textarea></div>' |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return executable javascript code for all registered commands |
| | | * |
| | | * @return string $out |
| | | */ |
| | | private function get_js_commands() |
| | | protected function get_js_commands() |
| | | { |
| | | $out = ''; |
| | | if (!$this->framed && !empty($this->js_env)) { |
| | | $out .= JS_OBJECT_NAME . '.set_env('.json_serialize($this->js_env).");\n"; |
| | | $out .= JS_OBJECT_NAME . '.set_env('.self::json_serialize($this->js_env).");\n"; |
| | | } |
| | | if (!empty($this->js_labels)) { |
| | | $this->command('add_label', $this->js_labels); |
| | |
| | | foreach ($this->js_commands as $i => $args) { |
| | | $method = array_shift($args); |
| | | foreach ($args as $i => $arg) { |
| | | $args[$i] = json_serialize($arg); |
| | | $args[$i] = self::json_serialize($arg); |
| | | } |
| | | $parent = $this->framed || preg_match('/^parent\./', $method); |
| | | $out .= sprintf( |
| | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Make URLs starting with a slash point to skin directory |
| | | * |
| | |
| | | public function abs_url($str) |
| | | { |
| | | if ($str[0] == '/') |
| | | return $this->config['skin_path'] . $str; |
| | | return $this->config->get('skin_path') . $str; |
| | | else |
| | | return $str; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Show error page and terminate script execution |
| | | * |
| | | * @param int $code Error code |
| | | * @param string $message Error message |
| | | */ |
| | | public function raise_error($code, $message) |
| | | { |
| | | global $__page_content, $ERROR_CODE, $ERROR_MESSAGE; |
| | | |
| | | $ERROR_CODE = $code; |
| | | $ERROR_MESSAGE = $message; |
| | | |
| | | include INSTALL_PATH . 'program/steps/utils/error.inc'; |
| | | exit; |
| | | } |
| | | |
| | | |
| | |
| | | * Replace all strings ($varname) |
| | | * with the content of the according global variable. |
| | | */ |
| | | private function parse_with_globals($input) |
| | | protected function parse_with_globals($input) |
| | | { |
| | | $GLOBALS['__version'] = Q(RCMAIL_VERSION); |
| | | $GLOBALS['__comm_path'] = Q($this->app->comm_path); |
| | | $GLOBALS['__skin_path'] = Q($this->config['skin_path']); |
| | | $GLOBALS['__version'] = html::quote(RCMAIL_VERSION); |
| | | $GLOBALS['__comm_path'] = html::quote($this->app->comm_path); |
| | | $GLOBALS['__skin_path'] = Q($this->config->get('skin_path')); |
| | | |
| | | return preg_replace_callback('/\$(__[a-z0-9_\-]+)/', |
| | | array($this, 'globals_callback'), $input); |
| | | array($this, 'globals_callback'), $input); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Callback funtion for preg_replace_callback() in parse_with_globals() |
| | | */ |
| | | private function globals_callback($matches) |
| | | protected function globals_callback($matches) |
| | | { |
| | | return $GLOBALS[$matches[1]]; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Public wrapper to dipp into template parsing. |
| | | * |
| | | * @param string $input |
| | | * @return string |
| | | * @uses rcube_template::parse_xml() |
| | | * @uses rcube_output_html::parse_xml() |
| | | * @since 0.1-rc1 |
| | | */ |
| | | public function just_parse($input) |
| | |
| | | return $this->parse_xml($input); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Parse for conditional tags |
| | | * |
| | | * @param string $input |
| | | * @return string |
| | | */ |
| | | private function parse_conditions($input) |
| | | protected function parse_conditions($input) |
| | | { |
| | | $matches = preg_split('/<roundcube:(if|elseif|else|endif)\s+([^>]+)>\n?/is', $input, 2, PREG_SPLIT_DELIM_CAPTURE); |
| | | if ($matches && count($matches) == 4) { |
| | | if (preg_match('/^(else|endif)$/i', $matches[1])) { |
| | | return $matches[0] . $this->parse_conditions($matches[3]); |
| | | } |
| | | $attrib = parse_attrib_string($matches[2]); |
| | | $attrib = html::parse_attrib_string($matches[2]); |
| | | if (isset($attrib['condition'])) { |
| | | $condmet = $this->check_condition($attrib['condition']); |
| | | $submatches = preg_split('/<roundcube:(elseif|else|endif)\s+([^>]+)>\n?/is', $matches[3], 2, PREG_SPLIT_DELIM_CAPTURE); |
| | |
| | | } |
| | | return $matches[0] . $this->parse_conditions($result); |
| | | } |
| | | raise_error(array( |
| | | rcube::raise_error(array( |
| | | 'code' => 500, |
| | | 'type' => 'php', |
| | | 'line' => __LINE__, |
| | |
| | | * @param string Condition statement |
| | | * @return boolean True if condition is met, False if not |
| | | */ |
| | | private function check_condition($condition) |
| | | protected function check_condition($condition) |
| | | { |
| | | return eval("return (".$this->parse_expression($condition).");"); |
| | | } |
| | |
| | | /** |
| | | * Inserts hidden field with CSRF-prevention-token into POST forms |
| | | */ |
| | | private function alter_form_tag($matches) |
| | | protected function alter_form_tag($matches) |
| | | { |
| | | $out = $matches[0]; |
| | | $attrib = parse_attrib_string($matches[1]); |
| | | $out = $matches[0]; |
| | | $attrib = html::parse_attrib_string($matches[1]); |
| | | |
| | | if (strtolower($attrib['method']) == 'post') { |
| | | $hidden = new html_hiddenfield(array('name' => '_token', 'value' => $this->app->get_request_token())); |
| | |
| | | * @param string Expression statement |
| | | * @return string Expression value |
| | | */ |
| | | private function parse_expression($expression) |
| | | protected function parse_expression($expression) |
| | | { |
| | | return preg_replace( |
| | | array( |
| | |
| | | "\$_SESSION['\\1']", |
| | | "\$this->app->config->get('\\1',get_boolean('\\3'))", |
| | | "\$this->env['\\1']", |
| | | "get_input_value('\\1', RCUBE_INPUT_GPC)", |
| | | "rcube_ui::get_input_value('\\1', rcube_ui::INPUT_GPC)", |
| | | "\$_COOKIE['\\1']", |
| | | "\$this->browser->{'\\1'}", |
| | | $this->template_name, |
| | |
| | | * @todo Use DOM-parser to traverse template HTML |
| | | * @todo Maybe a cache. |
| | | */ |
| | | private function parse_xml($input) |
| | | protected function parse_xml($input) |
| | | { |
| | | return preg_replace_callback('/<roundcube:([-_a-z]+)\s+((?:[^>]|\\\\>)+)(?<!\\\\)>/Ui', array($this, 'xml_command'), $input); |
| | | } |
| | |
| | | * @param array Matches array of preg_replace_callback |
| | | * @return string Tag/Object content |
| | | */ |
| | | private function xml_command($matches) |
| | | protected function xml_command($matches) |
| | | { |
| | | $command = strtolower($matches[1]); |
| | | $attrib = parse_attrib_string($matches[2]); |
| | | $attrib = html::parse_attrib_string($matches[2]); |
| | | |
| | | // empty output if required condition is not met |
| | | if (!empty($attrib['condition']) && !$this->check_condition($attrib['condition'])) { |
| | |
| | | // show a label |
| | | case 'label': |
| | | if ($attrib['name'] || $attrib['command']) { |
| | | $vars = $attrib + array('product' => $this->config['product_name']); |
| | | $vars = $attrib + array('product' => $this->config->get('product_name')); |
| | | unset($vars['name'], $vars['command']); |
| | | $label = rcube_label($attrib + array('vars' => $vars)); |
| | | return !$attrib['noshow'] ? (get_boolean((string)$attrib['html']) ? $label : Q($label)) : ''; |
| | | $label = $this->app->gettext($attrib + array('vars' => $vars)); |
| | | return !$attrib['noshow'] ? (get_boolean((string)$attrib['html']) ? $label : html::quote($label)) : ''; |
| | | } |
| | | break; |
| | | |
| | | // include a file |
| | | case 'include': |
| | | if (!$this->plugin_skin_path || !is_file($path = realpath($this->plugin_skin_path . $attrib['file']))) |
| | | $path = realpath(($attrib['skin_path'] ? $attrib['skin_path'] : $this->config['skin_path']).$attrib['file']); |
| | | |
| | | $path = realpath(($attrib['skin_path'] ? $attrib['skin_path'] : $this->config->get('skin_path')).$attrib['file']); |
| | | |
| | | if (is_readable($path)) { |
| | | if ($this->config['skin_include_php']) { |
| | | if ($this->config->get('skin_include_php')) { |
| | | $incl = $this->include_php($path); |
| | | } |
| | | else { |
| | |
| | | } |
| | | else if ($object == 'logo') { |
| | | $attrib += array('alt' => $this->xml_command(array('', 'object', 'name="productname"'))); |
| | | if ($this->config['skin_logo']) |
| | | $attrib['src'] = $this->config['skin_logo']; |
| | | if ($logo = $this->config->get('skin_logo')) |
| | | $attrib['src'] = $logo; |
| | | $content = html::img($attrib); |
| | | } |
| | | else if ($object == 'productname') { |
| | | $name = !empty($this->config['product_name']) ? $this->config['product_name'] : 'Roundcube Webmail'; |
| | | $content = Q($name); |
| | | $name = $this->config->get('product_name', 'Roundcube Webmail'); |
| | | $content = html::quote($name); |
| | | } |
| | | else if ($object == 'version') { |
| | | $ver = (string)RCMAIL_VERSION; |
| | |
| | | if (preg_match('/Revision:\s(\d+)/', @shell_exec('svn info'), $regs)) |
| | | $ver .= ' [SVN r'.$regs[1].']'; |
| | | } |
| | | $content = Q($ver); |
| | | $content = html::quote($ver); |
| | | } |
| | | else if ($object == 'steptitle') { |
| | | $content = Q($this->get_pagetitle()); |
| | | $content = html::quote($this->get_pagetitle()); |
| | | } |
| | | else if ($object == 'pagetitle') { |
| | | if (!empty($this->config['devel_mode']) && !empty($_SESSION['username'])) |
| | | $title = $_SESSION['username'].' :: '; |
| | | else if (!empty($this->config['product_name'])) |
| | | $title = $this->config['product_name'].' :: '; |
| | | if ($this->config->get('devel_mode') && !empty($_SESSION['username'])) |
| | | $title = $_SESSION['username'].' :: '; |
| | | else if ($prod_name = $this->config->get('product_name')) |
| | | $title = $prod_name . ' :: '; |
| | | else |
| | | $title = ''; |
| | | $title = ''; |
| | | $title .= $this->get_pagetitle(); |
| | | $content = Q($title); |
| | | $content = html::quote($title); |
| | | } |
| | | |
| | | // exec plugin hooks for this template object |
| | |
| | | // return code for a specified eval expression |
| | | case 'exp': |
| | | $value = $this->parse_expression($attrib['expression']); |
| | | return eval("return Q($value);"); |
| | | return eval("return html::quote($value);"); |
| | | |
| | | // return variable |
| | | case 'var': |
| | |
| | | $value = $this->env[$name]; |
| | | break; |
| | | case 'config': |
| | | $value = $this->config[$name]; |
| | | $value = $this->config->get($name); |
| | | if (is_array($value) && $value[$_SESSION['storage_host']]) { |
| | | $value = $value[$_SESSION['storage_host']]; |
| | | } |
| | | break; |
| | | case 'request': |
| | | $value = get_input_value($name, RCUBE_INPUT_GPC); |
| | | $value = rcube_ui::get_input_value($name, rcube_ui::INPUT_GPC); |
| | | break; |
| | | case 'session': |
| | | $value = $_SESSION[$name]; |
| | |
| | | $value = implode(', ', $value); |
| | | } |
| | | |
| | | return Q($value); |
| | | return html::quote($value); |
| | | break; |
| | | } |
| | | return ''; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Include a specific file and return it's contents |
| | |
| | | * @param string File path |
| | | * @return string Contents of the processed file |
| | | */ |
| | | private function include_php($file) |
| | | protected function include_php($file) |
| | | { |
| | | ob_start(); |
| | | include $file; |
| | |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create and register a button |
| | |
| | | } |
| | | // get localized text for labels and titles |
| | | if ($attrib['title']) { |
| | | $attrib['title'] = Q(rcube_label($attrib['title'], $attrib['domain'])); |
| | | $attrib['title'] = html::quote($this->app->gettext($attrib['title'], $attrib['domain'])); |
| | | } |
| | | if ($attrib['label']) { |
| | | $attrib['label'] = Q(rcube_label($attrib['label'], $attrib['domain'])); |
| | | $attrib['label'] = html::quote($this->app->gettext($attrib['label'], $attrib['domain'])); |
| | | } |
| | | if ($attrib['alt']) { |
| | | $attrib['alt'] = Q(rcube_label($attrib['alt'], $attrib['domain'])); |
| | | $attrib['alt'] = html::quote($this->app->gettext($attrib['alt'], $attrib['domain'])); |
| | | } |
| | | |
| | | // set title to alt attribute for IE browsers |
| | |
| | | |
| | | // make valid href to specific buttons |
| | | if (in_array($attrib['command'], rcmail::$main_tasks)) { |
| | | $attrib['href'] = rcmail_url(null, null, $attrib['command']); |
| | | $attrib['href'] = $this->app->url(array('task' => $attrib['command'])); |
| | | $attrib['onclick'] = sprintf("%s.command('switch-task','%s');return false", JS_OBJECT_NAME, $attrib['command']); |
| | | } |
| | | else if ($attrib['task'] && in_array($attrib['task'], rcmail::$main_tasks)) { |
| | | $attrib['href'] = rcmail_url($attrib['command'], null, $attrib['task']); |
| | | $attrib['href'] = $this->app->url(array('action' => $attrib['command'], 'task' => $attrib['task'])); |
| | | } |
| | | else if (in_array($attrib['command'], $a_static_commands)) { |
| | | $attrib['href'] = rcmail_url($attrib['command']); |
| | | $attrib['href'] = $this->app->url(array('action' => $attrib['command'])); |
| | | } |
| | | else if ($attrib['command'] == 'permaurl' && !empty($this->env['permaurl'])) { |
| | | $attrib['href'] = $this->env['permaurl']; |
| | |
| | | $out = ''; |
| | | |
| | | // generate image tag |
| | | if ($attrib['type']=='image') { |
| | | if ($attrib['type'] == 'image') { |
| | | $attrib_str = html::attrib_string( |
| | | $attrib, |
| | | array( |
| | |
| | | } |
| | | $link_attrib = array('href', 'onclick', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup', 'target'); |
| | | } |
| | | else if ($attrib['type']=='link') { |
| | | else if ($attrib['type'] == 'link') { |
| | | $btn_content = isset($attrib['content']) ? $attrib['content'] : ($attrib['label'] ? $attrib['label'] : $attrib['command']); |
| | | $link_attrib = array('href', 'onclick', 'title', 'id', 'class', 'style', 'tabindex', 'target'); |
| | | if ($attrib['innerclass']) |
| | | $btn_content = html::span($attrib['innerclass'], $btn_content); |
| | | } |
| | | else if ($attrib['type']=='input') { |
| | | else if ($attrib['type'] == 'input') { |
| | | $attrib['type'] = 'button'; |
| | | |
| | | if ($attrib['label']) { |
| | |
| | | } |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Link an external script file |
| | | * |
| | | * @param string File URL |
| | | * @param string Target position [head|foot] |
| | | */ |
| | | public function include_script($file, $position='head') |
| | | { |
| | | static $sa_files = array(); |
| | | |
| | | if (!preg_match('|^https?://|i', $file) && $file[0] != '/') { |
| | | $file = $this->scripts_path . $file; |
| | | if ($fs = @filemtime($file)) { |
| | | $file .= '?s=' . $fs; |
| | | } |
| | | } |
| | | |
| | | if (in_array($file, $sa_files)) { |
| | | return; |
| | | } |
| | | |
| | | $sa_files[] = $file; |
| | | |
| | | if (!is_array($this->script_files[$position])) { |
| | | $this->script_files[$position] = array(); |
| | | } |
| | | |
| | | $this->script_files[$position][] = $file; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Add inline javascript code |
| | | * |
| | | * @param string JS code snippet |
| | | * @param string Target position [head|head_top|foot] |
| | | */ |
| | | public function add_script($script, $position='head') |
| | | { |
| | | if (!isset($this->scripts[$position])) { |
| | | $this->scripts[$position] = "\n" . rtrim($script); |
| | | } |
| | | else { |
| | | $this->scripts[$position] .= "\n" . rtrim($script); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Link an external css file |
| | | * |
| | | * @param string File URL |
| | | */ |
| | | public function include_css($file) |
| | | { |
| | | $this->css_files[] = $file; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Add HTML code to the page header |
| | | * |
| | | * @param string $str HTML code |
| | | */ |
| | | public function add_header($str) |
| | | { |
| | | $this->header .= "\n" . $str; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Add HTML code to the page footer |
| | | * To be added right befor </body> |
| | | * |
| | | * @param string $str HTML code |
| | | */ |
| | | public function add_footer($str) |
| | | { |
| | | $this->footer .= "\n" . $str; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Process template and write to stdOut |
| | | * |
| | | * @param string HTML template |
| | | * @param string Base for absolute paths |
| | | */ |
| | | public function _write($templ = '', $base_path = '') |
| | | { |
| | | $output = empty($templ) ? $this->default_template : trim($templ); |
| | | |
| | | // set default page title |
| | | if (empty($this->pagetitle)) { |
| | | $this->pagetitle = 'Roundcube Mail'; |
| | | } |
| | | |
| | | // replace specialchars in content |
| | | $page_title = html::quote($this->pagetitle); |
| | | $page_header = ''; |
| | | $page_footer = ''; |
| | | |
| | | // include meta tag with charset |
| | | if (!empty($this->charset)) { |
| | | if (!headers_sent()) { |
| | | header('Content-Type: text/html; charset=' . $this->charset); |
| | | } |
| | | $page_header = '<meta http-equiv="content-type"'; |
| | | $page_header.= ' content="text/html; charset='; |
| | | $page_header.= $this->charset . '" />'."\n"; |
| | | } |
| | | |
| | | // definition of the code to be placed in the document header and footer |
| | | if (is_array($this->script_files['head'])) { |
| | | foreach ($this->script_files['head'] as $file) { |
| | | $page_header .= html::script($file); |
| | | } |
| | | } |
| | | |
| | | $head_script = $this->scripts['head_top'] . $this->scripts['head']; |
| | | if (!empty($head_script)) { |
| | | $page_header .= html::script(array(), $head_script); |
| | | } |
| | | |
| | | if (!empty($this->header)) { |
| | | $page_header .= $this->header; |
| | | } |
| | | |
| | | // put docready commands into page footer |
| | | if (!empty($this->scripts['docready'])) { |
| | | $this->add_script('$(document).ready(function(){ ' . $this->scripts['docready'] . "\n});", 'foot'); |
| | | } |
| | | |
| | | if (is_array($this->script_files['foot'])) { |
| | | foreach ($this->script_files['foot'] as $file) { |
| | | $page_footer .= html::script($file); |
| | | } |
| | | } |
| | | |
| | | if (!empty($this->footer)) { |
| | | $page_footer .= $this->footer . "\n"; |
| | | } |
| | | |
| | | if (!empty($this->scripts['foot'])) { |
| | | $page_footer .= html::script(array(), $this->scripts['foot']); |
| | | } |
| | | |
| | | // find page header |
| | | if ($hpos = stripos($output, '</head>')) { |
| | | $page_header .= "\n"; |
| | | } |
| | | else { |
| | | if (!is_numeric($hpos)) { |
| | | $hpos = stripos($output, '<body'); |
| | | } |
| | | if (!is_numeric($hpos) && ($hpos = stripos($output, '<html'))) { |
| | | while ($output[$hpos] != '>') { |
| | | $hpos++; |
| | | } |
| | | $hpos++; |
| | | } |
| | | $page_header = "<head>\n<title>$page_title</title>\n$page_header\n</head>\n"; |
| | | } |
| | | |
| | | // add page hader |
| | | if ($hpos) { |
| | | $output = substr_replace($output, $page_header, $hpos, 0); |
| | | } |
| | | else { |
| | | $output = $page_header . $output; |
| | | } |
| | | |
| | | // add page footer |
| | | if (($fpos = strripos($output, '</body>')) || ($fpos = strripos($output, '</html>'))) { |
| | | $output = substr_replace($output, $page_footer."\n", $fpos, 0); |
| | | } |
| | | else { |
| | | $output .= "\n".$page_footer; |
| | | } |
| | | |
| | | // add css files in head, before scripts, for speed up with parallel downloads |
| | | if (!empty($this->css_files) && |
| | | (($pos = stripos($output, '<script ')) || ($pos = stripos($output, '</head>'))) |
| | | ) { |
| | | $css = ''; |
| | | foreach ($this->css_files as $file) { |
| | | $css .= html::tag('link', array('rel' => 'stylesheet', |
| | | 'type' => 'text/css', 'href' => $file, 'nl' => true)); |
| | | } |
| | | $output = substr_replace($output, $css, $pos, 0); |
| | | } |
| | | |
| | | $this->base_path = $base_path; |
| | | |
| | | // correct absolute paths in images and other tags |
| | | // add timestamp to .js and .css filename |
| | | $output = preg_replace_callback( |
| | | '!(src|href|background)=(["\']?)([a-z0-9/_.-]+)(["\'\s>])!i', |
| | | array($this, 'file_callback'), $output); |
| | | |
| | | // trigger hook with final HTML content to be sent |
| | | $hook = rcmail::get_instance()->plugins->exec_hook("send_page", array('content' => $output)); |
| | | if (!$hook['abort']) { |
| | | if ($this->charset != RCMAIL_CHARSET) { |
| | | echo rcube_charset::convert($hook['content'], RCMAIL_CHARSET, $this->charset); |
| | | } |
| | | else { |
| | | echo $hook['content']; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Callback function for preg_replace_callback in write() |
| | | * |
| | | * @return string Parsed string |
| | | */ |
| | | protected function file_callback($matches) |
| | | { |
| | | $file = $matches[3]; |
| | | |
| | | // correct absolute paths |
| | | if ($file[0] == '/') { |
| | | $file = $this->base_path . $file; |
| | | } |
| | | |
| | | // add file modification timestamp |
| | | if (preg_match('/\.(js|css)$/', $file)) { |
| | | if ($fs = @filemtime($file)) { |
| | | $file .= '?s=' . $fs; |
| | | } |
| | | } |
| | | |
| | | return $matches[1] . '=' . $matches[2] . $file . $matches[4]; |
| | | } |
| | | |
| | | |
| | |
| | | * @param array Named parameters |
| | | * @return string HTML code for the gui object |
| | | */ |
| | | private function login_form($attrib) |
| | | protected function login_form($attrib) |
| | | { |
| | | $default_host = $this->config['default_host']; |
| | | $autocomplete = (int) $this->config['login_autocomplete']; |
| | | $default_host = $this->config->get('default_host'); |
| | | $autocomplete = (int) $this->config->get('login_autocomplete'); |
| | | |
| | | $_SESSION['temp'] = true; |
| | | |
| | | // save original url |
| | | $url = get_input_value('_url', RCUBE_INPUT_POST); |
| | | $url = rcube_ui::get_input_value('_url', rcube_ui::INPUT_POST); |
| | | if (empty($url) && !preg_match('/_(task|action)=logout/', $_SERVER['QUERY_STRING'])) |
| | | $url = $_SERVER['QUERY_STRING']; |
| | | |
| | |
| | | // create HTML table with two cols |
| | | $table = new html_table(array('cols' => 2)); |
| | | |
| | | $table->add('title', html::label('rcmloginuser', Q(rcube_label('username')))); |
| | | $table->add('input', $input_user->show(get_input_value('_user', RCUBE_INPUT_GPC))); |
| | | $table->add('title', html::label('rcmloginuser', html::quote($this->app->gettext('username')))); |
| | | $table->add('input', $input_user->show(rcube_ui::get_input_value('_user', rcube_ui::INPUT_GPC))); |
| | | |
| | | $table->add('title', html::label('rcmloginpwd', Q(rcube_label('password')))); |
| | | $table->add('title', html::label('rcmloginpwd', html::quote($this->app->gettext('password')))); |
| | | $table->add('input', $input_pass->show()); |
| | | |
| | | // add host selection row |
| | | if (is_object($input_host) && !$hide_host) { |
| | | $table->add('title', html::label('rcmloginhost', Q(rcube_label('server')))); |
| | | $table->add('input', $input_host->show(get_input_value('_host', RCUBE_INPUT_GPC))); |
| | | $table->add('title', html::label('rcmloginhost', html::quote($this->app->gettext('server')))); |
| | | $table->add('input', $input_host->show(rcube_ui::get_input_value('_host', rcube_ui::INPUT_GPC))); |
| | | } |
| | | |
| | | $out = $input_task->show(); |
| | |
| | | * @param array Named parameters |
| | | * @return void |
| | | */ |
| | | private function preloader($attrib) |
| | | protected function preloader($attrib) |
| | | { |
| | | $images = preg_split('/[\s\t\n,]+/', $attrib['images'], -1, PREG_SPLIT_NO_EMPTY); |
| | | $images = array_map(array($this, 'abs_url'), $images); |
| | |
| | | if (empty($images) || $this->app->task == 'logout') |
| | | return; |
| | | |
| | | $this->add_script('var images = ' . json_serialize($images) .'; |
| | | $this->add_script('var images = ' . self::json_serialize($images) .'; |
| | | for (var i=0; i<images.length; i++) { |
| | | img = new Image(); |
| | | img.src = images[i]; |
| | |
| | | * @param array Named parameters |
| | | * @return string HTML code for the gui object |
| | | */ |
| | | private function search_form($attrib) |
| | | protected function search_form($attrib) |
| | | { |
| | | // add some labels to client |
| | | $this->add_label('searching'); |
| | |
| | | * @param array Named tag parameters |
| | | * @return string HTML code for the gui object |
| | | */ |
| | | private function message_container($attrib) |
| | | protected function message_container($attrib) |
| | | { |
| | | if (isset($attrib['id']) === false) { |
| | | $attrib['id'] = 'rcmMessageContainer'; |
| | | } |
| | | |
| | | $this->add_gui_object('message', $attrib['id']); |
| | | return html::div($attrib, ""); |
| | | |
| | | return html::div($attrib, ''); |
| | | } |
| | | |
| | | |
| | |
| | | * @param array Named parameters for the select tag |
| | | * @return string HTML code for the gui object |
| | | */ |
| | | function charset_selector($attrib) |
| | | public function charset_selector($attrib) |
| | | { |
| | | // pass the following attributes to the form class |
| | | $field_attrib = array('name' => '_charset'); |
| | |
| | | } |
| | | |
| | | $charsets = array( |
| | | 'UTF-8' => 'UTF-8 ('.rcube_label('unicode').')', |
| | | 'US-ASCII' => 'ASCII ('.rcube_label('english').')', |
| | | 'ISO-8859-1' => 'ISO-8859-1 ('.rcube_label('westerneuropean').')', |
| | | 'ISO-8859-2' => 'ISO-8859-2 ('.rcube_label('easterneuropean').')', |
| | | 'ISO-8859-4' => 'ISO-8859-4 ('.rcube_label('baltic').')', |
| | | 'ISO-8859-5' => 'ISO-8859-5 ('.rcube_label('cyrillic').')', |
| | | 'ISO-8859-6' => 'ISO-8859-6 ('.rcube_label('arabic').')', |
| | | 'ISO-8859-7' => 'ISO-8859-7 ('.rcube_label('greek').')', |
| | | 'ISO-8859-8' => 'ISO-8859-8 ('.rcube_label('hebrew').')', |
| | | 'ISO-8859-9' => 'ISO-8859-9 ('.rcube_label('turkish').')', |
| | | 'ISO-8859-10' => 'ISO-8859-10 ('.rcube_label('nordic').')', |
| | | 'ISO-8859-11' => 'ISO-8859-11 ('.rcube_label('thai').')', |
| | | 'ISO-8859-13' => 'ISO-8859-13 ('.rcube_label('baltic').')', |
| | | 'ISO-8859-14' => 'ISO-8859-14 ('.rcube_label('celtic').')', |
| | | 'ISO-8859-15' => 'ISO-8859-15 ('.rcube_label('westerneuropean').')', |
| | | 'ISO-8859-16' => 'ISO-8859-16 ('.rcube_label('southeasterneuropean').')', |
| | | 'WINDOWS-1250' => 'Windows-1250 ('.rcube_label('easterneuropean').')', |
| | | 'WINDOWS-1251' => 'Windows-1251 ('.rcube_label('cyrillic').')', |
| | | 'WINDOWS-1252' => 'Windows-1252 ('.rcube_label('westerneuropean').')', |
| | | 'WINDOWS-1253' => 'Windows-1253 ('.rcube_label('greek').')', |
| | | 'WINDOWS-1254' => 'Windows-1254 ('.rcube_label('turkish').')', |
| | | 'WINDOWS-1255' => 'Windows-1255 ('.rcube_label('hebrew').')', |
| | | 'WINDOWS-1256' => 'Windows-1256 ('.rcube_label('arabic').')', |
| | | 'WINDOWS-1257' => 'Windows-1257 ('.rcube_label('baltic').')', |
| | | 'WINDOWS-1258' => 'Windows-1258 ('.rcube_label('vietnamese').')', |
| | | 'ISO-2022-JP' => 'ISO-2022-JP ('.rcube_label('japanese').')', |
| | | 'ISO-2022-KR' => 'ISO-2022-KR ('.rcube_label('korean').')', |
| | | 'ISO-2022-CN' => 'ISO-2022-CN ('.rcube_label('chinese').')', |
| | | 'EUC-JP' => 'EUC-JP ('.rcube_label('japanese').')', |
| | | 'EUC-KR' => 'EUC-KR ('.rcube_label('korean').')', |
| | | 'EUC-CN' => 'EUC-CN ('.rcube_label('chinese').')', |
| | | 'BIG5' => 'BIG5 ('.rcube_label('chinese').')', |
| | | 'GB2312' => 'GB2312 ('.rcube_label('chinese').')', |
| | | 'UTF-8' => 'UTF-8 ('.$this->app->gettext('unicode').')', |
| | | 'US-ASCII' => 'ASCII ('.$this->app->gettext('english').')', |
| | | 'ISO-8859-1' => 'ISO-8859-1 ('.$this->app->gettext('westerneuropean').')', |
| | | 'ISO-8859-2' => 'ISO-8859-2 ('.$this->app->gettext('easterneuropean').')', |
| | | 'ISO-8859-4' => 'ISO-8859-4 ('.$this->app->gettext('baltic').')', |
| | | 'ISO-8859-5' => 'ISO-8859-5 ('.$this->app->gettext('cyrillic').')', |
| | | 'ISO-8859-6' => 'ISO-8859-6 ('.$this->app->gettext('arabic').')', |
| | | 'ISO-8859-7' => 'ISO-8859-7 ('.$this->app->gettext('greek').')', |
| | | 'ISO-8859-8' => 'ISO-8859-8 ('.$this->app->gettext('hebrew').')', |
| | | 'ISO-8859-9' => 'ISO-8859-9 ('.$this->app->gettext('turkish').')', |
| | | 'ISO-8859-10' => 'ISO-8859-10 ('.$this->app->gettext('nordic').')', |
| | | 'ISO-8859-11' => 'ISO-8859-11 ('.$this->app->gettext('thai').')', |
| | | 'ISO-8859-13' => 'ISO-8859-13 ('.$this->app->gettext('baltic').')', |
| | | 'ISO-8859-14' => 'ISO-8859-14 ('.$this->app->gettext('celtic').')', |
| | | 'ISO-8859-15' => 'ISO-8859-15 ('.$this->app->gettext('westerneuropean').')', |
| | | 'ISO-8859-16' => 'ISO-8859-16 ('.$this->app->gettext('southeasterneuropean').')', |
| | | 'WINDOWS-1250' => 'Windows-1250 ('.$this->app->gettext('easterneuropean').')', |
| | | 'WINDOWS-1251' => 'Windows-1251 ('.$this->app->gettext('cyrillic').')', |
| | | 'WINDOWS-1252' => 'Windows-1252 ('.$this->app->gettext('westerneuropean').')', |
| | | 'WINDOWS-1253' => 'Windows-1253 ('.$this->app->gettext('greek').')', |
| | | 'WINDOWS-1254' => 'Windows-1254 ('.$this->app->gettext('turkish').')', |
| | | 'WINDOWS-1255' => 'Windows-1255 ('.$this->app->gettext('hebrew').')', |
| | | 'WINDOWS-1256' => 'Windows-1256 ('.$this->app->gettext('arabic').')', |
| | | 'WINDOWS-1257' => 'Windows-1257 ('.$this->app->gettext('baltic').')', |
| | | 'WINDOWS-1258' => 'Windows-1258 ('.$this->app->gettext('vietnamese').')', |
| | | 'ISO-2022-JP' => 'ISO-2022-JP ('.$this->app->gettext('japanese').')', |
| | | 'ISO-2022-KR' => 'ISO-2022-KR ('.$this->app->gettext('korean').')', |
| | | 'ISO-2022-CN' => 'ISO-2022-CN ('.$this->app->gettext('chinese').')', |
| | | 'EUC-JP' => 'EUC-JP ('.$this->app->gettext('japanese').')', |
| | | 'EUC-KR' => 'EUC-KR ('.$this->app->gettext('korean').')', |
| | | 'EUC-CN' => 'EUC-CN ('.$this->app->gettext('chinese').')', |
| | | 'BIG5' => 'BIG5 ('.$this->app->gettext('chinese').')', |
| | | 'GB2312' => 'GB2312 ('.$this->app->gettext('chinese').')', |
| | | ); |
| | | |
| | | if (!empty($_POST['_charset'])) |
| | |
| | | /** |
| | | * Include content from config/about.<LANG>.html if available |
| | | */ |
| | | private function about_content($attrib) |
| | | protected function about_content($attrib) |
| | | { |
| | | $content = ''; |
| | | $filenames = array( |
| | |
| | | return $content; |
| | | } |
| | | |
| | | } // end class rcube_template |
| | | |
| | | |
| | | } |
File was renamed from program/include/rcube_json_output.php |
| | |
| | | |
| | | /* |
| | | +-----------------------------------------------------------------------+ |
| | | | program/include/rcube_json_output.php | |
| | | | program/include/rcube_output_json.php | |
| | | | | |
| | | | This file is part of the Roundcube Webmail client | |
| | | | Copyright (C) 2008-2010, The Roundcube Dev Team | |
| | | | Copyright (C) 2008-2012, The Roundcube Dev Team | |
| | | | | |
| | | | Licensed under the GNU General Public License version 3 or | |
| | | | any later version with exceptions for skins & plugins. | |
| | | | See the README file for a full license statement. | |
| | | | | |
| | | | PURPOSE: | |
| | | | Class to handle HTML page output using a skin template. | |
| | | | Extends rcube_html_page class from rcube_shared.inc | |
| | | | | |
| | | | Class to handle JSON (AJAX) output | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Thomas Bruederli <roundcube@gmail.com> | |
| | | | Author: Aleksander Machniak <alec@alec.pl> | |
| | | +-----------------------------------------------------------------------+ |
| | | |
| | | $Id$ |
| | |
| | | * |
| | | * @package View |
| | | */ |
| | | class rcube_json_output |
| | | class rcube_output_json extends rcube_output |
| | | { |
| | | /** |
| | | * Stores configuration object. |
| | | * |
| | | * @var rcube_config |
| | | */ |
| | | private $config; |
| | | private $charset = RCMAIL_CHARSET; |
| | | private $texts = array(); |
| | | private $commands = array(); |
| | | private $callbacks = array(); |
| | | private $message = null; |
| | | protected $texts = array(); |
| | | protected $commands = array(); |
| | | protected $callbacks = array(); |
| | | protected $message = null; |
| | | |
| | | public $browser; |
| | | public $env = array(); |
| | | public $type = 'js'; |
| | | public $ajax_call = true; |
| | | |
| | | |
| | | /** |
| | | * Constructor |
| | | */ |
| | | public function __construct($task=null) |
| | | { |
| | | $this->config = rcmail::get_instance()->config; |
| | | $this->browser = new rcube_browser(); |
| | | } |
| | | |
| | | |
| | | /** |
| | |
| | | |
| | | |
| | | /** |
| | | * @ignore |
| | | */ |
| | | function set_charset($charset) |
| | | { |
| | | // ignore: $this->charset = $charset; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get charset for output |
| | | * |
| | | * @return string Output charset |
| | | */ |
| | | function get_charset() |
| | | { |
| | | return $this->charset; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Register a template object handler |
| | | * |
| | | * @param string $obj Object name |
| | | * @param string $func Function name to call |
| | | * @return void |
| | | */ |
| | | public function add_handler($obj, $func) |
| | | { |
| | |
| | | * Register a list of template object handlers |
| | | * |
| | | * @param array $arr Hash array with object=>handler pairs |
| | | * @return void |
| | | */ |
| | | public function add_handlers($arr) |
| | | { |
| | |
| | | $args = $args[0]; |
| | | |
| | | foreach ($args as $name) { |
| | | $this->texts[$name] = rcube_label($name); |
| | | $this->texts[$name] = $this->app->gettext($name); |
| | | } |
| | | } |
| | | |
| | |
| | | public function show_message($message, $type='notice', $vars=null, $override=true, $timeout=0) |
| | | { |
| | | if ($override || !$this->message) { |
| | | if (rcube_label_exists($message)) { |
| | | if (!empty($vars)) |
| | | $vars = array_map('Q', $vars); |
| | | $msgtext = rcube_label(array('name' => $message, 'vars' => $vars)); |
| | | if ($this->app->text_exists($message)) { |
| | | if (!empty($vars)) { |
| | | $vars = array_map(array('rcube_ui', 'Q'), $vars); |
| | | } |
| | | $msgtext = $this->app->gettext(array('name' => $message, 'vars' => $vars)); |
| | | } |
| | | else |
| | | $msgtext = $message; |
| | |
| | | */ |
| | | public function reset() |
| | | { |
| | | $this->env = array(); |
| | | parent::reset(); |
| | | $this->texts = array(); |
| | | $this->commands = array(); |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * Show error page and terminate script execution |
| | | * |
| | | * @param int $code Error code |
| | | * @param string $message Error message |
| | | */ |
| | | public function raise_error($code, $message) |
| | | { |
| | | $this->show_message("Application Error ($code): $message", 'error'); |
| | | $this->remote_response(); |
| | | exit; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Send an AJAX response with executable JS code |
| | | * |
| | | * @param string $add Additional JS code |
| | |
| | | * @return void |
| | | * @deprecated |
| | | */ |
| | | public function remote_response($add='') |
| | | protected function remote_response($add='') |
| | | { |
| | | static $s_header_sent = false; |
| | | |
| | | if (!$s_header_sent) { |
| | | $s_header_sent = true; |
| | | send_nocacheing_headers(); |
| | | $this->nocacheing_headers(); |
| | | header('Content-Type: text/plain; charset=' . $this->get_charset()); |
| | | } |
| | | |
| | |
| | | $rcmail = rcmail::get_instance(); |
| | | $response['action'] = $rcmail->action; |
| | | |
| | | if ($unlock = get_input_value('_unlock', RCUBE_INPUT_GPC)) { |
| | | if ($unlock = rcube_ui::get_input_value('_unlock', rcube_ui::INPUT_GPC)) { |
| | | $response['unlock'] = $unlock; |
| | | } |
| | | |
| | |
| | | if (!empty($this->callbacks)) |
| | | $response['callbacks'] = $this->callbacks; |
| | | |
| | | echo json_serialize($response); |
| | | echo self::json_serialize($response); |
| | | } |
| | | |
| | | |
| | |
| | | * |
| | | * @return string $out |
| | | */ |
| | | private function get_js_commands() |
| | | protected function get_js_commands() |
| | | { |
| | | $out = ''; |
| | | |
| | | foreach ($this->commands as $i => $args) { |
| | | $method = array_shift($args); |
| | | foreach ($args as $i => $arg) { |
| | | $args[$i] = json_serialize($arg); |
| | | $args[$i] = self::json_serialize($arg); |
| | | } |
| | | |
| | | $out .= sprintf( |
| | |
| | | public function load_config($fname = 'config.inc.php') |
| | | { |
| | | $fpath = $this->home.'/'.$fname; |
| | | $rcmail = rcmail::get_instance(); |
| | | $rcmail = rcube::get_instance(); |
| | | if (is_file($fpath) && !$rcmail->config->load_from_file($fpath)) { |
| | | raise_error(array('code' => 527, 'type' => 'php', |
| | | rcube::raise_error(array( |
| | | 'code' => 527, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Failed to load config from $fpath"), true, false); |
| | | return false; |
| | |
| | | foreach ($texts as $key => $value) |
| | | $add[$domain.'.'.$key] = $value; |
| | | |
| | | $rcmail = rcmail::get_instance(); |
| | | $rcmail = rcube::get_instance(); |
| | | $rcmail->load_language($lang, $add); |
| | | |
| | | // add labels to client |
| | |
| | | */ |
| | | public function gettext($p) |
| | | { |
| | | return rcmail::get_instance()->gettext($p, $this->ID); |
| | | return rcube::get_instance()->gettext($p, $this->ID); |
| | | } |
| | | |
| | | /** |
| | |
| | | */ |
| | | public function local_skin_path() |
| | | { |
| | | $skin_path = 'skins/'.$this->api->config->get('skin'); |
| | | if (!is_dir(realpath(slashify($this->home) . $skin_path))) |
| | | $skin_path = 'skins/default'; |
| | | $rcmail = rcube::get_instance(); |
| | | $skin_path = 'skins/' . $rcmail->config->get('skin'); |
| | | if (!is_dir(realpath(slashify($this->home) . $skin_path))) |
| | | $skin_path = 'skins/default'; |
| | | return $skin_path; |
| | | } |
| | | |
| | |
| | | |
| | | */ |
| | | |
| | | // location where plugins are loade from |
| | | if (!defined('RCMAIL_PLUGINS_DIR')) |
| | | define('RCMAIL_PLUGINS_DIR', INSTALL_PATH . 'plugins/'); |
| | | |
| | | |
| | | /** |
| | | * The plugin loader and global API |
| | | * |
| | |
| | | |
| | | public $dir; |
| | | public $url = 'plugins/'; |
| | | public $task = ''; |
| | | public $output; |
| | | public $config; |
| | | |
| | | public $handlers = array(); |
| | | private $plugins = array(); |
| | |
| | | private $actionmap = array(); |
| | | private $objectsmap = array(); |
| | | private $template_contents = array(); |
| | | private $required_plugins = array('filesystem_attachments', 'jqueryui'); |
| | | private $active_hook = false; |
| | | |
| | | // Deprecated names of hooks, will be removed after 0.5-stable release |
| | |
| | | */ |
| | | private function __construct() |
| | | { |
| | | $this->dir = INSTALL_PATH . $this->url; |
| | | $this->dir = slashify(RCMAIL_PLUGINS_DIR); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initialize plugin engine |
| | | * |
| | | * This has to be done after rcmail::load_gui() or rcmail::json_init() |
| | | * was called because plugins need to have access to rcmail->output |
| | | * |
| | | * @param object rcube Instance of the rcube base class |
| | | * @param string Current application task (used for conditional plugin loading) |
| | | */ |
| | | public function init($app, $task = '') |
| | | { |
| | | $this->task = $task; |
| | | $this->output = $app->output; |
| | | |
| | | // register an internal hook |
| | | $this->register_hook('template_container', array($this, 'template_container_hook')); |
| | | |
| | | // maybe also register a shudown function which triggers shutdown functions of all plugin objects |
| | | } |
| | | |
| | | |
| | |
| | | * |
| | | * This has to be done after rcmail::load_gui() or rcmail::json_init() |
| | | * was called because plugins need to have access to rcmail->output |
| | | * |
| | | * @param array List of configured plugins to load |
| | | * @param array List of plugins required by the application |
| | | */ |
| | | public function init() |
| | | public function load_plugins($plugins_enabled, $required_plugins = array()) |
| | | { |
| | | $rcmail = rcmail::get_instance(); |
| | | $this->output = $rcmail->output; |
| | | $this->config = $rcmail->config; |
| | | |
| | | $plugins_enabled = (array)$rcmail->config->get('plugins', array()); |
| | | foreach ($plugins_enabled as $plugin_name) { |
| | | $this->load_plugin($plugin_name); |
| | | } |
| | | |
| | | // check existance of all required core plugins |
| | | foreach ($this->required_plugins as $plugin_name) { |
| | | foreach ($required_plugins as $plugin_name) { |
| | | $loaded = false; |
| | | foreach ($this->plugins as $plugin) { |
| | | if ($plugin instanceof $plugin_name) { |
| | |
| | | |
| | | // trigger fatal error if still not loaded |
| | | if (!$loaded) { |
| | | raise_error(array('code' => 520, 'type' => 'php', |
| | | rcube::raise_error(array('code' => 520, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Requried plugin $plugin_name was not loaded"), true, true); |
| | | } |
| | | } |
| | | |
| | | // register an internal hook |
| | | $this->register_hook('template_container', array($this, 'template_container_hook')); |
| | | |
| | | // maybe also register a shudown function which triggers shutdown functions of all plugin objects |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Load the specified plugin |
| | |
| | | public function load_plugin($plugin_name) |
| | | { |
| | | static $plugins_dir; |
| | | |
| | | $rcmail = rcmail::get_instance(); |
| | | |
| | | if (!$plugins_dir) { |
| | | $dir = dir($this->dir); |
| | |
| | | // check inheritance... |
| | | if (is_subclass_of($plugin, 'rcube_plugin')) { |
| | | // ... task, request type and framed mode |
| | | if ((!$plugin->task || preg_match('/^('.$plugin->task.')$/i', $rcmail->task)) |
| | | && (!$plugin->noajax || (is_object($rcmail->output) && is_a($rcmail->output, 'rcube_template'))) |
| | | if ((!$plugin->task || preg_match('/^('.$plugin->task.')$/i', $this->task)) |
| | | && (!$plugin->noajax || (is_object($this->output) && $this->output->type == 'html')) |
| | | && (!$plugin->noframe || empty($_REQUEST['_framed'])) |
| | | ) { |
| | | $plugin->init(); |
| | |
| | | } |
| | | } |
| | | else { |
| | | raise_error(array('code' => 520, 'type' => 'php', |
| | | rcube::raise_error(array('code' => 520, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "No plugin class $plugin_name found in $fn"), true, false); |
| | | } |
| | | } |
| | | else { |
| | | raise_error(array('code' => 520, 'type' => 'php', |
| | | rcube::raise_error(array('code' => 520, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Failed to load plugin file $fn"), true, false); |
| | | } |
| | |
| | | { |
| | | if (is_callable($callback)) { |
| | | if (isset($this->deprecated_hooks[$hook])) { |
| | | raise_error(array('code' => 522, 'type' => 'php', |
| | | rcube::raise_error(array('code' => 522, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Deprecated hook name. ".$hook.' -> '.$this->deprecated_hooks[$hook]), true, false); |
| | | $hook = $this->deprecated_hooks[$hook]; |
| | |
| | | $this->handlers[$hook][] = $callback; |
| | | } |
| | | else |
| | | raise_error(array('code' => 521, 'type' => 'php', |
| | | rcube::raise_error(array('code' => 521, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Invalid callback function for $hook"), true, false); |
| | | } |
| | |
| | | $this->actionmap[$action] = $owner; |
| | | } |
| | | else { |
| | | raise_error(array('code' => 523, 'type' => 'php', |
| | | rcube::raise_error(array('code' => 523, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Cannot register action $action; already taken by another plugin"), true, false); |
| | | } |
| | |
| | | call_user_func($this->actions[$action]); |
| | | } |
| | | else { |
| | | raise_error(array('code' => 524, 'type' => 'php', |
| | | rcube::raise_error(array('code' => 524, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "No handler found for action $action"), true, true); |
| | | } |
| | |
| | | $name = 'plugin.'.$name; |
| | | |
| | | // can register handler only if it's not taken or registered by myself |
| | | if (!isset($this->objectsmap[$name]) || $this->objectsmap[$name] == $owner) { |
| | | if (is_object($this->output) && (!isset($this->objectsmap[$name]) || $this->objectsmap[$name] == $owner)) { |
| | | $this->output->add_handler($name, $callback); |
| | | $this->objectsmap[$name] = $owner; |
| | | } |
| | | else { |
| | | raise_error(array('code' => 525, 'type' => 'php', |
| | | rcube::raise_error(array('code' => 525, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Cannot register template handler $name; already taken by another plugin"), true, false); |
| | | 'message' => "Cannot register template handler $name; already taken by another plugin or no output object available"), true, false); |
| | | } |
| | | } |
| | | |
| | |
| | | public function register_task($task, $owner) |
| | | { |
| | | if ($task != asciiwords($task)) { |
| | | raise_error(array('code' => 526, 'type' => 'php', |
| | | rcube::raise_error(array('code' => 526, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Invalid task name: $task. Only characters [a-z0-9_.-] are allowed"), true, false); |
| | | } |
| | | else if (in_array($task, rcmail::$main_tasks)) { |
| | | raise_error(array('code' => 526, 'type' => 'php', |
| | | rcube::raise_error(array('code' => 526, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Cannot register taks $task; already taken by another plugin or the application itself"), true, false); |
| | | } |
| | |
| | | */ |
| | | public function include_script($fn) |
| | | { |
| | | if ($this->output->type == 'html') { |
| | | if (is_object($this->output) && $this->output->type == 'html') { |
| | | $src = $this->resource_url($fn); |
| | | $this->output->add_header(html::tag('script', array('type' => "text/javascript", 'src' => $src))); |
| | | } |
| | |
| | | */ |
| | | public function include_stylesheet($fn) |
| | | { |
| | | if ($this->output->type == 'html') { |
| | | if (is_object($this->output) && $this->output->type == 'html') { |
| | | $src = $this->resource_url($fn); |
| | | $this->output->include_css($src); |
| | | } |
| | |
| | | array($this, 'gc')); |
| | | } |
| | | else { |
| | | raise_error(array('code' => 604, 'type' => 'db', |
| | | rcube::raise_error(array('code' => 604, 'type' => 'db', |
| | | 'line' => __LINE__, 'file' => __FILE__, |
| | | 'message' => "Failed to connect to memcached. Please check configuration"), |
| | | true, true); |
| | |
| | | public function db_read($key) |
| | | { |
| | | $sql_result = $this->db->query( |
| | | "SELECT vars, ip, changed FROM ".get_table_name('session') |
| | | "SELECT vars, ip, changed FROM ".$this->db->table_name('session') |
| | | ." WHERE sess_id = ?", $key); |
| | | |
| | | if ($sql_result && ($sql_arr = $this->db->fetch_assoc($sql_result))) { |
| | |
| | | if ($newvars !== $oldvars) { |
| | | $this->db->query( |
| | | sprintf("UPDATE %s SET vars=?, changed=%s WHERE sess_id=?", |
| | | get_table_name('session'), $now), |
| | | $this->db->table_name('session'), $now), |
| | | base64_encode($newvars), $key); |
| | | } |
| | | else if ($ts - $this->changed > $this->lifetime / 2) { |
| | | $this->db->query("UPDATE ".get_table_name('session')." SET changed=$now WHERE sess_id=?", $key); |
| | | $this->db->query("UPDATE ".$this->db->table_name('session')." SET changed=$now WHERE sess_id=?", $key); |
| | | } |
| | | } |
| | | else { |
| | | $this->db->query( |
| | | sprintf("INSERT INTO %s (sess_id, vars, ip, created, changed) ". |
| | | "VALUES (?, ?, ?, %s, %s)", |
| | | get_table_name('session'), $now, $now), |
| | | $this->db->table_name('session'), $now, $now), |
| | | $key, base64_encode($vars), (string)$this->ip); |
| | | } |
| | | |
| | |
| | | public function db_destroy($key) |
| | | { |
| | | $this->db->query( |
| | | sprintf("DELETE FROM %s WHERE sess_id = ?", get_table_name('session')), |
| | | sprintf("DELETE FROM %s WHERE sess_id = ?", $this->db->table_name('session')), |
| | | $key); |
| | | |
| | | return true; |
| | |
| | | // just delete all expired sessions |
| | | $this->db->query( |
| | | sprintf("DELETE FROM %s WHERE changed < %s", |
| | | get_table_name('session'), $this->db->fromunixtime(time() - $maxlifetime))); |
| | | $this->db->table_name('session'), $this->db->fromunixtime(time() - $maxlifetime))); |
| | | |
| | | $this->gc(); |
| | | |
| | |
| | | */ |
| | | public function gc() |
| | | { |
| | | foreach ($this->gc_handlers as $fct) |
| | | foreach ($this->gc_handlers as $fct) { |
| | | call_user_func($fct); |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | $auth_string = "$this->key,$this->secret,$timeslot"; |
| | | return "S" . (function_exists('sha1') ? sha1($auth_string) : md5($auth_string)); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * |
| | | */ |
| | | function log($line) |
| | | { |
| | | if ($this->logging) |
| | | write_log('session', $line); |
| | | rcmail::write_log('session', $line); |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | /* |
| | | +-----------------------------------------------------------------------+ |
| | | | rcube_shared.inc | |
| | | | program/include/rcube_shared.inc | |
| | | | | |
| | | | This file is part of the Roundcube PHP suite | |
| | | | Copyright (C) 2005-2007, The Roundcube Dev Team | |
| | | | Copyright (C) 2005-2012, The Roundcube Dev Team | |
| | | | | |
| | | | Licensed under the GNU General Public License version 3 or | |
| | | | any later version with exceptions for skins & plugins. | |
| | | | See the README file for a full license statement. | |
| | | | | |
| | | | CONTENTS: | |
| | | | Shared functions and classes used in PHP projects | |
| | | | Shared functions used by Roundcube Framework | |
| | | | | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Thomas Bruederli <roundcube@gmail.com> | |
| | |
| | | |
| | | |
| | | /** |
| | | * Send HTTP headers to prevent caching this page |
| | | */ |
| | | function send_nocacheing_headers() |
| | | { |
| | | global $OUTPUT; |
| | | |
| | | if (headers_sent()) |
| | | return; |
| | | |
| | | header("Expires: ".gmdate("D, d M Y H:i:s")." GMT"); |
| | | header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT"); |
| | | // Request browser to disable DNS prefetching (CVE-2010-0464) |
| | | header("X-DNS-Prefetch-Control: off"); |
| | | |
| | | // We need to set the following headers to make downloads work using IE in HTTPS mode. |
| | | if ($OUTPUT->browser->ie && rcube_https_check()) { |
| | | header('Pragma: private'); |
| | | header("Cache-Control: private, must-revalidate"); |
| | | } else { |
| | | header("Cache-Control: private, no-cache, must-revalidate, post-check=0, pre-check=0"); |
| | | header("Pragma: no-cache"); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Send header with expire date 30 days in future |
| | | * |
| | | * @param int Expiration time in seconds |
| | | */ |
| | | function send_future_expire_header($offset=2600000) |
| | | { |
| | | if (headers_sent()) |
| | | return; |
| | | |
| | | header("Expires: ".gmdate("D, d M Y H:i:s", mktime()+$offset)." GMT"); |
| | | header("Cache-Control: max-age=$offset"); |
| | | header("Pragma: "); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Similar function as in_array() but case-insensitive |
| | | * |
| | | * @param mixed Needle value |
| | | * @param array Array to search in |
| | | * @param string $needle Needle value |
| | | * @param array $heystack Array to search in |
| | | * |
| | | * @return boolean True if found, False if not |
| | | */ |
| | | function in_array_nocase($needle, $haystack) |
| | | { |
| | | $needle = mb_strtolower($needle); |
| | | foreach ($haystack as $value) |
| | | if ($needle===mb_strtolower($value)) |
| | | return true; |
| | | $needle = mb_strtolower($needle); |
| | | foreach ($haystack as $value) { |
| | | if ($needle === mb_strtolower($value)) { |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | return false; |
| | | return false; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Find out if the string content means TRUE or FALSE |
| | | * Find out if the string content means true or false |
| | | * |
| | | * @param string Input value |
| | | * @return boolean Imagine what! |
| | | * @param string $str Input value |
| | | * |
| | | * @return boolean Boolean value |
| | | */ |
| | | function get_boolean($str) |
| | | { |
| | | $str = strtolower($str); |
| | | if (in_array($str, array('false', '0', 'no', 'off', 'nein', ''), TRUE)) |
| | | return FALSE; |
| | | else |
| | | return TRUE; |
| | | $str = strtolower($str); |
| | | |
| | | return !in_array($str, array('false', '0', 'no', 'off', 'nein', ''), true); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Parse a human readable string for a number of bytes |
| | | * Parse a human readable string for a number of bytes. |
| | | * |
| | | * @param string Input string |
| | | * @param string $str Input string |
| | | * |
| | | * @return float Number of bytes |
| | | */ |
| | | function parse_bytes($str) |
| | | { |
| | | if (is_numeric($str)) |
| | | return floatval($str); |
| | | |
| | | if (preg_match('/([0-9\.]+)\s*([a-z]*)/i', $str, $regs)) |
| | | { |
| | | $bytes = floatval($regs[1]); |
| | | switch (strtolower($regs[2])) |
| | | { |
| | | case 'g': |
| | | case 'gb': |
| | | $bytes *= 1073741824; |
| | | break; |
| | | case 'm': |
| | | case 'mb': |
| | | $bytes *= 1048576; |
| | | break; |
| | | case 'k': |
| | | case 'kb': |
| | | $bytes *= 1024; |
| | | break; |
| | | if (is_numeric($str)) { |
| | | return floatval($str); |
| | | } |
| | | } |
| | | |
| | | return floatval($bytes); |
| | | } |
| | | |
| | | /** |
| | | * Create a human readable string for a number of bytes |
| | | * |
| | | * @param int Number of bytes |
| | | * @return string Byte string |
| | | */ |
| | | function show_bytes($bytes) |
| | | { |
| | | if ($bytes >= 1073741824) |
| | | { |
| | | $gb = $bytes/1073741824; |
| | | $str = sprintf($gb>=10 ? "%d " : "%.1f ", $gb) . rcube_label('GB'); |
| | | } |
| | | else if ($bytes >= 1048576) |
| | | { |
| | | $mb = $bytes/1048576; |
| | | $str = sprintf($mb>=10 ? "%d " : "%.1f ", $mb) . rcube_label('MB'); |
| | | } |
| | | else if ($bytes >= 1024) |
| | | $str = sprintf("%d ", round($bytes/1024)) . rcube_label('KB'); |
| | | else |
| | | $str = sprintf('%d ', $bytes) . rcube_label('B'); |
| | | |
| | | return $str; |
| | | } |
| | | |
| | | /** |
| | | * Wrapper function for wordwrap |
| | | */ |
| | | function rc_wordwrap($string, $width=75, $break="\n", $cut=false) |
| | | { |
| | | $para = explode($break, $string); |
| | | $string = ''; |
| | | while (count($para)) { |
| | | $line = array_shift($para); |
| | | if ($line[0] == '>') { |
| | | $string .= $line.$break; |
| | | continue; |
| | | } |
| | | $list = explode(' ', $line); |
| | | $len = 0; |
| | | while (count($list)) { |
| | | $line = array_shift($list); |
| | | $l = mb_strlen($line); |
| | | $newlen = $len + $l + ($len ? 1 : 0); |
| | | |
| | | if ($newlen <= $width) { |
| | | $string .= ($len ? ' ' : '').$line; |
| | | $len += (1 + $l); |
| | | } else { |
| | | if ($l > $width) { |
| | | if ($cut) { |
| | | $start = 0; |
| | | while ($l) { |
| | | $str = mb_substr($line, $start, $width); |
| | | $strlen = mb_strlen($str); |
| | | $string .= ($len ? $break : '').$str; |
| | | $start += $strlen; |
| | | $l -= $strlen; |
| | | $len = $strlen; |
| | | } |
| | | } else { |
| | | $string .= ($len ? $break : '').$line; |
| | | if (count($list)) $string .= $break; |
| | | $len = 0; |
| | | } |
| | | } else { |
| | | $string .= $break.$line; |
| | | $len = $l; |
| | | if (preg_match('/([0-9\.]+)\s*([a-z]*)/i', $str, $regs)) { |
| | | $bytes = floatval($regs[1]); |
| | | switch (strtolower($regs[2])) { |
| | | case 'g': |
| | | case 'gb': |
| | | $bytes *= 1073741824; |
| | | break; |
| | | case 'm': |
| | | case 'mb': |
| | | $bytes *= 1048576; |
| | | break; |
| | | case 'k': |
| | | case 'kb': |
| | | $bytes *= 1024; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | if (count($para)) $string .= $break; |
| | | } |
| | | return $string; |
| | | |
| | | return floatval($bytes); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Read a specific HTTP request header |
| | | * Read a specific HTTP request header. |
| | | * |
| | | * @access static |
| | | * @param string $name Header name |
| | | * |
| | | * @return mixed Header value or null if not available |
| | | */ |
| | | function rc_request_header($name) |
| | | function rcube_request_header($name) |
| | | { |
| | | if (function_exists('getallheaders')) |
| | | { |
| | | $hdrs = array_change_key_case(getallheaders(), CASE_UPPER); |
| | | $key = strtoupper($name); |
| | | } |
| | | else |
| | | { |
| | | $key = 'HTTP_' . strtoupper(strtr($name, '-', '_')); |
| | | $hdrs = array_change_key_case($_SERVER, CASE_UPPER); |
| | | } |
| | | if (function_exists('getallheaders')) { |
| | | $hdrs = array_change_key_case(getallheaders(), CASE_UPPER); |
| | | $key = strtoupper($name); |
| | | } |
| | | else { |
| | | $key = 'HTTP_' . strtoupper(strtr($name, '-', '_')); |
| | | $hdrs = array_change_key_case($_SERVER, CASE_UPPER); |
| | | } |
| | | |
| | | return $hdrs[$key]; |
| | | return $hdrs[$key]; |
| | | } |
| | | |
| | | |
| | |
| | | * Delete all files within a folder |
| | | * |
| | | * @param string Path to directory |
| | | * |
| | | * @return boolean True on success, False if directory was not found |
| | | */ |
| | | function clear_directory($dir_path) |
| | | { |
| | | $dir = @opendir($dir_path); |
| | | if(!$dir) return FALSE; |
| | | $dir = @opendir($dir_path); |
| | | if (!$dir) { |
| | | return false; |
| | | } |
| | | |
| | | while ($file = readdir($dir)) |
| | | if (strlen($file)>2) |
| | | unlink("$dir_path/$file"); |
| | | while ($file = readdir($dir)) { |
| | | if (strlen($file) > 2) { |
| | | unlink("$dir_path/$file"); |
| | | } |
| | | } |
| | | |
| | | closedir($dir); |
| | | return TRUE; |
| | | closedir($dir); |
| | | |
| | | return true; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create a unix timestamp with a specified offset from now |
| | | * Create a unix timestamp with a specified offset from now. |
| | | * |
| | | * @param string String representation of the offset (e.g. 20min, 5h, 2days) |
| | | * @param int Factor to multiply with the offset |
| | | * @param string $offset_str String representation of the offset (e.g. 20min, 5h, 2days) |
| | | * @param int $factor Factor to multiply with the offset |
| | | * |
| | | * @return int Unix timestamp |
| | | */ |
| | | function get_offset_time($offset_str, $factor=1) |
| | | { |
| | | if (preg_match('/^([0-9]+)\s*([smhdw])/i', $offset_str, $regs)) |
| | | { |
| | | $amount = (int)$regs[1]; |
| | | $unit = strtolower($regs[2]); |
| | | } |
| | | else |
| | | { |
| | | $amount = (int)$offset_str; |
| | | $unit = 's'; |
| | | } |
| | | |
| | | $ts = mktime(); |
| | | switch ($unit) |
| | | { |
| | | case 'w': |
| | | $amount *= 7; |
| | | case 'd': |
| | | $amount *= 24; |
| | | case 'h': |
| | | $amount *= 60; |
| | | case 'm': |
| | | $amount *= 60; |
| | | case 's': |
| | | $ts += $amount * $factor; |
| | | } |
| | | |
| | | return $ts; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Truncate string if it is longer than the allowed length |
| | | * Replace the middle or the ending part of a string with a placeholder |
| | | * |
| | | * @param string Input string |
| | | * @param int Max. length |
| | | * @param string Replace removed chars with this |
| | | * @param bool Set to True if string should be truncated from the end |
| | | * @return string Abbreviated string |
| | | */ |
| | | function abbreviate_string($str, $maxlength, $place_holder='...', $ending=false) |
| | | { |
| | | $length = mb_strlen($str); |
| | | |
| | | if ($length > $maxlength) |
| | | { |
| | | if ($ending) |
| | | return mb_substr($str, 0, $maxlength) . $place_holder; |
| | | |
| | | $place_holder_length = mb_strlen($place_holder); |
| | | $first_part_length = floor(($maxlength - $place_holder_length)/2); |
| | | $second_starting_location = $length - $maxlength + $first_part_length + $place_holder_length; |
| | | $str = mb_substr($str, 0, $first_part_length) . $place_holder . mb_substr($str, $second_starting_location); |
| | | } |
| | | |
| | | return $str; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * A method to guess the mime_type of an attachment. |
| | | * |
| | | * @param string $path Path to the file. |
| | | * @param string $name File name (with suffix) |
| | | * @param string $failover Mime type supplied for failover. |
| | | * @param string $is_stream Set to True if $path contains file body |
| | | * |
| | | * @return string |
| | | * @author Till Klampaeckel <till@php.net> |
| | | * @see http://de2.php.net/manual/en/ref.fileinfo.php |
| | | * @see http://de2.php.net/mime_content_type |
| | | */ |
| | | function rc_mime_content_type($path, $name, $failover = 'application/octet-stream', $is_stream=false) |
| | | { |
| | | $mime_type = null; |
| | | $mime_magic = rcmail::get_instance()->config->get('mime_magic'); |
| | | $mime_ext = @include(RCMAIL_CONFIG_DIR . '/mimetypes.php'); |
| | | |
| | | // use file name suffix with hard-coded mime-type map |
| | | if (is_array($mime_ext) && $name) { |
| | | if ($suffix = substr($name, strrpos($name, '.')+1)) { |
| | | $mime_type = $mime_ext[strtolower($suffix)]; |
| | | } |
| | | } |
| | | |
| | | // try fileinfo extension if available |
| | | if (!$mime_type && function_exists('finfo_open')) { |
| | | if ($finfo = finfo_open(FILEINFO_MIME, $mime_magic)) { |
| | | if ($is_stream) |
| | | $mime_type = finfo_buffer($finfo, $path); |
| | | else |
| | | $mime_type = finfo_file($finfo, $path); |
| | | finfo_close($finfo); |
| | | } |
| | | } |
| | | |
| | | // try PHP's mime_content_type |
| | | if (!$mime_type && !$is_stream && function_exists('mime_content_type')) { |
| | | $mime_type = @mime_content_type($path); |
| | | } |
| | | |
| | | // fall back to user-submitted string |
| | | if (!$mime_type) { |
| | | $mime_type = $failover; |
| | | if (preg_match('/^([0-9]+)\s*([smhdw])/i', $offset_str, $regs)) { |
| | | $amount = (int)$regs[1]; |
| | | $unit = strtolower($regs[2]); |
| | | } |
| | | else { |
| | | // Sometimes (PHP-5.3?) content-type contains charset definition, |
| | | // Remove it (#1487122) also "charset=binary" is useless |
| | | $mime_type = array_shift(preg_split('/[; ]/', $mime_type)); |
| | | $amount = (int)$offset_str; |
| | | $unit = 's'; |
| | | } |
| | | |
| | | return $mime_type; |
| | | $ts = mktime(); |
| | | switch ($unit) { |
| | | case 'w': |
| | | $amount *= 7; |
| | | case 'd': |
| | | $amount *= 24; |
| | | case 'h': |
| | | $amount *= 60; |
| | | case 'm': |
| | | $amount *= 60; |
| | | case 's': |
| | | $ts += $amount * $factor; |
| | | } |
| | | |
| | | return $ts; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Detect image type of the given binary data by checking magic numbers |
| | | * Truncate string if it is longer than the allowed length. |
| | | * Replace the middle or the ending part of a string with a placeholder. |
| | | * |
| | | * @param string Binary file content |
| | | * @return string Detected mime-type or jpeg as fallback |
| | | * @param string $str Input string |
| | | * @param int $maxlength Max. length |
| | | * @param string $placeholder Replace removed chars with this |
| | | * @param bool $ending Set to True if string should be truncated from the end |
| | | * |
| | | * @return string Abbreviated string |
| | | */ |
| | | function rc_image_content_type($data) |
| | | function abbreviate_string($str, $maxlength, $placeholder='...', $ending=false) |
| | | { |
| | | $type = 'jpeg'; |
| | | if (preg_match('/^\x89\x50\x4E\x47/', $data)) $type = 'png'; |
| | | else if (preg_match('/^\x47\x49\x46\x38/', $data)) $type = 'gif'; |
| | | else if (preg_match('/^\x00\x00\x01\x00/', $data)) $type = 'ico'; |
| | | // else if (preg_match('/^\xFF\xD8\xFF\xE0/', $data)) $type = 'jpeg'; |
| | | $length = mb_strlen($str); |
| | | |
| | | return 'image/' . $type; |
| | | if ($length > $maxlength) { |
| | | if ($ending) { |
| | | return mb_substr($str, 0, $maxlength) . $placeholder; |
| | | } |
| | | |
| | | $placeholder_length = mb_strlen($placeholder); |
| | | $first_part_length = floor(($maxlength - $placeholder_length)/2); |
| | | $second_starting_location = $length - $maxlength + $first_part_length + $placeholder_length; |
| | | |
| | | $str = mb_substr($str, 0, $first_part_length) . $placeholder . mb_substr($str, $second_starting_location); |
| | | } |
| | | |
| | | return $str; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Explode quoted string |
| | | * |
| | | * |
| | | * @param string Delimiter expression string for preg_match() |
| | | * @param string Input string |
| | | */ |
| | | function rcube_explode_quoted_string($delimiter, $string) |
| | | { |
| | | $result = array(); |
| | | $strlen = strlen($string); |
| | | $result = array(); |
| | | $strlen = strlen($string); |
| | | |
| | | for ($q=$p=$i=0; $i < $strlen; $i++) { |
| | | if ($string[$i] == "\"" && $string[$i-1] != "\\") { |
| | | $q = $q ? false : true; |
| | | } |
| | | else if (!$q && preg_match("/$delimiter/", $string[$i])) { |
| | | $result[] = substr($string, $p, $i - $p); |
| | | $p = $i + 1; |
| | | for ($q=$p=$i=0; $i < $strlen; $i++) { |
| | | if ($string[$i] == "\"" && $string[$i-1] != "\\") { |
| | | $q = $q ? false : true; |
| | | } |
| | | else if (!$q && preg_match("/$delimiter/", $string[$i])) { |
| | | $result[] = substr($string, $p, $i - $p); |
| | | $p = $i + 1; |
| | | } |
| | | } |
| | | } |
| | | |
| | | $result[] = substr($string, $p); |
| | | return $result; |
| | | $result[] = substr($string, $p); |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Get all keys from array (recursive) |
| | | * |
| | | * @param array Input array |
| | | * @return array |
| | | * Get all keys from array (recursive). |
| | | * |
| | | * @param array $array Input array |
| | | * |
| | | * @return array List of array keys |
| | | */ |
| | | function array_keys_recursive($array) |
| | | { |
| | | $keys = array(); |
| | | $keys = array(); |
| | | |
| | | if (!empty($array)) |
| | | foreach ($array as $key => $child) { |
| | | $keys[] = $key; |
| | | foreach (array_keys_recursive($child) as $val) |
| | | $keys[] = $val; |
| | | if (!empty($array)) { |
| | | foreach ($array as $key => $child) { |
| | | $keys[] = $key; |
| | | foreach (array_keys_recursive($child) as $val) { |
| | | $keys[] = $val; |
| | | } |
| | | } |
| | | } |
| | | return $keys; |
| | | |
| | | return $keys; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Remove all non-ascii and non-word chars except ., -, _ |
| | | */ |
| | | function asciiwords($str, $css_id = false, $replace_with = '') |
| | | { |
| | | $allowed = 'a-z0-9\_\-' . (!$css_id ? '\.' : ''); |
| | | return preg_replace("/[^$allowed]/i", $replace_with, $str); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Remove single and double quotes from given string |
| | | * |
| | | * @param string Input value |
| | | * |
| | | * @return string Dequoted string |
| | | */ |
| | | function strip_quotes($str) |
| | | { |
| | | return str_replace(array("'", '"'), '', $str); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Remove new lines characters from given string |
| | | * |
| | | * @param string $str Input value |
| | | * |
| | | * @return string Stripped string |
| | | */ |
| | | function strip_newlines($str) |
| | | { |
| | | return preg_replace('/[\r\n]/', '', $str); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Improved equivalent to strtotime() |
| | | * |
| | | * @param string $date Date string |
| | | * |
| | | * @return int Unix timestamp |
| | | */ |
| | | 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; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Compose a valid representation of name and e-mail address |
| | | * |
| | | * @param string $email E-mail address |
| | | * @param string $name Person name |
| | | * |
| | | * @return string Formatted string |
| | | */ |
| | | function format_email_recipient($email, $name = '') |
| | | { |
| | | $email = trim($email); |
| | | |
| | | if ($name && $name != $email) { |
| | | // Special chars as defined by RFC 822 need to in quoted string (or escaped). |
| | | if (preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $name)) { |
| | | $name = '"'.addcslashes($name, '"').'"'; |
| | | } |
| | | |
| | | return "$name <$email>"; |
| | | } |
| | | |
| | | return $email; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * mbstring replacement functions |
| | | */ |
| | | |
| | | if (!extension_loaded('mbstring')) |
| | | { |
| | | function mb_strlen($str) |
| | | { |
| | | return strlen($str); |
| | | return strlen($str); |
| | | } |
| | | |
| | | function mb_strtolower($str) |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /* |
| | | * 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); |
| | | |
| | | if ($domain === false) { |
| | | return ''; |
| | | } |
| | | |
| | | return $at ? $user . '@' . $domain : $domain; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Use PHP5 autoload for dynamic class loading |
| | | * |
| | | * @todo Make Zend, PEAR etc play with this |
| | | * @todo Make our classes conform to a more straight forward CS. |
| | | */ |
| | | function rcube_autoload($classname) |
| | | { |
| | | $filename = preg_replace( |
| | | array( |
| | | '/MDB2_(.+)/', |
| | | '/Mail_(.+)/', |
| | | '/Net_(.+)/', |
| | | '/Auth_(.+)/', |
| | | '/^html_.+/', |
| | | '/^utf8$/', |
| | | ), |
| | | array( |
| | | 'MDB2/\\1', |
| | | 'Mail/\\1', |
| | | 'Net/\\1', |
| | | 'Auth/\\1', |
| | | 'html', |
| | | 'utf8.class', |
| | | ), |
| | | $classname |
| | | ); |
| | | |
| | | if ($fp = @fopen("$filename.php", 'r', true)) { |
| | | fclose($fp); |
| | | include_once("$filename.php"); |
| | | return true; |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * Local callback function for PEAR errors |
| | | */ |
| | | function rcube_pear_error($err) |
| | | { |
| | | error_log(sprintf("%s (%s): %s", |
| | | $err->getMessage(), |
| | | $err->getCode(), |
| | | $err->getUserinfo()), 0); |
| | | } |
| | |
| | | */ |
| | | public function connect($host=null, $port=null, $user=null, $pass=null) |
| | | { |
| | | $RCMAIL = rcmail::get_instance(); |
| | | $RCMAIL = rcube::get_instance(); |
| | | |
| | | // disconnect/destroy $this->conn |
| | | $this->disconnect(); |
| | |
| | | 'smtp_auth_callbacks' => array(), |
| | | )); |
| | | |
| | | $smtp_host = rcube_parse_host($CONFIG['smtp_server']); |
| | | $smtp_host = rcmail::parse_host($CONFIG['smtp_server']); |
| | | // when called from Installer it's possible to have empty $smtp_host here |
| | | if (!$smtp_host) $smtp_host = 'localhost'; |
| | | $smtp_port = is_numeric($CONFIG['smtp_port']) ? $CONFIG['smtp_port'] : 25; |
| | |
| | | */ |
| | | public function debug_handler(&$smtp, $message) |
| | | { |
| | | write_log('smtp', preg_replace('/\r\n$/', '', $message)); |
| | | rcmail::write_log('smtp', preg_replace('/\r\n$/', '', $message)); |
| | | } |
| | | |
| | | |
| | |
| | | $this->lang = $lang ? $lang : 'en'; |
| | | |
| | | if ($this->engine == 'pspell' && !extension_loaded('pspell')) { |
| | | raise_error(array( |
| | | rcube::raise_error(array( |
| | | 'code' => 500, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Pspell extension not available"), true, true); |
| | |
| | | private function update_dict() |
| | | { |
| | | if (strcasecmp($this->options['dictionary'], 'shared') != 0) { |
| | | $userid = (int) $this->rc->user->ID; |
| | | $userid = $this->rc->get_user_id(); |
| | | } |
| | | |
| | | $plugin = $this->rc->plugins->exec_hook('spell_dictionary_save', array( |
| | |
| | | if ($this->have_dict) { |
| | | if (!empty($this->dict)) { |
| | | $this->rc->db->query( |
| | | "UPDATE ".get_table_name('dictionary') |
| | | "UPDATE ".$this->rc->db->table_name('dictionary') |
| | | ." SET data = ?" |
| | | ." WHERE user_id " . ($plugin['userid'] ? "= ".$plugin['userid'] : "IS NULL") |
| | | ." WHERE user_id " . ($plugin['userid'] ? "= ".$this->rc->db->quote($plugin['userid']) : "IS NULL") |
| | | ." AND " . $this->rc->db->quoteIdentifier('language') . " = ?", |
| | | implode(' ', $plugin['dictionary']), $plugin['language']); |
| | | } |
| | | // don't store empty dict |
| | | else { |
| | | $this->rc->db->query( |
| | | "DELETE FROM " . get_table_name('dictionary') |
| | | ." WHERE user_id " . ($plugin['userid'] ? "= ".$plugin['userid'] : "IS NULL") |
| | | "DELETE FROM " . $this->rc->db->table_name('dictionary') |
| | | ." WHERE user_id " . ($plugin['userid'] ? "= ".$this->rc->db->quote($plugin['userid']) : "IS NULL") |
| | | ." AND " . $this->rc->db->quoteIdentifier('language') . " = ?", |
| | | $plugin['language']); |
| | | } |
| | | } |
| | | else if (!empty($this->dict)) { |
| | | $this->rc->db->query( |
| | | "INSERT INTO " .get_table_name('dictionary') |
| | | "INSERT INTO " .$this->rc->db->table_name('dictionary') |
| | | ." (user_id, " . $this->rc->db->quoteIdentifier('language') . ", data) VALUES (?, ?, ?)", |
| | | $plugin['userid'], $plugin['language'], implode(' ', $plugin['dictionary'])); |
| | | } |
| | |
| | | } |
| | | |
| | | if (strcasecmp($this->options['dictionary'], 'shared') != 0) { |
| | | $userid = (int) $this->rc->user->ID; |
| | | $userid = $this->rc->get_user_id(); |
| | | } |
| | | |
| | | $plugin = $this->rc->plugins->exec_hook('spell_dictionary_get', array( |
| | |
| | | if (empty($plugin['abort'])) { |
| | | $dict = array(); |
| | | $this->rc->db->query( |
| | | "SELECT data FROM ".get_table_name('dictionary') |
| | | ." WHERE user_id ". ($plugin['userid'] ? "= ".$plugin['userid'] : "IS NULL") |
| | | "SELECT data FROM ".$this->rc->db->table_name('dictionary') |
| | | ." WHERE user_id ". ($plugin['userid'] ? "= ".$this->rc->db->quote($plugin['userid']) : "IS NULL") |
| | | ." AND " . $this->rc->db->quoteIdentifier('language') . " = ?", |
| | | $plugin['language']); |
| | | |
| | |
| | | * @param int $uid Message UID to fetch |
| | | * @param string $folder Folder to read from |
| | | * |
| | | * @return object rcube_mail_header Message data |
| | | * @return object rcube_message_header Message data |
| | | */ |
| | | abstract function get_message($uid, $folder = null); |
| | | |
| | |
| | | * @param string $folder Folder to read from |
| | | * @param bool $force True to skip cache |
| | | * |
| | | * @return rcube_mail_header Message headers |
| | | * @return rcube_message_header Message headers |
| | | */ |
| | | abstract function get_message_headers($uid, $folder = null, $force = false); |
| | | |
| | |
| | | public function get_body($uid, $part = 1) |
| | | { |
| | | $headers = $this->get_message_headers($uid); |
| | | return rcube_charset_convert($this->get_message_part($uid, $part, null), |
| | | return rcube_charset::convert($this->get_message_part($uid, $part, null), |
| | | $headers->charset ? $headers->charset : $this->default_charset); |
| | | } |
| | | |
| | |
| | | */ |
| | | abstract function clear_cache($key = null, $prefix_mode = false); |
| | | |
| | | |
| | | /** |
| | | * Returns cached value |
| | | * |
| | |
| | | */ |
| | | abstract function get_cache($key); |
| | | |
| | | |
| | | /** |
| | | * Delete outdated cache entries |
| | | */ |
| | | abstract function expunge_cache(); |
| | | |
| | | } // end class rcube_storage |
| | | |
| | | |
| | | /** |
| | | * Class representing a message part |
| | | * |
| | | * @package Mail |
| | | */ |
| | | class rcube_message_part |
| | | { |
| | | var $mime_id = ''; |
| | | var $ctype_primary = 'text'; |
| | | var $ctype_secondary = 'plain'; |
| | | var $mimetype = 'text/plain'; |
| | | var $disposition = ''; |
| | | var $filename = ''; |
| | | var $encoding = '8bit'; |
| | | var $charset = ''; |
| | | var $size = 0; |
| | | var $headers = array(); |
| | | var $d_parameters = array(); |
| | | var $ctype_parameters = array(); |
| | | |
| | | function __clone() |
| | | { |
| | | if (isset($this->parts)) { |
| | | foreach ($this->parts as $idx => $part) { |
| | | if (is_object($part)) { |
| | | $this->parts[$idx] = clone $part; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Class for sorting an array of rcube_mail_header objects in a predetermined order. |
| | | * |
| | | * @package Mail |
| | | * @author Eric Stadtherr |
| | | */ |
| | | class rcube_header_sorter |
| | | { |
| | | private $uids = array(); |
| | | |
| | | |
| | | /** |
| | | * Set the predetermined sort order. |
| | | * |
| | | * @param array $index Numerically indexed array of IMAP UIDs |
| | | */ |
| | | function set_index($index) |
| | | { |
| | | $index = array_flip($index); |
| | | |
| | | $this->uids = $index; |
| | | } |
| | | |
| | | /** |
| | | * Sort the array of header objects |
| | | * |
| | | * @param array $headers Array of rcube_mail_header objects indexed by UID |
| | | */ |
| | | function sort_headers(&$headers) |
| | | { |
| | | uksort($headers, array($this, "compare_uids")); |
| | | } |
| | | |
| | | /** |
| | | * Sort method called by uksort() |
| | | * |
| | | * @param int $a Array key (UID) |
| | | * @param int $b Array key (UID) |
| | | */ |
| | | function compare_uids($a, $b) |
| | | { |
| | | // then find each sequence number in my ordered list |
| | | $posa = isset($this->uids[$a]) ? intval($this->uids[$a]) : -1; |
| | | $posb = isset($this->uids[$b]) ? intval($this->uids[$b]) : -1; |
| | | |
| | | // return the relative position as the comparison value |
| | | return $posa - $posb; |
| | | } |
| | | } |
| | |
| | | $i = $this->add($prefix . html::a(array( |
| | | 'href' => $url_prefix . $url, |
| | | 'target' => '_blank' |
| | | ), Q($url)) . $suffix); |
| | | ), rcube_ui::Q($url)) . $suffix); |
| | | } |
| | | |
| | | // Return valid link for recognized schemes, otherwise, return the unmodified string for unrecognized schemes. |
| | |
| | | |
| | | $i = $this->add(html::a(array( |
| | | 'href' => 'mailto:' . $href, |
| | | 'onclick' => "return ".JS_OBJECT_NAME.".command('compose','".JQ($href)."',this)", |
| | | ), Q($href)) . $suffix); |
| | | 'onclick' => "return ".JS_OBJECT_NAME.".command('compose','".rcube_ui::JQ($href)."',this)", |
| | | ), rcube_ui::Q($href)) . $suffix); |
| | | |
| | | return $i >= 0 ? $this->get_replacement($i) : ''; |
| | | } |
New file |
| | |
| | | <?php |
| | | |
| | | /* |
| | | +-----------------------------------------------------------------------+ |
| | | | program/include/rcube_ui.php | |
| | | | | |
| | | | This file is part of the Roundcube Webmail client | |
| | | | Copyright (C) 2005-2012, The Roundcube Dev Team | |
| | | | Copyright (C) 2011-2012, Kolab Systems AG | |
| | | | | |
| | | | Licensed under the GNU General Public License version 3 or | |
| | | | any later version with exceptions for skins & plugins. | |
| | | | See the README file for a full license statement. | |
| | | | | |
| | | | PURPOSE: | |
| | | | Provide basic functions for the webmail user interface | |
| | | | | |
| | | +-----------------------------------------------------------------------+ |
| | | | Author: Thomas Bruederli <roundcube@gmail.com> | |
| | | | Author: Aleksander Machniak <alec@alec.pl> | |
| | | +-----------------------------------------------------------------------+ |
| | | |
| | | $Id$ |
| | | |
| | | */ |
| | | |
| | | /** |
| | | * Roundcube Webmail functions for user interface |
| | | * |
| | | * @package Core |
| | | * @author Thomas Bruederli <roundcube@gmail.com> |
| | | * @author Aleksander Machniak <alec@alec.pl> |
| | | */ |
| | | class rcube_ui |
| | | { |
| | | // define constants for input reading |
| | | const INPUT_GET = 0x0101; |
| | | const INPUT_POST = 0x0102; |
| | | const INPUT_GPC = 0x0103; |
| | | |
| | | |
| | | /** |
| | | * Get localized text in the desired language |
| | | * It's a global wrapper for rcube::gettext() |
| | | * |
| | | * @param mixed $p Named parameters array or label name |
| | | * @param string $domain Domain to search in (e.g. plugin name) |
| | | * |
| | | * @return string Localized text |
| | | * @see rcube::gettext() |
| | | */ |
| | | public static function label($p, $domain = null) |
| | | { |
| | | return rcube::get_instance()->gettext($p, $domain); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Global wrapper of rcube::text_exists() |
| | | * to check whether a text label is defined |
| | | * |
| | | * @see rcube::text_exists() |
| | | */ |
| | | public static function label_exists($name, $domain = null, &$ref_domain = null) |
| | | { |
| | | return rcube::get_instance()->text_exists($name, $domain, $ref_domain); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Compose an URL for a specific action |
| | | * |
| | | * @param string Request action |
| | | * @param array More URL parameters |
| | | * @param string Request task (omit if the same) |
| | | * |
| | | * @return The application URL |
| | | */ |
| | | public static function url($action, $p = array(), $task = null) |
| | | { |
| | | return rcube::get_instance()->url((array)$p + array('_action' => $action, 'task' => $task)); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Replacing specials characters to a specific encoding type |
| | | * |
| | | * @param string Input string |
| | | * @param string Encoding type: text|html|xml|js|url |
| | | * @param string Replace mode for tags: show|replace|remove |
| | | * @param boolean Convert newlines |
| | | * |
| | | * @return string The quoted string |
| | | */ |
| | | public static function rep_specialchars_output($str, $enctype = '', $mode = '', $newlines = true) |
| | | { |
| | | static $html_encode_arr = false; |
| | | static $js_rep_table = false; |
| | | static $xml_rep_table = false; |
| | | |
| | | // encode for HTML output |
| | | if ($enctype == 'html') { |
| | | if (!$html_encode_arr) { |
| | | $html_encode_arr = get_html_translation_table(HTML_SPECIALCHARS); |
| | | unset($html_encode_arr['?']); |
| | | } |
| | | |
| | | $encode_arr = $html_encode_arr; |
| | | |
| | | // don't replace quotes and html tags |
| | | if ($mode == 'show' || $mode == '') { |
| | | $ltpos = strpos($str, '<'); |
| | | if ($ltpos !== false && strpos($str, '>', $ltpos) !== false) { |
| | | unset($encode_arr['"']); |
| | | unset($encode_arr['<']); |
| | | unset($encode_arr['>']); |
| | | unset($encode_arr['&']); |
| | | } |
| | | } |
| | | else if ($mode == 'remove') { |
| | | $str = strip_tags($str); |
| | | } |
| | | |
| | | $out = strtr($str, $encode_arr); |
| | | |
| | | // avoid douple quotation of & |
| | | $out = preg_replace('/&([A-Za-z]{2,6}|#[0-9]{2,4});/', '&\\1;', $out); |
| | | |
| | | return $newlines ? nl2br($out) : $out; |
| | | } |
| | | |
| | | // if the replace tables for XML and JS are not yet defined |
| | | if ($js_rep_table === false) { |
| | | $js_rep_table = $xml_rep_table = array(); |
| | | $xml_rep_table['&'] = '&'; |
| | | |
| | | // can be increased to support more charsets |
| | | for ($c=160; $c<256; $c++) { |
| | | $xml_rep_table[chr($c)] = "&#$c;"; |
| | | } |
| | | |
| | | $xml_rep_table['"'] = '"'; |
| | | $js_rep_table['"'] = '\\"'; |
| | | $js_rep_table["'"] = "\\'"; |
| | | $js_rep_table["\\"] = "\\\\"; |
| | | // Unicode line and paragraph separators (#1486310) |
| | | $js_rep_table[chr(hexdec(E2)).chr(hexdec(80)).chr(hexdec(A8))] = '
'; |
| | | $js_rep_table[chr(hexdec(E2)).chr(hexdec(80)).chr(hexdec(A9))] = '
'; |
| | | } |
| | | |
| | | // encode for javascript use |
| | | if ($enctype == 'js') { |
| | | return preg_replace(array("/\r?\n/", "/\r/", '/<\\//'), array('\n', '\n', '<\\/'), strtr($str, $js_rep_table)); |
| | | } |
| | | |
| | | // encode for plaintext |
| | | if ($enctype == 'text') { |
| | | return str_replace("\r\n", "\n", $mode=='remove' ? strip_tags($str) : $str); |
| | | } |
| | | |
| | | if ($enctype == 'url') { |
| | | return rawurlencode($str); |
| | | } |
| | | |
| | | // encode for XML |
| | | if ($enctype == 'xml') { |
| | | return strtr($str, $xml_rep_table); |
| | | } |
| | | |
| | | // no encoding given -> return original string |
| | | return $str; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Quote a given string. |
| | | * Shortcut function for self::rep_specialchars_output() |
| | | * |
| | | * @return string HTML-quoted string |
| | | * @see self::rep_specialchars_output() |
| | | */ |
| | | public static function Q($str, $mode = 'strict', $newlines = true) |
| | | { |
| | | return self::rep_specialchars_output($str, 'html', $mode, $newlines); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Quote a given string for javascript output. |
| | | * Shortcut function for self::rep_specialchars_output() |
| | | * |
| | | * @return string JS-quoted string |
| | | * @see self::rep_specialchars_output() |
| | | */ |
| | | public static function JQ($str) |
| | | { |
| | | return self::rep_specialchars_output($str, 'js'); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Read input value and convert it for internal use |
| | | * Performs stripslashes() and charset conversion if necessary |
| | | * |
| | | * @param string Field name to read |
| | | * @param int Source to get value from (GPC) |
| | | * @param boolean Allow HTML tags in field value |
| | | * @param string Charset to convert into |
| | | * |
| | | * @return string Field value or NULL if not available |
| | | */ |
| | | public static function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL) |
| | | { |
| | | $value = NULL; |
| | | |
| | | if ($source == self::INPUT_GET) { |
| | | if (isset($_GET[$fname])) { |
| | | $value = $_GET[$fname]; |
| | | } |
| | | } |
| | | else if ($source == self::INPUT_POST) { |
| | | if (isset($_POST[$fname])) { |
| | | $value = $_POST[$fname]; |
| | | } |
| | | } |
| | | else if ($source == self::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 self::parse_input_value($value, $allow_html, $charset); |
| | | } |
| | | |
| | | /** |
| | | * Parse/validate input value. See self::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 |
| | | * |
| | | * @return string Parsed value |
| | | */ |
| | | public static function parse_input_value($value, $allow_html=FALSE, $charset=NULL) |
| | | { |
| | | global $OUTPUT; |
| | | |
| | | if (empty($value)) { |
| | | return $value; |
| | | } |
| | | |
| | | if (is_array($value)) { |
| | | foreach ($value as $idx => $val) { |
| | | $value[$idx] = self::parse_input_value($val, $allow_html, $charset); |
| | | } |
| | | return $value; |
| | | } |
| | | |
| | | // strip single quotes if magic_quotes_sybase is enabled |
| | | if (ini_get('magic_quotes_sybase')) { |
| | | $value = str_replace("''", "'", $value); |
| | | } |
| | | // strip slashes if magic_quotes enabled |
| | | else if (get_magic_quotes_gpc() || get_magic_quotes_runtime()) { |
| | | $value = stripslashes($value); |
| | | } |
| | | |
| | | // 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 = rcube_charset::clean($value); |
| | | } |
| | | |
| | | // convert to internal charset |
| | | if ($charset && $output_charset) { |
| | | $value = rcube_charset::convert($value, $output_charset, $charset); |
| | | } |
| | | |
| | | return $value; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Convert array of request parameters (prefixed with _) |
| | | * to a regular array with non-prefixed keys. |
| | | * |
| | | * @param int $mode Source to get value from (GPC) |
| | | * @param string $ignore PCRE expression to skip parameters by name |
| | | * |
| | | * @return array Hash array with all request parameters |
| | | */ |
| | | public static function request2param($mode = null, $ignore = 'task|action') |
| | | { |
| | | $out = array(); |
| | | $src = $mode == self::INPUT_GET ? $_GET : ($mode == self::INPUT_POST ? $_POST : $_REQUEST); |
| | | |
| | | foreach ($src as $key => $value) { |
| | | $fname = $key[0] == '_' ? substr($key, 1) : $key; |
| | | if ($ignore && !preg_match('/^(' . $ignore . ')$/', $fname)) { |
| | | $out[$fname] = self::get_input_value($key, $mode); |
| | | } |
| | | } |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Convert the given string into a valid HTML identifier |
| | | * Same functionality as done in app.js with rcube_webmail.html_identifier() |
| | | */ |
| | | public static function html_identifier($str, $encode=false) |
| | | { |
| | | if ($encode) { |
| | | return rtrim(strtr(base64_encode($str), '+/', '-_'), '='); |
| | | } |
| | | else { |
| | | return asciiwords($str, true, '_'); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create a HTML table based on the given data |
| | | * |
| | | * @param array Named table attributes |
| | | * @param mixed Table row data. Either a two-dimensional array or a valid SQL result set |
| | | * @param array List of cols to show |
| | | * @param string Name of the identifier col |
| | | * |
| | | * @return string HTML table code |
| | | */ |
| | | public static function 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, self::Q(self::label($col))); |
| | | } |
| | | } |
| | | |
| | | if (!is_array($table_data)) { |
| | | $db = $RCMAIL->get_dbh(); |
| | | while ($table_data && ($sql_arr = $db->fetch_assoc($table_data))) { |
| | | $table->add_row(array('id' => 'rcmrow' . self::html_identifier($sql_arr[$id_col]))); |
| | | |
| | | // format each col |
| | | foreach ($a_show_cols as $col) { |
| | | $table->add($col, self::Q($sql_arr[$col])); |
| | | } |
| | | } |
| | | } |
| | | else { |
| | | foreach ($table_data as $row_data) { |
| | | $class = !empty($row_data['class']) ? $row_data['class'] : ''; |
| | | $rowid = 'rcmrow' . self::html_identifier($row_data[$id_col]); |
| | | |
| | | $table->add_row(array('id' => $rowid, 'class' => $class)); |
| | | |
| | | // format each col |
| | | foreach ($a_show_cols as $col) { |
| | | $table->add($col, self::Q(is_array($row_data[$col]) ? $row_data[$col][0] : $row_data[$col])); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return $table->show($attrib); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create an edit field for inclusion on a form |
| | | * |
| | | * @param string col field name |
| | | * @param string value field value |
| | | * @param array attrib HTML element attributes for field |
| | | * @param string type HTML element type (default 'text') |
| | | * |
| | | * @return string HTML field definition |
| | | */ |
| | | public static function get_edit_field($col, $value, $attrib, $type = 'text') |
| | | { |
| | | static $colcounts = array(); |
| | | |
| | | $fname = '_'.$col; |
| | | $attrib['name'] = $fname . ($attrib['array'] ? '[]' : ''); |
| | | $attrib['class'] = trim($attrib['class'] . ' ff_' . $col); |
| | | |
| | | if ($type == 'checkbox') { |
| | | $attrib['value'] = '1'; |
| | | $input = new html_checkbox($attrib); |
| | | } |
| | | else if ($type == 'textarea') { |
| | | $attrib['cols'] = $attrib['size']; |
| | | $input = new html_textarea($attrib); |
| | | } |
| | | else if ($type == 'select') { |
| | | $input = new html_select($attrib); |
| | | $input->add('---', ''); |
| | | $input->add(array_values($attrib['options']), array_keys($attrib['options'])); |
| | | } |
| | | else if ($attrib['type'] == 'password') { |
| | | $input = new html_passwordfield($attrib); |
| | | } |
| | | else { |
| | | if ($attrib['type'] != 'text' && $attrib['type'] != 'hidden') { |
| | | $attrib['type'] = 'text'; |
| | | } |
| | | $input = new html_inputfield($attrib); |
| | | } |
| | | |
| | | // use value from post |
| | | if (isset($_POST[$fname])) { |
| | | $postvalue = self::get_input_value($fname, self::INPUT_POST, true); |
| | | $value = $attrib['array'] ? $postvalue[intval($colcounts[$col]++)] : $postvalue; |
| | | } |
| | | |
| | | $out = $input->show($value); |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Replace all css definitions with #container [def] |
| | | * and remove css-inlined scripting |
| | | * |
| | | * @param string CSS source code |
| | | * @param string Container ID to use as prefix |
| | | * |
| | | * @return string Modified CSS source |
| | | * @todo I'm not sure this should belong to rcube_ui class |
| | | */ |
| | | public static function 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 |
| | | $source = self::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! */'; |
| | | } |
| | | |
| | | // cut out all contents between { and } |
| | | 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 |
| | | $regexp = '!url\s*\([ "\'](https?:)//[a-z0-9/._+-]+["\' ]\)!Uims'; |
| | | if (stripos($stripped, 'url(') && !preg_match($regexp, $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; |
| | | } |
| | | |
| | | // remove html comments and add #container to each tag selector. |
| | | // also replace body definition because we also stripped off the <body> tag |
| | | $styles = preg_replace( |
| | | array( |
| | | '/(^\s*<!--)|(-->\s*$)/', |
| | | '/(^\s*|,\s*|\}\s*)([a-z0-9\._#\*][a-z0-9\.\-_]*)/im', |
| | | '/'.preg_quote($container_id, '/').'\s+body/i', |
| | | ), |
| | | array( |
| | | '', |
| | | "\\1#$container_id \\2", |
| | | $container_id, |
| | | ), |
| | | $source); |
| | | |
| | | // put block contents back in |
| | | $styles = $replacements->resolve($styles); |
| | | |
| | | return $styles; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Convert the given date to a human readable form |
| | | * This uses the date formatting properties from config |
| | | * |
| | | * @param mixed Date representation (string, timestamp or DateTime object) |
| | | * @param string Date format to use |
| | | * @param bool Enables date convertion according to user timezone |
| | | * |
| | | * @return string Formatted date string |
| | | */ |
| | | public static function format_date($date, $format = null, $convert = true) |
| | | { |
| | | global $RCMAIL, $CONFIG; |
| | | |
| | | if (is_object($date) && is_a($date, 'DateTime')) { |
| | | $timestamp = $date->format('U'); |
| | | } |
| | | else { |
| | | if (!empty($date)) { |
| | | $timestamp = rcube_strtotime($date); |
| | | } |
| | | |
| | | if (empty($timestamp)) { |
| | | return ''; |
| | | } |
| | | |
| | | try { |
| | | $date = new DateTime("@".$timestamp); |
| | | } |
| | | catch (Exception $e) { |
| | | return ''; |
| | | } |
| | | } |
| | | |
| | | if ($convert) { |
| | | try { |
| | | // convert to the right timezone |
| | | $stz = date_default_timezone_get(); |
| | | $tz = new DateTimeZone($RCMAIL->config->get('timezone')); |
| | | $date->setTimezone($tz); |
| | | date_default_timezone_set($tz->getName()); |
| | | |
| | | $timestamp = $date->format('U'); |
| | | } |
| | | catch (Exception $e) { |
| | | } |
| | | } |
| | | |
| | | // define date format depending on current time |
| | | if (!$format) { |
| | | $now = 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']); |
| | | |
| | | if ($CONFIG['prettydate'] && $timestamp > $today_limit && $timestamp < $now) { |
| | | $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 = $RCMAIL->config->get('date_short', 'D H:i'); |
| | | } |
| | | else { |
| | | $format = $RCMAIL->config->get('date_long', 'Y-m-d H:i'); |
| | | } |
| | | } |
| | | |
| | | // strftime() format |
| | | if (preg_match('/%[a-z]+/i', $format)) { |
| | | $format = strftime($format, $timestamp); |
| | | if ($stz) { |
| | | date_default_timezone_set($stz); |
| | | } |
| | | return $today ? (self::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++) { |
| | | if ($format[$i] == "\\") { // skip escape chars |
| | | continue; |
| | | } |
| | | |
| | | // write char "as-is" |
| | | if ($format[$i] == ' ' || $format[$i-1] == "\\") { |
| | | $out .= $format[$i]; |
| | | } |
| | | // weekday (short) |
| | | else if ($format[$i] == 'D') { |
| | | $out .= self::label(strtolower(date('D', $timestamp))); |
| | | } |
| | | // weekday long |
| | | else if ($format[$i] == 'l') { |
| | | $out .= self::label(strtolower(date('l', $timestamp))); |
| | | } |
| | | // month name (short) |
| | | else if ($format[$i] == 'M') { |
| | | $out .= self::label(strtolower(date('M', $timestamp))); |
| | | } |
| | | // month name (long) |
| | | else if ($format[$i] == 'F') { |
| | | $out .= self::label('long'.strtolower(date('M', $timestamp))); |
| | | } |
| | | else if ($format[$i] == 'x') { |
| | | $out .= strftime('%x %X', $timestamp); |
| | | } |
| | | else { |
| | | $out .= date($format[$i], $timestamp); |
| | | } |
| | | } |
| | | |
| | | if ($today) { |
| | | $label = self::label('today'); |
| | | // replcae $ character with "Today" label (#1486120) |
| | | if (strpos($out, '$') !== false) { |
| | | $out = preg_replace('/\$/', $label, $out, 1); |
| | | } |
| | | else { |
| | | $out = $label . ' ' . $out; |
| | | } |
| | | } |
| | | |
| | | if ($stz) { |
| | | date_default_timezone_set($stz); |
| | | } |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return folders list in HTML |
| | | * |
| | | * @param array $attrib Named parameters |
| | | * |
| | | * @return string HTML code for the gui object |
| | | */ |
| | | public static function folder_list($attrib) |
| | | { |
| | | global $RCMAIL; |
| | | static $a_mailboxes; |
| | | |
| | | $attrib += array('maxlength' => 100, 'realnames' => false, 'unreadwrap' => ' (%s)'); |
| | | |
| | | // 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 current folder |
| | | $mbox_name = $RCMAIL->storage->get_folder(); |
| | | |
| | | // build the folders tree |
| | | if (empty($a_mailboxes)) { |
| | | // get mailbox list |
| | | $a_folders = $RCMAIL->storage->list_folders_subscribed( |
| | | '', $attrib['folder_name'], $attrib['folder_filter']); |
| | | $delimiter = $RCMAIL->storage->get_hierarchy_delimiter(); |
| | | $a_mailboxes = array(); |
| | | |
| | | foreach ($a_folders as $folder) { |
| | | self::build_folder_tree($a_mailboxes, $folder, $delimiter); |
| | | } |
| | | } |
| | | |
| | | // 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, |
| | | 'type' => $type, |
| | | 'attribs' => $attrib, |
| | | )); |
| | | |
| | | $a_mailboxes = $hook['list']; |
| | | $attrib = $hook['attribs']; |
| | | |
| | | if ($type == 'select') { |
| | | $select = new html_select($attrib); |
| | | |
| | | // add no-selection option |
| | | if ($attrib['noselection']) { |
| | | $select->add(self::label($attrib['noselection']), ''); |
| | | } |
| | | |
| | | self::render_folder_tree_select($a_mailboxes, $mbox_name, $attrib['maxlength'], $select, $attrib['realnames']); |
| | | $out = $select->show($attrib['default']); |
| | | } |
| | | else { |
| | | $js_mailboxlist = array(); |
| | | $out = html::tag('ul', $attrib, self::render_folder_tree_html($a_mailboxes, $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('unreadwrap', $attrib['unreadwrap']); |
| | | $RCMAIL->output->set_env('collapsed_folders', (string)$RCMAIL->config->get('collapsed_folders')); |
| | | } |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return folders list as html_select object |
| | | * |
| | | * @param array $p Named parameters |
| | | * |
| | | * @return html_select HTML drop-down object |
| | | */ |
| | | public static function folder_selector($p = array()) |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | $p += array('maxlength' => 100, 'realnames' => false); |
| | | $a_mailboxes = array(); |
| | | $storage = $RCMAIL->get_storage(); |
| | | |
| | | if (empty($p['folder_name'])) { |
| | | $p['folder_name'] = '*'; |
| | | } |
| | | |
| | | if ($p['unsubscribed']) { |
| | | $list = $storage->list_folders('', $p['folder_name'], $p['folder_filter'], $p['folder_rights']); |
| | | } |
| | | else { |
| | | $list = $storage->list_folders_subscribed('', $p['folder_name'], $p['folder_filter'], $p['folder_rights']); |
| | | } |
| | | |
| | | $delimiter = $storage->get_hierarchy_delimiter(); |
| | | |
| | | foreach ($list as $folder) { |
| | | if (empty($p['exceptions']) || !in_array($folder, $p['exceptions'])) { |
| | | self::build_folder_tree($a_mailboxes, $folder, $delimiter); |
| | | } |
| | | } |
| | | |
| | | $select = new html_select($p); |
| | | |
| | | if ($p['noselection']) { |
| | | $select->add($p['noselection'], ''); |
| | | } |
| | | |
| | | self::render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames'], 0, $p); |
| | | |
| | | return $select; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create a hierarchical array of the mailbox list |
| | | */ |
| | | private static function build_folder_tree(&$arrFolders, $folder, $delm = '/', $path = '') |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | // Handle namespace prefix |
| | | $prefix = ''; |
| | | if (!$path) { |
| | | $n_folder = $folder; |
| | | $folder = $RCMAIL->storage->mod_folder($folder); |
| | | |
| | | if ($n_folder != $folder) { |
| | | $prefix = substr($n_folder, 0, -strlen($folder)); |
| | | } |
| | | } |
| | | |
| | | $pos = strpos($folder, $delm); |
| | | |
| | | if ($pos !== false) { |
| | | $subFolders = substr($folder, $pos+1); |
| | | $currentFolder = substr($folder, 0, $pos); |
| | | |
| | | // sometimes folder has a delimiter as the last character |
| | | if (!strlen($subFolders)) { |
| | | $virtual = false; |
| | | } |
| | | else if (!isset($arrFolders[$currentFolder])) { |
| | | $virtual = true; |
| | | } |
| | | else { |
| | | $virtual = $arrFolders[$currentFolder]['virtual']; |
| | | } |
| | | } |
| | | else { |
| | | $subFolders = false; |
| | | $currentFolder = $folder; |
| | | $virtual = false; |
| | | } |
| | | |
| | | $path .= $prefix . $currentFolder; |
| | | |
| | | if (!isset($arrFolders[$currentFolder])) { |
| | | $arrFolders[$currentFolder] = array( |
| | | 'id' => $path, |
| | | 'name' => rcube_charset::convert($currentFolder, 'UTF7-IMAP'), |
| | | 'virtual' => $virtual, |
| | | 'folders' => array()); |
| | | } |
| | | else { |
| | | $arrFolders[$currentFolder]['virtual'] = $virtual; |
| | | } |
| | | |
| | | if (strlen($subFolders)) { |
| | | self::build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return html for a structured list <ul> for the mailbox tree |
| | | */ |
| | | private static function render_folder_tree_html(&$arrFolders, &$mbox_name, &$jslist, $attrib, $nestLevel = 0) |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | $maxlength = intval($attrib['maxlength']); |
| | | $realnames = (bool)$attrib['realnames']; |
| | | $msgcounts = $RCMAIL->storage->get_cache('messagecount'); |
| | | $collapsed = $RCMAIL->config->get('collapsed_folders'); |
| | | |
| | | $out = ''; |
| | | foreach ($arrFolders as $key => $folder) { |
| | | $title = null; |
| | | $folder_class = self::folder_classname($folder['id']); |
| | | $collapsed = strpos($collapsed, '&'.rawurlencode($folder['id']).'&') !== false; |
| | | $unread = $msgcounts ? intval($msgcounts[$folder['id']]['UNSEEN']) : 0; |
| | | |
| | | if ($folder_class && !$realnames) { |
| | | $foldername = $RCMAIL->gettext($folder_class); |
| | | } |
| | | else { |
| | | $foldername = $folder['name']; |
| | | |
| | | // shorten the folder name to a given length |
| | | if ($maxlength && $maxlength > 1) { |
| | | $fname = abbreviate_string($foldername, $maxlength); |
| | | if ($fname != $foldername) { |
| | | $title = $foldername; |
| | | } |
| | | $foldername = $fname; |
| | | } |
| | | } |
| | | |
| | | // make folder name safe for ids and class names |
| | | $folder_id = self::html_identifier($folder['id'], true); |
| | | $classes = array('mailbox'); |
| | | |
| | | // set special class for Sent, Drafts, Trash and Junk |
| | | if ($folder_class) { |
| | | $classes[] = $folder_class; |
| | | } |
| | | |
| | | if ($folder['id'] == $mbox_name) { |
| | | $classes[] = 'selected'; |
| | | } |
| | | |
| | | if ($folder['virtual']) { |
| | | $classes[] = 'virtual'; |
| | | } |
| | | else if ($unread) { |
| | | $classes[] = 'unread'; |
| | | } |
| | | |
| | | $js_name = self::JQ($folder['id']); |
| | | $html_name = self::Q($foldername) . ($unread ? html::span('unreadcount', sprintf($attrib['unreadwrap'], $unread)) : ''); |
| | | $link_attrib = $folder['virtual'] ? array() : array( |
| | | 'href' => self::url('', array('_mbox' => $folder['id'])), |
| | | 'onclick' => sprintf("return %s.command('list','%s',this)", JS_OBJECT_NAME, $js_name), |
| | | 'rel' => $folder['id'], |
| | | 'title' => $title, |
| | | ); |
| | | |
| | | $out .= html::tag('li', array( |
| | | 'id' => "rcmli".$folder_id, |
| | | 'class' => join(' ', $classes), |
| | | 'noclose' => true), |
| | | html::a($link_attrib, $html_name) . |
| | | (!empty($folder['folders']) ? html::div(array( |
| | | 'class' => ($collapsed ? 'collapsed' : 'expanded'), |
| | | '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)), |
| | | self::render_folder_tree_html($folder['folders'], $mbox_name, $jslist, $attrib, $nestLevel+1)); |
| | | } |
| | | |
| | | $out .= "</li>\n"; |
| | | } |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return html for a flat list <select> for the mailbox tree |
| | | */ |
| | | private static function render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, &$select, $realnames = false, $nestLevel = 0, $opts = array()) |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | $out = ''; |
| | | |
| | | 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->storage->folder_attributes($folder['id']); |
| | | if ($attrs && in_array('\\Noinferiors', $attrs)) { |
| | | continue; |
| | | } |
| | | } |
| | | |
| | | if (!$realnames && ($folder_class = self::folder_classname($folder['id']))) { |
| | | $foldername = self::label($folder_class); |
| | | } |
| | | 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 .= self::render_folder_tree_select($folder['folders'], $mbox_name, $maxlength, |
| | | $select, $realnames, $nestLevel+1, $opts); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return internal name for the given folder if it matches the configured special folders |
| | | */ |
| | | private static function folder_classname($folder_id) |
| | | { |
| | | global $CONFIG; |
| | | |
| | | if ($folder_id == 'INBOX') { |
| | | return 'inbox'; |
| | | } |
| | | |
| | | // for these mailboxes we have localized labels and css classes |
| | | foreach (array('sent', 'drafts', 'trash', 'junk') as $smbx) |
| | | { |
| | | if ($folder_id == $CONFIG[$smbx.'_mbox']) { |
| | | return $smbx; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Try to localize the given IMAP folder name. |
| | | * UTF-7 decode it in case no localized text was found |
| | | * |
| | | * @param string $name Folder name |
| | | * |
| | | * @return string Localized folder name in UTF-8 encoding |
| | | */ |
| | | public static function localize_foldername($name) |
| | | { |
| | | if ($folder_class = self::folder_classname($name)) { |
| | | return self::label($folder_class); |
| | | } |
| | | else { |
| | | return rcube_charset::convert($name, 'UTF7-IMAP'); |
| | | } |
| | | } |
| | | |
| | | |
| | | public static function localize_folderpath($path) |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | $protect_folders = $RCMAIL->config->get('protect_default_folders'); |
| | | $default_folders = (array) $RCMAIL->config->get('default_folders'); |
| | | $delimiter = $RCMAIL->storage->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[] = self::localize_foldername($directory); |
| | | } |
| | | else { |
| | | $result[] = rcube_charset::convert($dir, 'UTF7-IMAP'); |
| | | } |
| | | } |
| | | |
| | | return implode($delimiter, $result); |
| | | } |
| | | |
| | | |
| | | public static function quota_display($attrib) |
| | | { |
| | | global $OUTPUT; |
| | | |
| | | if (!$attrib['id']) { |
| | | $attrib['id'] = 'rcmquotadisplay'; |
| | | } |
| | | |
| | | $_SESSION['quota_display'] = !empty($attrib['display']) ? $attrib['display'] : 'text'; |
| | | |
| | | $OUTPUT->add_gui_object('quotadisplay', $attrib['id']); |
| | | |
| | | $quota = self::quota_content($attrib); |
| | | |
| | | $OUTPUT->add_script('rcmail.set_quota('.rcube_output::json_serialize($quota).');', 'docready'); |
| | | |
| | | return html::span($attrib, ''); |
| | | } |
| | | |
| | | |
| | | public static function quota_content($attrib = null) |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | $quota = $RCMAIL->storage->get_quota(); |
| | | $quota = $RCMAIL->plugins->exec_hook('quota', $quota); |
| | | |
| | | $quota_result = (array) $quota; |
| | | $quota_result['type'] = isset($_SESSION['quota_display']) ? $_SESSION['quota_display'] : ''; |
| | | |
| | | if (!$quota['total'] && $RCMAIL->config->get('quota_zero_as_unlimited')) { |
| | | $quota_result['title'] = self::label('unlimited'); |
| | | $quota_result['percent'] = 0; |
| | | } |
| | | else if ($quota['total']) { |
| | | if (!isset($quota['percent'])) { |
| | | $quota_result['percent'] = min(100, round(($quota['used']/max(1,$quota['total']))*100)); |
| | | } |
| | | |
| | | $title = sprintf('%s / %s (%.0f%%)', |
| | | self::show_bytes($quota['used'] * 1024), self::show_bytes($quota['total'] * 1024), |
| | | $quota_result['percent']); |
| | | |
| | | $quota_result['title'] = $title; |
| | | |
| | | if ($attrib['width']) { |
| | | $quota_result['width'] = $attrib['width']; |
| | | } |
| | | if ($attrib['height']) { |
| | | $quota_result['height'] = $attrib['height']; |
| | | } |
| | | } |
| | | else { |
| | | $quota_result['title'] = self::label('unknown'); |
| | | $quota_result['percent'] = 0; |
| | | } |
| | | |
| | | return $quota_result; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Outputs error message according to server error/response codes |
| | | * |
| | | * @param string $fallback Fallback message label |
| | | * @param array $fallback_args Fallback message label arguments |
| | | */ |
| | | public static function display_server_error($fallback = null, $fallback_args = null) |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | $err_code = $RCMAIL->storage->get_error_code(); |
| | | $res_code = $RCMAIL->storage->get_response_code(); |
| | | |
| | | if ($err_code < 0) { |
| | | $RCMAIL->output->show_message('storageerror', 'error'); |
| | | } |
| | | else if ($res_code == rcube_storage::NOPERM) { |
| | | $RCMAIL->output->show_message('errornoperm', 'error'); |
| | | } |
| | | else if ($res_code == rcube_storage::READONLY) { |
| | | $RCMAIL->output->show_message('errorreadonly', 'error'); |
| | | } |
| | | else if ($err_code && ($err_str = $RCMAIL->storage->get_error_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); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Generate CSS classes from mimetype and filename extension |
| | | * |
| | | * @param string $mimetype Mimetype |
| | | * @param string $filename Filename |
| | | * |
| | | * @return string CSS classes separated by space |
| | | */ |
| | | public static function file2class($mimetype, $filename) |
| | | { |
| | | list($primary, $secondary) = explode('/', $mimetype); |
| | | |
| | | $classes = array($primary ? $primary : 'unknown'); |
| | | if ($secondary) { |
| | | $classes[] = $secondary; |
| | | } |
| | | if (preg_match('/\.([a-z0-9]+)$/i', $filename, $m)) { |
| | | $classes[] = $m[1]; |
| | | } |
| | | |
| | | return strtolower(join(" ", $classes)); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Output HTML editor scripts |
| | | * |
| | | * @param string $mode Editor mode |
| | | */ |
| | | public static function html_editor($mode = '') |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | $hook = $RCMAIL->plugins->exec_hook('html_editor', array('mode' => $mode)); |
| | | |
| | | if ($hook['abort']) { |
| | | return; |
| | | } |
| | | |
| | | $lang = strtolower($_SESSION['language']); |
| | | |
| | | // TinyMCE uses two-letter lang codes, with exception of Chinese |
| | | if (strpos($lang, 'zh_') === 0) { |
| | | $lang = str_replace('_', '-', $lang); |
| | | } |
| | | else { |
| | | $lang = substr($lang, 0, 2); |
| | | } |
| | | |
| | | if (!file_exists(INSTALL_PATH . 'program/js/tiny_mce/langs/'.$lang.'.js')) { |
| | | $lang = 'en'; |
| | | } |
| | | |
| | | $script = json_encode(array( |
| | | 'mode' => $mode, |
| | | 'lang' => $lang, |
| | | 'skin_path' => $RCMAIL->output->get_skin_path(), |
| | | 'spellcheck' => intval($RCMAIL->config->get('enable_spellcheck')), |
| | | 'spelldict' => intval($RCMAIL->config->get('spellcheck_dictionary')) |
| | | )); |
| | | |
| | | $RCMAIL->output->include_script('tiny_mce/tiny_mce.js'); |
| | | $RCMAIL->output->include_script('editor.js'); |
| | | $RCMAIL->output->add_script("rcmail_editor_init($script)", 'docready'); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Replaces TinyMCE's emoticon images with plain-text representation |
| | | * |
| | | * @param string $html HTML content |
| | | * |
| | | * @return string HTML content |
| | | */ |
| | | public static function replace_emoticons($html) |
| | | { |
| | | $emoticons = array( |
| | | '8-)' => 'smiley-cool', |
| | | ':-#' => 'smiley-foot-in-mouth', |
| | | ':-*' => 'smiley-kiss', |
| | | ':-X' => 'smiley-sealed', |
| | | ':-P' => 'smiley-tongue-out', |
| | | ':-@' => 'smiley-yell', |
| | | ":'(" => 'smiley-cry', |
| | | ':-(' => 'smiley-frown', |
| | | ':-D' => 'smiley-laughing', |
| | | ':-)' => 'smiley-smile', |
| | | ':-S' => 'smiley-undecided', |
| | | ':-$' => 'smiley-embarassed', |
| | | 'O:-)' => 'smiley-innocent', |
| | | ':-|' => 'smiley-money-mouth', |
| | | ':-O' => 'smiley-surprised', |
| | | ';-)' => 'smiley-wink', |
| | | ); |
| | | |
| | | foreach ($emoticons as $idx => $file) { |
| | | // <img title="Cry" src="http://.../program/js/tiny_mce/plugins/emotions/img/smiley-cry.gif" border="0" alt="Cry" /> |
| | | $search[] = '/<img title="[a-z ]+" src="https?:\/\/[a-z0-9_.\/-]+\/tiny_mce\/plugins\/emotions\/img\/'.$file.'.gif"[^>]+\/>/i'; |
| | | $replace[] = $idx; |
| | | } |
| | | |
| | | return preg_replace($search, $replace, $html); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * File upload progress handler. |
| | | */ |
| | | public static function upload_progress() |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | $prefix = ini_get('apc.rfc1867_prefix'); |
| | | $params = array( |
| | | 'action' => $RCMAIL->action, |
| | | 'name' => self::get_input_value('_progress', self::INPUT_GET), |
| | | ); |
| | | |
| | | if (function_exists('apc_fetch')) { |
| | | $status = apc_fetch($prefix . $params['name']); |
| | | |
| | | if (!empty($status)) { |
| | | $status['percent'] = round($status['current']/$status['total']*100); |
| | | $params = array_merge($status, $params); |
| | | } |
| | | } |
| | | |
| | | if (isset($params['percent'])) |
| | | $params['text'] = self::label(array('name' => 'uploadprogress', 'vars' => array( |
| | | 'percent' => $params['percent'] . '%', |
| | | 'current' => self::show_bytes($params['current']), |
| | | 'total' => self::show_bytes($params['total']) |
| | | ))); |
| | | |
| | | $RCMAIL->output->command('upload_progress_update', $params); |
| | | $RCMAIL->output->send(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initializes file uploading interface. |
| | | */ |
| | | public static function upload_init() |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | // Enable upload progress bar |
| | | if (($seconds = $RCMAIL->config->get('upload_progress')) && ini_get('apc.rfc1867')) { |
| | | if ($field_name = ini_get('apc.rfc1867_name')) { |
| | | $RCMAIL->output->set_env('upload_progress_name', $field_name); |
| | | $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 = self::show_bytes($max_filesize); |
| | | $RCMAIL->output->set_env('filesizeerror', self::label(array( |
| | | 'name' => 'filesizeerror', 'vars' => array('size' => $max_filesize)))); |
| | | |
| | | return $max_filesize; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initializes client-side autocompletion. |
| | | */ |
| | | public static function 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'); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns supported font-family specifications |
| | | * |
| | | * @param string $font Font name |
| | | * |
| | | * @param string|array Font-family specification array or string (if $font is used) |
| | | */ |
| | | public static function font_defs($font = null) |
| | | { |
| | | $fonts = array( |
| | | 'Andale Mono' => '"Andale Mono",Times,monospace', |
| | | 'Arial' => 'Arial,Helvetica,sans-serif', |
| | | 'Arial Black' => '"Arial Black","Avant Garde",sans-serif', |
| | | 'Book Antiqua' => '"Book Antiqua",Palatino,serif', |
| | | 'Courier New' => '"Courier New",Courier,monospace', |
| | | 'Georgia' => 'Georgia,Palatino,serif', |
| | | 'Helvetica' => 'Helvetica,Arial,sans-serif', |
| | | 'Impact' => 'Impact,Chicago,sans-serif', |
| | | 'Tahoma' => 'Tahoma,Arial,Helvetica,sans-serif', |
| | | 'Terminal' => 'Terminal,Monaco,monospace', |
| | | 'Times New Roman' => '"Times New Roman",Times,serif', |
| | | 'Trebuchet MS' => '"Trebuchet MS",Geneva,sans-serif', |
| | | 'Verdana' => 'Verdana,Geneva,sans-serif', |
| | | ); |
| | | |
| | | if ($font) { |
| | | return $fonts[$font]; |
| | | } |
| | | |
| | | return $fonts; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create a human readable string for a number of bytes |
| | | * |
| | | * @param int Number of bytes |
| | | * |
| | | * @return string Byte string |
| | | */ |
| | | public static function show_bytes($bytes) |
| | | { |
| | | if ($bytes >= 1073741824) { |
| | | $gb = $bytes/1073741824; |
| | | $str = sprintf($gb>=10 ? "%d " : "%.1f ", $gb) . self::label('GB'); |
| | | } |
| | | else if ($bytes >= 1048576) { |
| | | $mb = $bytes/1048576; |
| | | $str = sprintf($mb>=10 ? "%d " : "%.1f ", $mb) . self::label('MB'); |
| | | } |
| | | else if ($bytes >= 1024) { |
| | | $str = sprintf("%d ", round($bytes/1024)) . self::label('KB'); |
| | | } |
| | | else { |
| | | $str = sprintf('%d ', $bytes) . self::label('B'); |
| | | } |
| | | |
| | | return $str; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Decode escaped entities used by known XSS exploits. |
| | | * See http://downloads.securityfocus.com/vulnerabilities/exploits/26800.eml for examples |
| | | * |
| | | * @param string CSS content to decode |
| | | * |
| | | * @return string Decoded string |
| | | * @todo I'm not sure this should belong to rcube_ui class |
| | | */ |
| | | public static function xss_entity_decode($content) |
| | | { |
| | | $out = html_entity_decode(html_entity_decode($content)); |
| | | $out = preg_replace_callback('/\\\([0-9a-f]{4})/i', |
| | | array(self, 'xss_entity_decode_callback'), $out); |
| | | $out = preg_replace('#/\*.*\*/#Ums', '', $out); |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * preg_replace_callback callback for xss_entity_decode |
| | | * |
| | | * @param array $matches Result from preg_replace_callback |
| | | * |
| | | * @return string Decoded entity |
| | | */ |
| | | public static function xss_entity_decode_callback($matches) |
| | | { |
| | | return chr(hexdec($matches[1])); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Check if we can process not exceeding memory_limit |
| | | * |
| | | * @param integer Required amount of memory |
| | | * |
| | | * @return boolean True if memory won't be exceeded, False otherwise |
| | | */ |
| | | public static function mem_check($need) |
| | | { |
| | | $mem_limit = parse_bytes(ini_get('memory_limit')); |
| | | $memory = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB |
| | | |
| | | return $mem_limit > 0 && $memory + $need > $mem_limit ? false : true; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Check if working in SSL mode |
| | | * |
| | | * @param integer $port HTTPS port number |
| | | * @param boolean $use_https Enables 'use_https' option checking |
| | | * |
| | | * @return boolean |
| | | */ |
| | | public static function https_check($port=null, $use_https=true) |
| | | { |
| | | global $RCMAIL; |
| | | |
| | | if (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) != 'off') { |
| | | return true; |
| | | } |
| | | if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https') { |
| | | return true; |
| | | } |
| | | if ($port && $_SERVER['SERVER_PORT'] == $port) { |
| | | return true; |
| | | } |
| | | if ($use_https && isset($RCMAIL) && $RCMAIL->config->get('use_https')) { |
| | | return true; |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | if ($id && !$sql_arr) { |
| | | $sql_result = $this->db->query( |
| | | "SELECT * FROM ".get_table_name('users')." WHERE user_id = ?", $id); |
| | | "SELECT * FROM ".$this->db->table_name('users')." WHERE user_id = ?", $id); |
| | | $sql_arr = $this->db->fetch_assoc($sql_result); |
| | | } |
| | | |
| | |
| | | if (!empty($_SESSION['preferences'])) { |
| | | // Check last write attempt time, try to write again (every 5 minutes) |
| | | if ($_SESSION['preferences_time'] < time() - 5 * 60) { |
| | | $saved_prefs = unserialize($_SESSION['preferences']); |
| | | $saved_prefs = unserialize($_SESSION['preferences']); |
| | | $this->rc->session->remove('preferences'); |
| | | $this->rc->session->remove('preferences_time'); |
| | | $this->rc->session->remove('preferences_time'); |
| | | $this->save_prefs($saved_prefs); |
| | | } |
| | | else { |
| | |
| | | $save_prefs = serialize($save_prefs); |
| | | |
| | | $this->db->query( |
| | | "UPDATE ".get_table_name('users'). |
| | | "UPDATE ".$this->db->table_name('users'). |
| | | " SET preferences = ?". |
| | | ", language = ?". |
| | | " WHERE user_id = ?", |
| | |
| | | $result = array(); |
| | | |
| | | $sql_result = $this->db->query( |
| | | "SELECT * FROM ".get_table_name('identities'). |
| | | "SELECT * FROM ".$this->db->table_name('identities'). |
| | | " WHERE del <> 1 AND user_id = ?". |
| | | ($sql_add ? " ".$sql_add : ""). |
| | | " ORDER BY ".$this->db->quoteIdentifier('standard')." DESC, name ASC, identity_id ASC", |
| | |
| | | $query_params[] = $iid; |
| | | $query_params[] = $this->ID; |
| | | |
| | | $sql = "UPDATE ".get_table_name('identities'). |
| | | $sql = "UPDATE ".$this->db->table_name('identities'). |
| | | " SET changed = ".$this->db->now().", ".join(', ', $query_cols). |
| | | " WHERE identity_id = ?". |
| | | " AND user_id = ?". |
| | |
| | | $insert_cols[] = 'user_id'; |
| | | $insert_values[] = $this->ID; |
| | | |
| | | $sql = "INSERT INTO ".get_table_name('identities'). |
| | | $sql = "INSERT INTO ".$this->db->table_name('identities'). |
| | | " (changed, ".join(', ', $insert_cols).")". |
| | | " VALUES (".$this->db->now().", ".join(', ', array_pad(array(), sizeof($insert_values), '?')).")"; |
| | | |
| | |
| | | return false; |
| | | |
| | | $sql_result = $this->db->query( |
| | | "SELECT count(*) AS ident_count FROM ".get_table_name('identities'). |
| | | "SELECT count(*) AS ident_count FROM ".$this->db->table_name('identities'). |
| | | " WHERE user_id = ? AND del <> 1", |
| | | $this->ID); |
| | | |
| | |
| | | return -1; |
| | | |
| | | $this->db->query( |
| | | "UPDATE ".get_table_name('identities'). |
| | | "UPDATE ".$this->db->table_name('identities'). |
| | | " SET del = 1, changed = ".$this->db->now(). |
| | | " WHERE user_id = ?". |
| | | " AND identity_id = ?", |
| | |
| | | { |
| | | if ($this->ID && $iid) { |
| | | $this->db->query( |
| | | "UPDATE ".get_table_name('identities'). |
| | | "UPDATE ".$this->db->table_name('identities'). |
| | | " SET ".$this->db->quoteIdentifier('standard')." = '0'". |
| | | " WHERE user_id = ?". |
| | | " AND identity_id <> ?". |
| | |
| | | { |
| | | if ($this->ID) { |
| | | $this->db->query( |
| | | "UPDATE ".get_table_name('users'). |
| | | "UPDATE ".$this->db->table_name('users'). |
| | | " SET last_login = ".$this->db->now(). |
| | | " WHERE user_id = ?", |
| | | $this->ID); |
| | |
| | | $dbh = rcmail::get_instance()->get_dbh(); |
| | | |
| | | // query for matching user name |
| | | $query = "SELECT * FROM ".get_table_name('users')." WHERE mail_host = ? AND %s = ?"; |
| | | $query = "SELECT * FROM ".$dbh->table_name('users')." WHERE mail_host = ? AND %s = ?"; |
| | | $sql_result = $dbh->query(sprintf($query, 'username'), $host, $user); |
| | | |
| | | // query for matching alias |
| | |
| | | $dbh = $rcmail->get_dbh(); |
| | | |
| | | $dbh->query( |
| | | "INSERT INTO ".get_table_name('users'). |
| | | "INSERT INTO ".$dbh->table_name('users'). |
| | | " (created, last_login, username, mail_host, alias, language)". |
| | | " VALUES (".$dbh->now().", ".$dbh->now().", ?, ?, ?, ?)", |
| | | strip_newlines($user), |
| | |
| | | } |
| | | } |
| | | else { |
| | | raise_error(array( |
| | | rcube::raise_error(array( |
| | | 'code' => 500, |
| | | 'type' => 'php', |
| | | 'line' => __LINE__, |
| | |
| | | |
| | | $sql_result = $this->db->query( |
| | | "SELECT search_id AS id, ".$this->db->quoteIdentifier('name') |
| | | ." FROM ".get_table_name('searches') |
| | | ." FROM ".$this->db->table_name('searches') |
| | | ." WHERE user_id = ?" |
| | | ." AND ".$this->db->quoteIdentifier('type')." = ?" |
| | | ." ORDER BY ".$this->db->quoteIdentifier('name'), |
| | |
| | | "SELECT ".$this->db->quoteIdentifier('name') |
| | | .", ".$this->db->quoteIdentifier('data') |
| | | .", ".$this->db->quoteIdentifier('type') |
| | | ." FROM ".get_table_name('searches') |
| | | ." FROM ".$this->db->table_name('searches') |
| | | ." WHERE user_id = ?" |
| | | ." AND search_id = ?", |
| | | (int) $this->ID, (int) $id); |
| | |
| | | return false; |
| | | |
| | | $this->db->query( |
| | | "DELETE FROM ".get_table_name('searches') |
| | | "DELETE FROM ".$this->db->table_name('searches') |
| | | ." WHERE user_id = ?" |
| | | ." AND search_id = ?", |
| | | (int) $this->ID, $sid); |
| | |
| | | $insert_cols[] = $this->db->quoteIdentifier('data'); |
| | | $insert_values[] = serialize($data['data']); |
| | | |
| | | $sql = "INSERT INTO ".get_table_name('searches') |
| | | $sql = "INSERT INTO ".$this->db->table_name('searches') |
| | | ." (".join(', ', $insert_cols).")" |
| | | ." VALUES (".join(', ', array_pad(array(), sizeof($insert_values), '?')).")"; |
| | | |
| | |
| | | if (is_array($subnode) && (($charset = $force_charset) || ($subnode['charset'] && ($charset = $subnode['charset'][0])))) { |
| | | foreach ($subnode as $j => $value) { |
| | | if (is_numeric($j) && is_string($value)) |
| | | $card[$key][$i][$j] = rcube_charset_convert($value, $charset); |
| | | $card[$key][$i][$j] = rcube_charset::convert($value, $charset); |
| | | } |
| | | unset($card[$key][$i]['charset']); |
| | | } |
| | |
| | | $charset = null; |
| | | // detect charset and convert to utf-8 |
| | | else if (($charset = self::detect_encoding($data)) && $charset != RCMAIL_CHARSET) { |
| | | $data = rcube_charset_convert($data, $charset); |
| | | $data = rcube_charset::convert($data, $charset); |
| | | $data = preg_replace(array('/^[\xFE\xFF]{2}/', '/^\xEF\xBB\xBF/', '/^\x00+/'), '', $data); // also remove BOM |
| | | $charset = RCMAIL_CHARSET; |
| | | } |
| | |
| | | )*\z/xs', substr($string, 0, 2048))) |
| | | return 'UTF-8'; |
| | | |
| | | return rcmail::get_instance()->config->get('default_charset', 'ISO-8859-1'); # fallback to Latin-1 |
| | | return rcube::get_instance()->config->get('default_charset', 'ISO-8859-1'); # fallback to Latin-1 |
| | | } |
| | | |
| | | } |
| | |
| | | 'rel' => '%s', |
| | | 'onclick' => "return ".JS_OBJECT_NAME.".command('list','%s',this)"), '%s')); |
| | | |
| | | $sources = (array) $OUTPUT->env['address_sources']; |
| | | $sources = (array) $OUTPUT->get_env('address_sources'); |
| | | reset($sources); |
| | | |
| | | // currently selected source |
| | |
| | | $data = $message->get_part_content($pid); |
| | | } |
| | | |
| | | $mimetype = $part->ctype_primary . '/' . $part->ctype_secondary; |
| | | $filename = $part->filename; |
| | | if (!strlen($filename)) { |
| | | if ($mimetype == 'text/html') { |
| | | $filename = rcube_label('htmlmessage'); |
| | | } |
| | | else { |
| | | $filename = 'Part_'.$pid; |
| | | } |
| | | $filename .= '.' . $part->ctype_secondary; |
| | | } |
| | | |
| | | $attachment = array( |
| | | 'group' => $COMPOSE['id'], |
| | | 'name' => $part->filename ? $part->filename : 'Part_'.$pid.'.'.$part->ctype_secondary, |
| | | 'mimetype' => $part->ctype_primary . '/' . $part->ctype_secondary, |
| | | 'name' => $filename, |
| | | 'mimetype' => $mimetype, |
| | | 'content_id' => $part->content_id, |
| | | 'data' => $data, |
| | | 'path' => $path, |
| | |
| | | // Make sure there are no duplicated columns (#1486999) |
| | | $a_show_cols = array_unique($a_show_cols); |
| | | |
| | | // Plugins may set header's list_cols/list_flags and other rcube_mail_header variables |
| | | // Plugins may set header's list_cols/list_flags and other rcube_message_header variables |
| | | // and list columns |
| | | $plugin = $RCMAIL->plugins->exec_hook('messages_list', |
| | | array('messages' => $a_headers, 'cols' => $a_show_cols)); |
| | |
| | | foreach ($MESSAGE->parts as $i => $part) { |
| | | if ($part->type == 'headers') |
| | | $out .= rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : NULL, $part->headers); |
| | | else if ($part->type == 'content' && $part->size) { |
| | | else if ($part->type == 'content') { |
| | | // unsapported |
| | | if ($part->realtype) { |
| | | if ($part->realtype == 'multipart/encrypted') { |
| | | $out .= html::span('part-notice', rcube_label('encryptedmessage')); |
| | | } |
| | | continue; |
| | | } |
| | | else if (!$part->size) { |
| | | continue; |
| | | } |
| | | // Check if we have enough memory to handle the message in it |
| | | // #1487424: we need up to 10x more memory than the body |
| | | if (!rcmail_mem_check($part->size * 10)) { |
| | | else if (!rcmail_mem_check($part->size * 10)) { |
| | | $out .= html::span('part-notice', rcube_label('messagetoobig'). ' ' |
| | | . html::a('?_task=mail&_action=get&_download=1&_uid='.$MESSAGE->uid.'&_part='.$part->mime_id |
| | | .'&_mbox='. urlencode($RCMAIL->storage->get_folder()), rcube_label('download'))); |
| | |
| | | $part = $MESSAGE->mime_parts[$part]; |
| | | $table = new html_table(array('cols' => 3)); |
| | | |
| | | if (!empty($part->filename)) { |
| | | $filename = $part->filename; |
| | | if (empty($filename) && $attach_prop->mimetype == 'text/html') { |
| | | $filename = rcube_label('htmlmessage'); |
| | | } |
| | | |
| | | if (!empty($filename)) { |
| | | $table->add('title', Q(rcube_label('filename'))); |
| | | $table->add('header', Q($part->filename)); |
| | | $table->add('header', Q($filename)); |
| | | $table->add('download-link', html::a(array('href' => './?'.str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING'])), Q(rcube_label('download')))); |
| | | } |
| | | |
| | |
| | | |
| | | // show part page |
| | | if (!empty($_GET['_frame'])) { |
| | | if (($part_id = get_input_value('_part', RCUBE_INPUT_GPC)) && ($part = $MESSAGE->mime_parts[$part_id]) && $part->filename) |
| | | $OUTPUT->set_pagetitle($part->filename); |
| | | if (($part_id = get_input_value('_part', RCUBE_INPUT_GPC)) && ($part = $MESSAGE->mime_parts[$part_id])) { |
| | | $filename = $part->filename; |
| | | if (empty($filename) && $part->mimetype == 'text/html') { |
| | | $filename = rcube_label('htmlmessage'); |
| | | } |
| | | if (!empty($filename)) { |
| | | $OUTPUT->set_pagetitle($filename); |
| | | } |
| | | } |
| | | |
| | | $OUTPUT->send('messagepart'); |
| | | exit; |
| | |
| | | $out = rcmail_print_body($part, array('safe' => $MESSAGE->is_safe, 'inline_html' => false)); |
| | | } |
| | | |
| | | $OUTPUT = new rcube_html_page(); |
| | | $OUTPUT = new rcube_output_html(); |
| | | $OUTPUT->write($out); |
| | | } |
| | | else { |
| | | // don't kill the connection if download takes more than 30 sec. |
| | | @set_time_limit(0); |
| | | |
| | | if ($part->filename) { |
| | | $filename = $part->filename; |
| | | } |
| | | else if ($part->mimetype == 'text/html') { |
| | | $filename = rcube_label('htmlmessage'); |
| | | } |
| | | else { |
| | | $filename = ($MESSAGE->subject ? $MESSAGE->subject : 'roundcube'); |
| | | } |
| | | |
| | | $ext = '.' . ($mimetype == 'text/plain' ? 'txt' : $ctype_secondary); |
| | | $filename = $part->filename ? $part->filename : ($MESSAGE->subject ? $MESSAGE->subject : 'roundcube') . $ext; |
| | | $filename .= $ext; |
| | | $filename = preg_replace('[\r\n]', '', $filename); |
| | | |
| | | if ($browser->ie && $browser->ver < 7) |
| | |
| | | |
| | | if (sizeof($MESSAGE->attachments)) { |
| | | foreach ($MESSAGE->attachments as $attach_prop) { |
| | | if ($PRINT_MODE) { |
| | | $ol .= html::tag('li', null, sprintf("%s (%s)", Q($attach_prop->filename), Q(show_bytes($attach_prop->size)))); |
| | | } |
| | | else { |
| | | if (mb_strlen($attach_prop->filename) > 50) { |
| | | $filename = abbreviate_string($attach_prop->filename, 50); |
| | | $title = $attach_prop->filename; |
| | | } |
| | | else { |
| | | $filename = $attach_prop->filename; |
| | | $title = ''; |
| | | $filename = $attach_prop->filename; |
| | | if (empty($filename) && $attach_prop->mimetype == 'text/html') { |
| | | $filename = rcube_label('htmlmessage'); |
| | | } |
| | | |
| | | $ol .= html::tag('li', rcmail_filetype2classname($attach_prop->mimetype, $attach_prop->filename), |
| | | if ($PRINT_MODE) { |
| | | $ol .= html::tag('li', null, sprintf("%s (%s)", Q($filename), Q(show_bytes($attach_prop->size)))); |
| | | } |
| | | else { |
| | | if (mb_strlen($filename) > 50) { |
| | | $filename = abbreviate_string($filename, 50); |
| | | $title = $filename; |
| | | } |
| | | else { |
| | | $title = ''; |
| | | } |
| | | |
| | | $ol .= html::tag('li', rcmail_filetype2classname($attach_prop->mimetype, $filename), |
| | | html::a(array( |
| | | 'href' => $MESSAGE->get_part_url($attach_prop->mime_id, false), |
| | | 'onclick' => sprintf( |
| | |
| | | |
| | | */ |
| | | |
| | | $rcmail = rcmail::get_instance(); |
| | | |
| | | // browser is not compatible with this application |
| | | if ($ERROR_CODE==409) { |
| | |
| | | $__error_title = "SERVICE CURRENTLY NOT AVAILABLE!"; |
| | | $__error_text = "Please contact your server-administrator."; |
| | | |
| | | if (($CONFIG['debug_level'] & 4) && $ERROR_MESSAGE) |
| | | if (($rcmail->config->get('debug_level') & 4) && $ERROR_MESSAGE) |
| | | $__error_text = $ERROR_MESSAGE; |
| | | else |
| | | $__error_text = sprintf('Error No. [%s]', $ERROR_CODE); |
| | |
| | | $HTTP_ERR_CODE = $ERROR_CODE && $ERROR_CODE < 600 ? $ERROR_CODE : 500; |
| | | |
| | | // Ajax request |
| | | if ($OUTPUT && ($OUTPUT instanceof rcube_json_output)) { |
| | | if ($rcmail->output && $rcmail->output->type == 'js') { |
| | | header("HTTP/1.0 $HTTP_ERR_CODE $__error_title"); |
| | | die; |
| | | } |
| | |
| | | </div> |
| | | EOF; |
| | | |
| | | if ($OUTPUT && $OUTPUT->template_exists('error')) { |
| | | $OUTPUT->reset(); |
| | | $OUTPUT->send('error'); |
| | | if ($rcmail->output && $rcmail->output->template_exists('error')) { |
| | | $rcmail->output->reset(); |
| | | $rcmail->output->send('error'); |
| | | } |
| | | |
| | | $__skin = $CONFIG->skin ? $CONFIG->skin : 'default'; |
| | | $__productname = $CONFIG['product_name'] ? $CONFIG['product_name'] : 'Roundcube Webmail'; |
| | | $__skin = $rcmail->config->get('skin', 'default'); |
| | | $__productname = $rcmail->config->get('product_name', 'Roundcube Webmail'); |
| | | |
| | | // print system error page |
| | | print <<<EOF |