diff options
author | Thomas Bruederli <thomas@roundcube.net> | 2012-09-05 16:17:56 +0200 |
---|---|---|
committer | Thomas Bruederli <thomas@roundcube.net> | 2012-09-05 16:17:56 +0200 |
commit | ba3cd80c0c61e679cef92a1f4f51e645090a1472 (patch) | |
tree | 4bd7c352dade0d18c19f25d65b04a5b896813918 /program/include | |
parent | 957ac142e3c80db3b86df4f0db3b82cfa8856206 (diff) | |
parent | e263994adc3f8f331c6167da1665c1920a5142f9 (diff) |
Merge branch 'master' of github.com:roundcube/roundcubemail
Diffstat (limited to 'program/include')
-rw-r--r-- | program/include/html.php | 2 | ||||
-rw-r--r-- | program/include/rcmail.php | 35 | ||||
-rw-r--r-- | program/include/rcube.php | 1439 | ||||
-rw-r--r-- | program/include/rcube_bc.inc | 5 | ||||
-rw-r--r-- | program/include/rcube_browser.php | 2 | ||||
-rw-r--r-- | program/include/rcube_charset.php | 6 | ||||
-rw-r--r-- | program/include/rcube_config.php | 1 | ||||
-rw-r--r-- | program/include/rcube_imap.php | 4 | ||||
-rw-r--r-- | program/include/rcube_imap_cache.php | 52 | ||||
-rw-r--r-- | program/include/rcube_imap_generic.php | 5 | ||||
-rw-r--r-- | program/include/rcube_ldap.php | 5 | ||||
-rw-r--r-- | program/include/rcube_message.php | 28 | ||||
-rw-r--r-- | program/include/rcube_mime.php | 4 | ||||
-rw-r--r-- | program/include/rcube_output_html.php | 36 | ||||
-rw-r--r-- | program/include/rcube_plugin.php | 2 | ||||
-rw-r--r-- | program/include/rcube_shared.inc | 33 | ||||
-rw-r--r-- | program/include/rcube_storage.php | 2 | ||||
-rw-r--r-- | program/include/rcube_utils.php | 61 | ||||
-rw-r--r-- | program/include/rcube_vcard.php | 2 |
19 files changed, 960 insertions, 764 deletions
diff --git a/program/include/html.php b/program/include/html.php index d15d50875..c6507f813 100644 --- a/program/include/html.php +++ b/program/include/html.php @@ -154,7 +154,7 @@ class html $attr = array('src' => $attr); } return self::tag('img', $attr + array('alt' => ''), null, array_merge(self::$common_attrib, - array('src','alt','width','height','border','usemap','onclick'))); + array('src','alt','width','height','border','usemap','onclick'))); } /** diff --git a/program/include/rcmail.php b/program/include/rcmail.php index a6b0bcd57..02f38e647 100644 --- a/program/include/rcmail.php +++ b/program/include/rcmail.php @@ -157,14 +157,16 @@ class rcmail extends rcube $this->config->set_user_prefs((array)$this->user->get_prefs()); } - $_SESSION['language'] = $this->user->language = $this->language_prop($this->config->get('language', $_SESSION['language'])); + $lang = $this->language_prop($this->config->get('language', $_SESSION['language'])); + $_SESSION['language'] = $this->user->language = $lang; // set localization - setlocale(LC_ALL, $_SESSION['language'] . '.utf8', 'en_US.utf8'); + setlocale(LC_ALL, $lang . '.utf8', $lang . '.UTF-8', 'en_US.utf8', 'en_US.UTF-8'); // workaround for http://bugs.php.net/bug.php?id=18556 - if (in_array($_SESSION['language'], array('tr_TR', 'ku', 'az_AZ'))) - setlocale(LC_CTYPE, 'en_US' . '.utf8'); + if (in_array($lang, array('tr_TR', 'ku', 'az_AZ'))) { + setlocale(LC_CTYPE, 'en_US.utf8', 'en_US.UTF-8'); + } } @@ -306,7 +308,7 @@ class rcmail extends rcube /** * Init output object for GUI and add common scripts. - * This will instantiate a rcmail_template object and set + * This will instantiate a rcube_output_html object and set * environment vars according to the current session and configuration * * @param boolean True if this request is loaded in a (i)frame @@ -453,7 +455,14 @@ class rcmail extends rcube // Convert username to lowercase. If storage backend // is case-insensitive we need to store always the same username (#1487113) if ($config['login_lc']) { - $username = mb_strtolower($username); + if ($config['login_lc'] == 2 || $config['login_lc'] === true) { + $username = mb_strtolower($username); + } + else if (strpos($username, '@')) { + // lowercase domain name + list($local, $domain) = explode('@', $username); + $username = $local . '@' . mb_strtolower($domain); + } } // try to resolve email address from virtuser table @@ -463,17 +472,13 @@ class rcmail extends rcube // Here we need IDNA ASCII // Only rcube_contacts class is using domain names in Unicode - $host = rcube_utils::idn_to_ascii($host); - if (strpos($username, '@')) { - // lowercase domain name - list($local, $domain) = explode('@', $username); - $username = $local . '@' . mb_strtolower($domain); - $username = rcube_utils::idn_to_ascii($username); - } + $host = rcube_utils::idn_to_ascii($host); + $username = rcube_utils::idn_to_ascii($username); // user already registered -> overwrite username - if ($user = rcube_user::query($username, $host)) + if ($user = rcube_user::query($username, $host)) { $username = $user->data['username']; + } $storage = $this->get_storage(); @@ -1204,7 +1209,7 @@ class rcmail extends rcube } else { if (!empty($date)) { - $timestamp = rcube_strtotime($date); + $timestamp = rcube_utils::strtotime($date); } if (empty($timestamp)) { diff --git a/program/include/rcube.php b/program/include/rcube.php index 494b5c3dd..0e40b3c6b 100644 --- a/program/include/rcube.php +++ b/program/include/rcube.php @@ -25,364 +25,372 @@ * Base class of the Roundcube Framework * implemented as singleton * - * @package Core + * @package Framework + * @subpackage Core */ class rcube { - const INIT_WITH_DB = 1; - const INIT_WITH_PLUGINS = 2; - - /** - * Singleton instace of rcube - * - * @var rcmail - */ - static protected $instance; - - /** - * Stores instance of rcube_config. - * - * @var rcube_config - */ - public $config; - - /** - * Instace of database class. - * - * @var rcube_pdo - */ - public $db; - - /** - * Instace of Memcache class. - * - * @var Memcache - */ - 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 - * - * @param integer Options to initialize with this instance. See rcube::INIT_WITH_* constants - * @return rcube The one and only instance - */ - static function get_instance($mode = 0) - { - if (!self::$instance) { - self::$instance = new rcube(); - self::$instance->init($mode); - } + const INIT_WITH_DB = 1; + const INIT_WITH_PLUGINS = 2; - 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); - } + /** + * Singleton instace of rcube + * + * @var rcmail + */ + static protected $instance; - // connect to database - if ($mode & self::INIT_WITH_DB) { - $this->get_dbh(); - } + /** + * Stores instance of rcube_config. + * + * @var rcube_config + */ + public $config; + + /** + * Instace of database class. + * + * @var rcube_db + */ + public $db; + + /** + * Instace of Memcache class. + * + * @var Memcache + */ + 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; - // create plugin API and load plugins - if ($mode & self::INIT_WITH_PLUGINS) { - $this->plugins = rcube_plugin_api::get_instance(); + + /* private/protected vars */ + protected $texts; + protected $caches = array(); + protected $shutdown_functions = array(); + protected $expunge_cache = false; + + + /** + * This implements the 'singleton' design pattern + * + * @param integer Options to initialize with this instance. See rcube::INIT_WITH_* constants + * + * @return rcube The one and only instance + */ + static function get_instance($mode = 0) + { + if (!self::$instance) { + self::$instance = new rcube(); + self::$instance->init($mode); + } + + return self::$instance; } - } - - - /** - * Get the current database connection - * - * @return rcube_pdo Database connection object - */ - public function get_dbh() - { - if (!$this->db) { - $config_all = $this->config->all(); - $this->db = rcube_db::factory($config_all['db_dsnw'], $config_all['db_dsnr'], $config_all['db_persistent']); - $this->db->set_debug((bool)$config_all['sql_debug']); + + + /** + * 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')); } - 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; + /** + * 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); + } - // add all configured hosts to pool - $pconnect = $this->config->get('memcache_pconnect', true); - foreach ($this->config->get('memcache_hosts', array()) as $host) { - if (substr($host, 0, 7) != 'unix://') { - list($host, $port) = explode(':', $host); - if (!$port) $port = 11211; + // connect to database + if ($mode & self::INIT_WITH_DB) { + $this->get_dbh(); } - else { - $port = 0; + + // create plugin API and load plugins + if ($mode & self::INIT_WITH_PLUGINS) { + $this->plugins = rcube_plugin_api::get_instance(); } + } - $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 + /** + * Get the current database connection + * + * @return rcube_db Database object + */ + public function get_dbh() + { + if (!$this->db) { + $config_all = $this->config->all(); + $this->db = rcube_db::factory($config_all['db_dsnw'], $config_all['db_dsnr'], $config_all['db_persistent']); + $this->db->set_debug((bool)$config_all['sql_debug']); + } - if (!$this->mc_available) - $this->memcache = false; + return $this->db; } - return $this->memcache; - } + /** + * 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; + } - /** - * Callback for memcache failure - */ - public function memcache_failure($host, $port) - { - static $seen = array(); + $this->memcache = new Memcache; + $this->mc_available = 0; - // 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 string $ttl Expiration time for cache items - * @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]) && ($userid = $this->get_user_id())) { - $this->caches[$name] = new rcube_cache($type, $userid, $name, $ttl, $packed); - } + // add all configured hosts to pool + $pconnect = $this->config->get('memcache_pconnect', true); + foreach ($this->config->get('memcache_hosts', array()) as $host) { + if (substr($host, 0, 7) != 'unix://') { + list($host, $port) = explode(':', $host); + if (!$port) $port = 11211; + } + else { + $port = 0; + } - 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(); - } + $this->mc_available += intval($this->memcache->addServer( + $host, $port, $pconnect, 1, 1, 15, false, array($this, 'memcache_failure'))); + } - return $this->storage; - } + // 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; + } + } - /** - * Initialize storage object - */ - public function storage_init() - { - // already initialized - if (is_object($this->storage)) { - return; + return $this->memcache; } - $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); + /** + * 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 storage object - $this->storage = new $driver_class; - // for backward compat. (deprecated, will be removed) - $this->imap = $this->storage; + /** + * Initialize and get cache object + * + * @param string $name Cache identifier + * @param string $type Cache type ('db', 'apc' or 'memcache') + * @param string $ttl Expiration time for cache items + * @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]) && ($userid = $this->get_user_id())) { + $this->caches[$name] = new rcube_cache($type, $userid, $name, $ttl, $packed); + } - // 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; + return $this->caches[$name]; } - 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); + /** + * 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(); + } } - $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']); - $_SESSION[$driver.'_host'] = $_SESSION['storage_host']; + + + /** + * 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; } - $options = $this->plugins->exec_hook("storage_init", $options); - // for backward compat. (deprecated, to be removed) - $options = $this->plugins->exec_hook("imap_init", $options); + /** + * Initialize storage object + */ + public function storage_init() + { + // already initialized + if (is_object($this->storage)) { + return; + } - $this->storage->set_options($options); - $this->set_storage_prop(); - } + $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); + } - /** - * Set storage parameters. - * This must be done AFTER connecting to the server! - */ - protected function set_storage_prop() - { - $storage = $this->get_storage(); + // Initialize storage object + $this->storage = new $driver_class; - $storage->set_charset($this->config->get('default_charset', RCMAIL_CHARSET)); + // for backward compat. (deprecated, will be removed) + $this->imap = $this->storage; - if ($default_folders = $this->config->get('default_folders')) { - $storage->set_default_folders($default_folders); - } - if (isset($_SESSION['mbox'])) { - $storage->set_folder($_SESSION['mbox']); + // 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']); + $_SESSION[$driver.'_host'] = $_SESSION['storage_host']; + } + + $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(); } - if (isset($_SESSION['page'])) { - $storage->set_page($_SESSION['page']); + + + /** + * 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']); + } } - } /** @@ -397,12 +405,17 @@ class rcube $sess_name = $this->config->get('session_name'); $sess_domain = $this->config->get('session_domain'); + $sess_path = $this->config->get('session_path'); $lifetime = $this->config->get('session_lifetime', 0) * 60; // set session domain if ($sess_domain) { ini_set('session.cookie_domain', $sess_domain); } + // set session path + if ($sess_path) { + ini_set('session.cookie_path', $sess_path); + } // set session garbage collecting time according to session_lifetime if ($lifetime) { ini_set('session.gc_maxlifetime', $lifetime * 2); @@ -492,433 +505,469 @@ class rcube } - /** - * 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]"; - } + /** + * 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(); + } - // 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); - } + // extract attributes + if (is_string($attrib)) { + $attrib = array('name' => $attrib); + } - // 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; - } + $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; + } - // 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; + // 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")); } - // specified domain - else if ($domain) { - $ref_domain = $domain; - return isset($this->texts[$domain.'.'.$name]); + + + /** + * 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; } - 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 a localization package + * + * @param string Language ID + * @param array Additional text labels/messages + */ + 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(); - // 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(); - // 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'); - // 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); - 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' && $lang != 'en_US' && 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'); - // include user language files - if ($lang != 'en' && $lang != 'en_US' && 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); + } - 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(); - ob_end_clean(); + $_SESSION['language'] = $lang; + } - $_SESSION['language'] = $lang; + // append additional texts (from plugin) + if (is_array($add) && !empty($add)) { + $this->texts += $add; + } } - // 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]; + /** + * 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; } - // 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); - } + + + /** + * 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; } - if (!isset($rcube_languages[$lang]) || !is_dir(INSTALL_PATH . 'program/localization/' . $lang)) { - $lang = 'en_US'; + + /** + * 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; } - return $lang; - } + /** + * 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 ''; + } - /** - * 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(); + $cipher = $base64 ? base64_decode($cipher) : $cipher; - if (!sizeof($sa_languages)) { - @include(INSTALL_PATH . 'program/localization/index.inc'); + 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); - if ($dh = @opendir(INSTALL_PATH . 'program/localization')) { - while (($name = readdir($dh)) !== false) { - if ($name[0] == '.' || !is_dir(INSTALL_PATH . 'program/localization/' . $name)) - continue; + // session corruption? (#1485970) + if (strlen($iv) < $iv_size) { + return ''; + } - if ($label = $rcube_languages[$name]) - $sa_languages[$name] = $label; + $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); + } } - closedir($dh); - } + + /*- + * Trim PHP's padding and the canary byte; see note in + * rcube::encrypt() and http://php.net/mcrypt_generic#68082 + */ + $clear = substr(rtrim($clear, "\0"), 0, -1); + + return $clear; } - 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 + + /** + * Generates encryption initialization vector (IV) + * + * @param int Vector size + * + * @return string Vector string */ - $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); - } + 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; } - 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); - } + /** + * 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 ''; } - /*- - * Trim PHP's padding and the canary byte; see note in - * rcube::encrypt() and http://php.net/mcrypt_generic#68082 + + /** + * Function to be executed in script shutdown + * Registered with register_shutdown_function() */ - $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(); - } + 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(); + 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; - } - - - /** - * 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); + + + /** + * 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 strtr behaviour of going through source string once - $cmd = strtr($cmd, $replacements); - return (string)shell_exec($cmd); - } + /** + * 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); + } /** diff --git a/program/include/rcube_bc.inc b/program/include/rcube_bc.inc index 1932f86e2..1894873e6 100644 --- a/program/include/rcube_bc.inc +++ b/program/include/rcube_bc.inc @@ -38,11 +38,6 @@ function get_table_name($table) return rcmail::get_instance()->db->table_name($table); } -function get_sequence_name($sequence) -{ - return rcmail::get_instance()->db->sequence_name($sequence); -} - function rcube_label($p, $domain=null) { return rcmail::get_instance()->gettext($p, $domain); diff --git a/program/include/rcube_browser.php b/program/include/rcube_browser.php index 06033e036..7cfae709d 100644 --- a/program/include/rcube_browser.php +++ b/program/include/rcube_browser.php @@ -20,8 +20,6 @@ */ /** - * rcube_browser - * * Provide details about the client's browser based on the User-Agent header * * @package Core diff --git a/program/include/rcube_charset.php b/program/include/rcube_charset.php index 380d14978..1740a6096 100644 --- a/program/include/rcube_charset.php +++ b/program/include/rcube_charset.php @@ -181,6 +181,12 @@ class rcube_charset $to = empty($to) ? strtoupper(RCMAIL_CHARSET) : self::parse_charset($to); $from = self::parse_charset($from); + // It is a common case when UTF-16 charset is used with US-ASCII content (#1488654) + // In that case we can just skip the conversion (use UTF-8) + if ($from == 'UTF-16' && !preg_match('/[^\x00-\x7F]/', $str)) { + $from = 'UTF-8'; + } + if ($from == $to || empty($str) || empty($from)) { return $str; } diff --git a/program/include/rcube_config.php b/program/include/rcube_config.php index e2997906c..41acc80dd 100644 --- a/program/include/rcube_config.php +++ b/program/include/rcube_config.php @@ -42,6 +42,7 @@ class rcube_config 'default_folders' => 'default_imap_folders', 'mail_pagesize' => 'pagesize', 'addressbook_pagesize' => 'pagesize', + 'reply_mode' => 'top_posting', ); diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php index 5dd9c1250..66b5c4bd6 100644 --- a/program/include/rcube_imap.php +++ b/program/include/rcube_imap.php @@ -2222,6 +2222,10 @@ class rcube_imap extends rcube_storage $folder = $this->folder; } + if (!$this->check_connection()) { + return false; + } + // make sure folder exists if ($this->folder_exists($folder)) { if ($is_file) { diff --git a/program/include/rcube_imap_cache.php b/program/include/rcube_imap_cache.php index a061a1f6e..f36ace0eb 100644 --- a/program/include/rcube_imap_cache.php +++ b/program/include/rcube_imap_cache.php @@ -5,7 +5,7 @@ | program/include/rcube_imap_cache.php | | | | 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. | @@ -350,11 +350,11 @@ class rcube_imap_cache function get_message($mailbox, $uid, $update = true, $cache = true) { // Check internal cache - if ($this->icache['message'] - && $this->icache['message']['mailbox'] == $mailbox - && $this->icache['message']['object']->uid == $uid + if ($this->icache['__message'] + && $this->icache['__message']['mailbox'] == $mailbox + && $this->icache['__message']['object']->uid == $uid ) { - return $this->icache['message']['object']; + return $this->icache['__message']['object']; } $sql_result = $this->db->query( @@ -386,7 +386,7 @@ class rcube_imap_cache // Save current message from internal cache $this->save_icache(); - $this->icache['message'] = array( + $this->icache['__message'] = array( 'object' => $message, 'mailbox' => $mailbox, 'exists' => $found, @@ -459,20 +459,28 @@ class rcube_imap_cache */ function change_flag($mailbox, $uids, $flag, $enabled = false) { + if (empty($uids)) { + return; + } + $flag = strtoupper($flag); $idx = (int) array_search($flag, $this->flags); + $uids = (array) $uids; if (!$idx) { return; } // Internal cache update - if ($uids && count($uids) == 1 && ($uid = current($uids)) - && ($message = $this->icache['message']) - && $message['mailbox'] == $mailbox && $message['object']->uid == $uid + if (($message = $this->icache['__message']) + && $message['mailbox'] === $mailbox + && in_array($message['object']->uid, $uids) ) { $message['object']->flags[$flag] = $enabled; - return; + + if (count($uids) == 1) { + return; + } } $this->db->query( @@ -481,7 +489,7 @@ class rcube_imap_cache .", flags = flags ".($enabled ? "+ $idx" : "- $idx") ." WHERE user_id = ?" ." AND mailbox = ?" - .($uids !== null ? " AND uid IN (".$this->db->array2list((array)$uids, 'integer').")" : "") + .($uids !== null ? " AND uid IN (".$this->db->array2list($uids, 'integer').")" : "") ." AND (flags & $idx) ".($enabled ? "= 0" : "= $idx"), $this->userid, $mailbox); } @@ -503,10 +511,11 @@ class rcube_imap_cache } else { // Remove the message from internal cache - if (!empty($uids) && !is_array($uids) && ($message = $this->icache['message']) - && $message['mailbox'] == $mailbox && $message['object']->uid == $uids + if (!empty($uids) && ($message = $this->icache['__message']) + && $message['mailbox'] === $mailbox + && in_array($message['object']->uid, (array)$uids) ) { - $this->icache['message'] = null; + $this->icache['__message'] = null; } $this->db->query( @@ -608,13 +617,13 @@ class rcube_imap_cache // get expiration timestamp $ts = get_offset_time($ttl, -1); - $this->db->query("DELETE FROM ".get_table_name('cache_messages') + $this->db->query("DELETE FROM ".$this->db->table_name('cache_messages') ." WHERE changed < " . $this->db->fromunixtime($ts)); - $this->db->query("DELETE FROM ".get_table_name('cache_index') + $this->db->query("DELETE FROM ".$this->db->table_name('cache_index') ." WHERE changed < " . $this->db->fromunixtime($ts)); - $this->db->query("DELETE FROM ".get_table_name('cache_thread') + $this->db->query("DELETE FROM ".$this->db->table_name('cache_thread') ." WHERE changed < " . $this->db->fromunixtime($ts)); } @@ -763,6 +772,11 @@ class rcube_imap_cache $object = $index['object']; $is_thread = is_a($object, 'rcube_result_thread'); + // sanity check + if (empty($object)) { + return false; + } + // Get mailbox data (UIDVALIDITY, counters, etc.) for status check $mbox_data = $this->imap->folder_data($mailbox); @@ -1078,7 +1092,7 @@ class rcube_imap_cache private function save_icache() { // Save current message from internal cache - if ($message = $this->icache['message']) { + if ($message = $this->icache['__message']) { // clean up some object's data $object = $this->message_object_prepare($message['object']); @@ -1089,7 +1103,7 @@ class rcube_imap_cache $this->add_message($message['mailbox'], $object, !$message['exists']); } - $this->icache['message']['md5sum'] = $md5sum; + $this->icache['__message']['md5sum'] = $md5sum; } } diff --git a/program/include/rcube_imap_generic.php b/program/include/rcube_imap_generic.php index 915a11aad..c3cfabc3a 100644 --- a/program/include/rcube_imap_generic.php +++ b/program/include/rcube_imap_generic.php @@ -2538,7 +2538,7 @@ class rcube_imap_generic { unset($this->data['APPENDUID']); - if (!$mailbox) { + if ($mailbox === null || $mailbox === '') { return false; } @@ -2603,7 +2603,7 @@ class rcube_imap_generic { unset($this->data['APPENDUID']); - if (!$mailbox) { + if ($mailbox === null || $mailbox === '') { return false; } @@ -2612,6 +2612,7 @@ class rcube_imap_generic if (file_exists(realpath($path))) { $in_fp = fopen($path, 'r'); } + if (!$in_fp) { $this->setError(self::ERROR_UNKNOWN, "Couldn't open $path for reading"); return false; diff --git a/program/include/rcube_ldap.php b/program/include/rcube_ldap.php index dbab0fd06..ad2ccddeb 100644 --- a/program/include/rcube_ldap.php +++ b/program/include/rcube_ldap.php @@ -139,6 +139,11 @@ class rcube_ldap extends rcube_addressbook unset($this->coltypes[$childcol]); // remove address child col from global coltypes list } } + + // at least one address type must be specified + if (empty($this->coltypes['address']['subtypes'])) { + $this->coltypes['address']['subtypes'] = array('home'); + } } else if ($this->coltypes['address']) { $this->coltypes['address'] += array('type' => 'textarea', 'childs' => null, 'size' => 40); diff --git a/program/include/rcube_message.php b/program/include/rcube_message.php index 9d36acf38..6af1d0133 100644 --- a/program/include/rcube_message.php +++ b/program/include/rcube_message.php @@ -50,13 +50,14 @@ class rcube_message */ private $mime; private $opt = array(); - private $inline_parts = array(); private $parse_alternative = false; - public $uid = null; + public $uid; + public $folder; public $headers; public $parts = array(); public $mime_parts = array(); + public $inline_parts = array(); public $attachments = array(); public $subject = ''; public $sender = null; @@ -68,17 +69,22 @@ class rcube_message * * Provide a uid, and parse message structure. * - * @param string $uid The message UID. + * @param string $uid The message UID. + * @param string $folder Folder name * * @see self::$app, self::$storage, self::$opt, self::$parts */ - function __construct($uid) + function __construct($uid, $folder = null) { $this->uid = $uid; $this->app = rcube::get_instance(); $this->storage = $this->app->get_storage(); + $this->folder = strlen($folder) ? $folder : $this->storage->get_folder(); $this->storage->set_options(array('all_headers' => true)); + // Set current folder + $this->storage->set_folder($this->folder); + $this->headers = $this->storage->get_message($uid); if (!$this->headers) @@ -179,10 +185,12 @@ class rcube_message } return $fp ? true : $part->body; } + // get from IMAP + $this->storage->set_folder($this->folder); + return $this->storage->get_message_part($this->uid, $mime_id, $part, NULL, $fp, $skip_charset_conv); - } else - return null; + } } @@ -637,8 +645,10 @@ class rcube_message function tnef_decode(&$part) { // @TODO: attachment may be huge, hadle it via file - if (!isset($part->body)) + if (!isset($part->body)) { + $this->storage->set_folder($this->folder); $part->body = $this->storage->get_message_part($this->uid, $part->mime_id, $part); + } $parts = array(); $tnef = new tnef_decoder; @@ -673,8 +683,10 @@ class rcube_message function uu_decode(&$part) { // @TODO: messages may be huge, hadle body via file - if (!isset($part->body)) + if (!isset($part->body)) { + $this->storage->set_folder($this->folder); $part->body = $this->storage->get_message_part($this->uid, $part->mime_id, $part); + } $parts = array(); // FIXME: line length is max.65? diff --git a/program/include/rcube_mime.php b/program/include/rcube_mime.php index e1f736a78..d8e04a97c 100644 --- a/program/include/rcube_mime.php +++ b/program/include/rcube_mime.php @@ -541,10 +541,10 @@ class rcube_mime $prefix = $regs[0]; $level = strlen($prefix); $line = rtrim(substr($line, $level)); - $line = $prefix . rc_wordwrap($line, $length - $level - 2, " \r\n$prefix "); + $line = $prefix . self::wordwrap($line, $length - $level - 2, " \r\n$prefix "); } else if ($line) { - $line = rc_wordwrap(rtrim($line), $length - 2, " \r\n"); + $line = self::wordwrap(rtrim($line), $length - 2, " \r\n"); // space-stuffing $line = preg_replace('/(^|\r\n)(From| |>)/', '\\1 \\2', $line); } diff --git a/program/include/rcube_output_html.php b/program/include/rcube_output_html.php index 0a8f0e364..2743e7705 100644 --- a/program/include/rcube_output_html.php +++ b/program/include/rcube_output_html.php @@ -67,6 +67,11 @@ class rcube_output_html extends rcube_output $this->set_env('task', $task); $this->set_env('x_frame_options', $this->config->get('x_frame_options', 'sameorigin')); + // add cookie info + $this->set_env('cookie_domain', ini_get('session.cookie_domain')); + $this->set_env('cookie_path', ini_get('session.cookie_path')); + $this->set_env('cookie_secure', ini_get('session.cookie_secure')); + // load the correct skin (in case user-defined) $skin = $this->config->get('skin'); $this->set_skin($skin); @@ -395,7 +400,7 @@ class rcube_output_html extends rcube_output 'line' => __LINE__, 'file' => __FILE__, 'message' => 'Error loading template for '.$realname - ), true, true); + ), true, $write); return false; } @@ -693,6 +698,11 @@ class rcube_output_html extends rcube_output } break; + // frame + case 'frame': + return $this->frame($attrib); + break; + // show a label case 'label': if ($attrib['name'] || $attrib['command']) { @@ -1270,6 +1280,30 @@ class rcube_output_html extends rcube_output } + /** + * Returns iframe object, registers some related env variables + * + * @param array $attrib HTML attributes + * + * @return string IFRAME element + */ + public function frame($attrib) + { + if (!$attrib['id']) { + $attrib['id'] = 'rcmframe'; + } + + if (!$attrib['name']) { + $attrib['name'] = $attrib['id']; + } + + $this->set_env('contentframe', $attrib['id']); + $this->set_env('blankpage', $attrib['src'] ? $this->abs_url($attrib['src']) : 'program/resources/blank.gif'); + + return html::iframe($attrib); + } + + /* ************* common functions delivering gui objects ************** */ diff --git a/program/include/rcube_plugin.php b/program/include/rcube_plugin.php index b1ec32a8a..c1035733b 100644 --- a/program/include/rcube_plugin.php +++ b/program/include/rcube_plugin.php @@ -336,7 +336,7 @@ abstract class rcube_plugin public function local_skin_path() { $rcmail = rcube::get_instance(); - foreach (array($rcmail->config->get('skin'),'default') as $skin) { + foreach (array($rcmail->config->get('skin'), 'larry') as $skin) { $skin_path = 'skins/' . $skin; if (is_dir(realpath(slashify($this->home) . $skin_path))) break; diff --git a/program/include/rcube_shared.inc b/program/include/rcube_shared.inc index 85f278432..c15305c08 100644 --- a/program/include/rcube_shared.inc +++ b/program/include/rcube_shared.inc @@ -108,11 +108,11 @@ function slashify($str) /** - * Remove slash at the end of the string + * Remove slashes at the end of the string */ function unslashify($str) { - return preg_replace('/\/$/', '', $str); + return preg_replace('/\/+$/', '', $str); } @@ -231,7 +231,7 @@ function array_keys_recursive($array) { $keys = array(); - if (!empty($array)) { + if (!empty($array) && is_array($array)) { foreach ($array as $key => $child) { $keys[] = $key; foreach (array_keys_recursive($child) as $val) { @@ -255,7 +255,7 @@ function asciiwords($str, $css_id = false, $replace_with = '') /** - * Remove single and double quotes from given string + * Remove single and double quotes from a given string * * @param string Input value * @@ -306,6 +306,29 @@ function format_email_recipient($email, $name = '') /** + * Format e-mail address + * + * @param string $email E-mail address + * + * @return string Formatted e-mail address + */ +function format_email($email) +{ + $email = trim($email); + $parts = explode('@', $email); + $count = count($parts); + + if ($count > 1) { + $parts[$count-1] = mb_strtolower($parts[$count-1]); + + $email = implode('@', $parts); + } + + return $email; +} + + +/** * mbstring replacement functions */ if (!extension_loaded('mbstring')) @@ -399,7 +422,6 @@ function rcube_autoload($classname) { $filename = preg_replace( array( - '/MDB2_(.+)/', '/Mail_(.+)/', '/Net_(.+)/', '/Auth_(.+)/', @@ -408,7 +430,6 @@ function rcube_autoload($classname) ), array( 'Mail/\\1', - 'Mail/\\1', 'Net/\\1', 'Auth/\\1', 'html', diff --git a/program/include/rcube_storage.php b/program/include/rcube_storage.php index 768a26d73..f83e24041 100644 --- a/program/include/rcube_storage.php +++ b/program/include/rcube_storage.php @@ -195,7 +195,7 @@ abstract class rcube_storage */ public function set_folder($folder) { - if ($this->folder == $folder) { + if ($this->folder === $folder) { return; } diff --git a/program/include/rcube_utils.php b/program/include/rcube_utils.php index d1a8315ec..23bf556e4 100644 --- a/program/include/rcube_utils.php +++ b/program/include/rcube_utils.php @@ -92,9 +92,9 @@ class rcube_utils 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 + // Validate domain part + if (preg_match('/^\[((IPv6:[0-9a-f:.]+)|([0-9.]+))\]$/i', $domain_part, $matches)) { + return self::check_ip(preg_replace('/^IPv6:/i', '', $matches[1])); // valid IPv4 or IPv6 address } else { // If not an IP address @@ -110,6 +110,11 @@ class rcube_utils } } + // last domain part + if (preg_match('/[^a-zA-Z]/', array_pop($domain_array))) { + return false; + } + $rcube = rcube::get_instance(); if (!$dns_check || !$rcube->config->get('email_dns_check')) { @@ -141,6 +146,52 @@ class rcube_utils return false; } + + /** + * Validates IPv4 or IPv6 address + * + * @param string $ip IP address in v4 or v6 format + * + * @return bool True if the address is valid + */ + public static function check_ip($ip) + { + // IPv6, but there's no build-in IPv6 support + if (strpos($ip, ':') !== false && !defined('AF_INET6')) { + $parts = explode(':', $domain_part); + $count = count($parts); + + if ($count > 8 || $count < 2) { + return false; + } + + foreach ($parts as $idx => $part) { + $length = strlen($part); + if (!$length) { + // there can be only one :: + if ($found_empty) { + return false; + } + $found_empty = true; + } + // last part can be an IPv4 address + else if ($idx == $count - 1) { + if (!preg_match('/^[0-9a-f]{1,4}$/i', $part)) { + return @inet_pton($part) !== false; + } + } + else if (!preg_match('/^[0-9a-f]{1,4}$/i', $part)) { + return false; + } + } + + return true; + } + + return @inet_pton($ip) !== false; + } + + /** * Check whether the HTTP referer matches the current request * @@ -149,8 +200,8 @@ class rcube_utils 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']; + $referer = parse_url(self::request_header('Referer')); + return $referer['host'] == self::request_header('Host') && $referer['path'] == $uri['path']; } diff --git a/program/include/rcube_vcard.php b/program/include/rcube_vcard.php index 37cd3ab26..2bfd474c6 100644 --- a/program/include/rcube_vcard.php +++ b/program/include/rcube_vcard.php @@ -313,7 +313,7 @@ class rcube_vcard case 'birthday': case 'anniversary': - if (($val = rcube_strtotime($value)) && ($fn = self::$fieldmap[$field])) + if (($val = rcube_utils::strtotime($value)) && ($fn = self::$fieldmap[$field])) $this->raw[$fn][] = array(0 => date('Y-m-d', $val), 'value' => array('date')); break; |