| | |
| | | | 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 | |
| | | | Copyright (C) 2008-2014, The Roundcube Dev Team | |
| | | | Copyright (C) 2011-2014, Kolab Systems AG | |
| | | | | |
| | | | Licensed under the GNU General Public License version 3 or | |
| | | | any later version with exceptions for skins & plugins. | |
| | |
| | | +-----------------------------------------------------------------------+ |
| | | */ |
| | | |
| | | |
| | | /** |
| | | * Application class of Roundcube Webmail |
| | | * implemented as singleton |
| | | * |
| | | * @package Core |
| | | * @package Webmail |
| | | */ |
| | | class rcmail extends rcube |
| | | { |
| | | /** |
| | | * Main tasks. |
| | | * |
| | | * @var array |
| | | */ |
| | | static public $main_tasks = array('mail','settings','addressbook','login','logout','utils','dummy'); |
| | | /** |
| | | * Main tasks. |
| | | * |
| | | * @var array |
| | | */ |
| | | static public $main_tasks = array('mail','settings','addressbook','login','logout','utils','dummy'); |
| | | |
| | | /** |
| | | * Current task. |
| | | * |
| | | * @var string |
| | | */ |
| | | public $task; |
| | | /** |
| | | * Current task. |
| | | * |
| | | * @var string |
| | | */ |
| | | public $task; |
| | | |
| | | /** |
| | | * Current action. |
| | | * |
| | | * @var string |
| | | */ |
| | | public $action = ''; |
| | | public $comm_path = './'; |
| | | public $filename = ''; |
| | | /** |
| | | * Current action. |
| | | * |
| | | * @var string |
| | | */ |
| | | public $action = ''; |
| | | public $comm_path = './'; |
| | | public $filename = ''; |
| | | |
| | | private $address_books = array(); |
| | | private $action_map = array(); |
| | | private $address_books = array(); |
| | | private $action_map = array(); |
| | | |
| | | |
| | | const ERROR_STORAGE = -2; |
| | | const ERROR_INVALID_REQUEST = 1; |
| | | const ERROR_INVALID_HOST = 2; |
| | | const ERROR_COOKIES_DISABLED = 3; |
| | | const ERROR_STORAGE = -2; |
| | | const ERROR_INVALID_REQUEST = 1; |
| | | const ERROR_INVALID_HOST = 2; |
| | | const ERROR_COOKIES_DISABLED = 3; |
| | | |
| | | |
| | | /** |
| | | * This implements the 'singleton' design pattern |
| | | * |
| | | * @param string Environment name to run (e.g. live, dev, test) |
| | | * @return rcmail The one and only instance |
| | | */ |
| | | static function get_instance($env = '') |
| | | { |
| | | if (!self::$instance || !is_a(self::$instance, 'rcmail')) { |
| | | self::$instance = new rcmail($env); |
| | | self::$instance->startup(); // init AFTER object was linked with self::$instance |
| | | } |
| | | |
| | | return self::$instance; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initial startup function |
| | | * to register session, create database and imap connections |
| | | */ |
| | | protected function startup() |
| | | { |
| | | $this->init(self::INIT_WITH_DB | self::INIT_WITH_PLUGINS); |
| | | |
| | | // set filename if not index.php |
| | | if (($basename = basename($_SERVER['SCRIPT_FILENAME'])) && $basename != 'index.php') |
| | | $this->filename = $basename; |
| | | |
| | | // start session |
| | | $this->session_init(); |
| | | |
| | | // create user object |
| | | $this->set_user(new rcube_user($_SESSION['user_id'])); |
| | | |
| | | // set task and action properties |
| | | $this->set_task(rcube_utils::get_input_value('_task', rcube_utils::INPUT_GPC)); |
| | | $this->action = asciiwords(rcube_utils::get_input_value('_action', rcube_utils::INPUT_GPC)); |
| | | |
| | | // reset some session parameters when changing task |
| | | if ($this->task != 'utils') { |
| | | // we reset list page when switching to another task |
| | | // but only to the main task interface - empty action (#1489076) |
| | | // this will prevent from unintentional page reset on cross-task requests |
| | | if ($this->session && $_SESSION['task'] != $this->task && empty($this->action)) |
| | | $this->session->remove('page'); |
| | | // set current task to session |
| | | $_SESSION['task'] = $this->task; |
| | | } |
| | | |
| | | // init output class |
| | | if (!empty($_REQUEST['_remote'])) |
| | | $GLOBALS['OUTPUT'] = $this->json_init(); |
| | | else |
| | | $GLOBALS['OUTPUT'] = $this->load_gui(!empty($_REQUEST['_framed'])); |
| | | |
| | | // load plugins |
| | | $this->plugins->init($this, $this->task); |
| | | $this->plugins->load_plugins((array)$this->config->get('plugins', array()), array('filesystem_attachments', 'jqueryui')); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Setter for application task |
| | | * |
| | | * @param string Task to set |
| | | */ |
| | | public function set_task($task) |
| | | { |
| | | $task = asciiwords($task, true); |
| | | |
| | | if ($this->user && $this->user->ID) |
| | | $task = !$task ? 'mail' : $task; |
| | | else |
| | | $task = 'login'; |
| | | |
| | | $this->task = $task; |
| | | $this->comm_path = $this->url(array('task' => $this->task)); |
| | | |
| | | if ($this->output) |
| | | $this->output->set_env('task', $this->task); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Setter for system user object |
| | | * |
| | | * @param rcube_user Current user instance |
| | | */ |
| | | public function set_user($user) |
| | | { |
| | | if (is_object($user)) { |
| | | $this->user = $user; |
| | | |
| | | // overwrite config with user preferences |
| | | $this->config->set_user_prefs((array)$this->user->get_prefs()); |
| | | } |
| | | |
| | | $lang = $this->language_prop($this->config->get('language', $_SESSION['language'])); |
| | | $_SESSION['language'] = $this->user->language = $lang; |
| | | |
| | | // set localization |
| | | 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 (version_compare(PHP_VERSION, '5.5.0', '<') && in_array($lang, array('tr_TR', 'ku', 'az_AZ'))) { |
| | | setlocale(LC_CTYPE, 'en_US.utf8', 'en_US.UTF-8'); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return instance of the internal address book class |
| | | * |
| | | * @param string Address book identifier (-1 for default addressbook) |
| | | * @param boolean True if the address book needs to be writeable |
| | | * |
| | | * @return rcube_contacts Address book object |
| | | */ |
| | | public function get_address_book($id, $writeable = false) |
| | | { |
| | | $contacts = null; |
| | | $ldap_config = (array)$this->config->get('ldap_public'); |
| | | |
| | | // 'sql' is the alias for '0' used by autocomplete |
| | | if ($id == 'sql') |
| | | $id = '0'; |
| | | else if ($id == -1) { |
| | | $id = $this->config->get('default_addressbook'); |
| | | $default = true; |
| | | } |
| | | |
| | | // use existing instance |
| | | if (isset($this->address_books[$id]) && ($this->address_books[$id] instanceof rcube_addressbook)) { |
| | | $contacts = $this->address_books[$id]; |
| | | } |
| | | else if ($id && $ldap_config[$id]) { |
| | | $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->get_user_id()); |
| | | } |
| | | else { |
| | | $plugin = $this->plugins->exec_hook('addressbook_get', array('id' => $id, 'writeable' => $writeable)); |
| | | |
| | | // plugin returned instance of a rcube_addressbook |
| | | if ($plugin['instance'] instanceof rcube_addressbook) { |
| | | $contacts = $plugin['instance']; |
| | | } |
| | | } |
| | | |
| | | // when user requested default writeable addressbook |
| | | // we need to check if default is writeable, if not we |
| | | // will return first writeable book (if any exist) |
| | | if ($contacts && $default && $contacts->readonly && $writeable) { |
| | | $contacts = null; |
| | | } |
| | | |
| | | // Get first addressbook from the list if configured default doesn't exist |
| | | // This can happen when user deleted the addressbook (e.g. Kolab folder) |
| | | if (!$contacts && (!$id || $default)) { |
| | | $source = reset($this->get_address_sources($writeable, !$default)); |
| | | if (!empty($source)) { |
| | | $contacts = $this->get_address_book($source['id']); |
| | | if ($contacts) { |
| | | $id = $source['id']; |
| | | /** |
| | | * This implements the 'singleton' design pattern |
| | | * |
| | | * @param string Environment name to run (e.g. live, dev, test) |
| | | * |
| | | * @return rcmail The one and only instance |
| | | */ |
| | | static function get_instance($env = '') |
| | | { |
| | | if (!self::$instance || !is_a(self::$instance, 'rcmail')) { |
| | | self::$instance = new rcmail($env); |
| | | // init AFTER object was linked with self::$instance |
| | | self::$instance->startup(); |
| | | } |
| | | } |
| | | |
| | | return self::$instance; |
| | | } |
| | | |
| | | if (!$contacts) { |
| | | // there's no default, just return |
| | | if ($default) { |
| | | return null; |
| | | } |
| | | /** |
| | | * Initial startup function |
| | | * to register session, create database and imap connections |
| | | */ |
| | | protected function startup() |
| | | { |
| | | $this->init(self::INIT_WITH_DB | self::INIT_WITH_PLUGINS); |
| | | |
| | | self::raise_error(array( |
| | | 'code' => 700, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Addressbook source ($id) not found!"), |
| | | true, true); |
| | | } |
| | | |
| | | // add to the 'books' array for shutdown function |
| | | $this->address_books[$id] = $contacts; |
| | | |
| | | if ($writeable && $contacts->readonly) { |
| | | return null; |
| | | } |
| | | |
| | | // set configured sort order |
| | | if ($sort_col = $this->config->get('addressbook_sort_col')) { |
| | | $contacts->set_sort_order($sort_col); |
| | | } |
| | | |
| | | return $contacts; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return identifier of the address book object |
| | | * |
| | | * @param rcube_addressbook Addressbook source object |
| | | * |
| | | * @return string Source identifier |
| | | */ |
| | | public function get_address_book_id($object) |
| | | { |
| | | foreach ($this->address_books as $index => $book) { |
| | | if ($book === $object) { |
| | | return $index; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return address books list |
| | | * |
| | | * @param boolean True if the address book needs to be writeable |
| | | * @param boolean True if the address book needs to be not hidden |
| | | * |
| | | * @return array Address books array |
| | | */ |
| | | public function get_address_sources($writeable = false, $skip_hidden = false) |
| | | { |
| | | $abook_type = (string) $this->config->get('address_book_type'); |
| | | $ldap_config = (array) $this->config->get('ldap_public'); |
| | | $autocomplete = (array) $this->config->get('autocomplete_addressbooks'); |
| | | $list = array(); |
| | | |
| | | // We are using the DB address book or a plugin address book |
| | | if (!empty($abook_type) && strtolower($abook_type) != 'ldap') { |
| | | if (!isset($this->address_books['0'])) |
| | | $this->address_books['0'] = new rcube_contacts($this->db, $this->get_user_id()); |
| | | $list['0'] = array( |
| | | 'id' => '0', |
| | | 'name' => $this->gettext('personaladrbook'), |
| | | 'groups' => $this->address_books['0']->groups, |
| | | 'readonly' => $this->address_books['0']->readonly, |
| | | 'autocomplete' => in_array('sql', $autocomplete), |
| | | 'undelete' => $this->address_books['0']->undelete && $this->config->get('undo_timeout'), |
| | | ); |
| | | } |
| | | |
| | | if (!empty($ldap_config)) { |
| | | foreach ($ldap_config as $id => $prop) { |
| | | // handle misconfiguration |
| | | if (empty($prop) || !is_array($prop)) { |
| | | continue; |
| | | // set filename if not index.php |
| | | if (($basename = basename($_SERVER['SCRIPT_FILENAME'])) && $basename != 'index.php') { |
| | | $this->filename = $basename; |
| | | } |
| | | $list[$id] = array( |
| | | 'id' => $id, |
| | | 'name' => html::quote($prop['name']), |
| | | 'groups' => !empty($prop['groups']) || !empty($prop['group_filters']), |
| | | 'readonly' => !$prop['writable'], |
| | | 'hidden' => $prop['hidden'], |
| | | 'autocomplete' => in_array($id, $autocomplete) |
| | | ); |
| | | } |
| | | } |
| | | |
| | | $plugin = $this->plugins->exec_hook('addressbooks_list', array('sources' => $list)); |
| | | $list = $plugin['sources']; |
| | | // start session |
| | | $this->session_init(); |
| | | |
| | | foreach ($list as $idx => $item) { |
| | | // register source for shutdown function |
| | | if (!is_object($this->address_books[$item['id']])) { |
| | | $this->address_books[$item['id']] = $item; |
| | | } |
| | | // remove from list if not writeable as requested |
| | | if ($writeable && $item['readonly']) { |
| | | unset($list[$idx]); |
| | | } |
| | | // remove from list if hidden as requested |
| | | else if ($skip_hidden && $item['hidden']) { |
| | | unset($list[$idx]); |
| | | } |
| | | } |
| | | // create user object |
| | | $this->set_user(new rcube_user($_SESSION['user_id'])); |
| | | |
| | | return $list; |
| | | } |
| | | // set task and action properties |
| | | $this->set_task(rcube_utils::get_input_value('_task', rcube_utils::INPUT_GPC)); |
| | | $this->action = asciiwords(rcube_utils::get_input_value('_action', rcube_utils::INPUT_GPC)); |
| | | |
| | | /** |
| | | * Getter for compose responses. |
| | | * These are stored in local config and user preferences. |
| | | * |
| | | * @param boolean True to sort the list alphabetically |
| | | * @param boolean True if only this user's responses shall be listed |
| | | * @return array List of the current user's stored responses |
| | | */ |
| | | public function get_compose_responses($sorted = false, $user_only = false) |
| | | { |
| | | $responses = array(); |
| | | // reset some session parameters when changing task |
| | | if ($this->task != 'utils') { |
| | | // we reset list page when switching to another task |
| | | // but only to the main task interface - empty action (#1489076) |
| | | // this will prevent from unintentional page reset on cross-task requests |
| | | if ($this->session && $_SESSION['task'] != $this->task && empty($this->action)) { |
| | | $this->session->remove('page'); |
| | | } |
| | | |
| | | if (!$user_only) { |
| | | foreach ($this->config->get('compose_responses_static', array()) as $response) { |
| | | if (empty($response['key'])) |
| | | $response['key'] = substr(md5($response['name']), 0, 16); |
| | | $response['static'] = true; |
| | | $response['class'] = 'readonly'; |
| | | $k = $sorted ? '0000-' . strtolower($response['name']) : $response['key']; |
| | | $responses[$k] = $response; |
| | | } |
| | | } |
| | | |
| | | foreach ($this->config->get('compose_responses', array()) as $response) { |
| | | if (empty($response['key'])) |
| | | $response['key'] = substr(md5($response['name']), 0, 16); |
| | | $k = $sorted ? strtolower($response['name']) : $response['key']; |
| | | $responses[$k] = $response; |
| | | } |
| | | |
| | | // sort list by name |
| | | if ($sorted) { |
| | | ksort($responses, SORT_LOCALE_STRING); |
| | | } |
| | | |
| | | return array_values($responses); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Init output object for GUI and add common scripts. |
| | | * This will instantiate a rcmail_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 |
| | | * @return rcube_output Reference to HTML output object |
| | | */ |
| | | public function load_gui($framed = false) |
| | | { |
| | | // init output page |
| | | if (!($this->output instanceof rcmail_output_html)) |
| | | $this->output = new rcmail_output_html($this->task, $framed); |
| | | |
| | | // set refresh interval |
| | | $this->output->set_env('refresh_interval', $this->config->get('refresh_interval', 0)); |
| | | $this->output->set_env('session_lifetime', $this->config->get('session_lifetime', 0) * 60); |
| | | |
| | | if ($framed) { |
| | | $this->comm_path .= '&_framed=1'; |
| | | $this->output->set_env('framed', true); |
| | | } |
| | | |
| | | $this->output->set_env('task', $this->task); |
| | | $this->output->set_env('action', $this->action); |
| | | $this->output->set_env('comm_path', $this->comm_path); |
| | | $this->output->set_charset(RCUBE_CHARSET); |
| | | |
| | | if ($this->user && $this->user->ID) |
| | | $this->output->set_env('user_id', $this->user->get_hash()); |
| | | |
| | | // add some basic labels to client |
| | | $this->output->add_label('loading', 'servererror', 'requesttimedout', 'refreshing'); |
| | | |
| | | return $this->output; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create an output object for JSON responses |
| | | * |
| | | * @return rcube_output Reference to JSON output object |
| | | */ |
| | | public function json_init() |
| | | { |
| | | if (!($this->output instanceof rcmail_output_json)) |
| | | $this->output = new rcmail_output_json($this->task); |
| | | |
| | | return $this->output; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create session object and start the session. |
| | | */ |
| | | public function session_init() |
| | | { |
| | | parent::session_init(); |
| | | |
| | | // set initial session vars |
| | | if (!$_SESSION['user_id']) |
| | | $_SESSION['temp'] = true; |
| | | |
| | | // restore skin selection after logout |
| | | if ($_SESSION['temp'] && !empty($_SESSION['skin'])) |
| | | $this->config->set('skin', $_SESSION['skin']); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Perfom login to the mail server and to the webmail service. |
| | | * This will also create a new user entry if auto_create_user is configured. |
| | | * |
| | | * @param string Mail storage (IMAP) user name |
| | | * @param string Mail storage (IMAP) password |
| | | * @param string Mail storage (IMAP) host |
| | | * @param bool Enables cookie check |
| | | * |
| | | * @return boolean True on success, False on failure |
| | | */ |
| | | function login($username, $pass, $host = null, $cookiecheck = false) |
| | | { |
| | | $this->login_error = null; |
| | | |
| | | if (empty($username)) { |
| | | return false; |
| | | } |
| | | |
| | | if ($cookiecheck && empty($_COOKIE)) { |
| | | $this->login_error = self::ERROR_COOKIES_DISABLED; |
| | | return false; |
| | | } |
| | | |
| | | $config = $this->config->all(); |
| | | |
| | | if (!$host) |
| | | $host = $config['default_host']; |
| | | |
| | | // Validate that selected host is in the list of configured hosts |
| | | if (is_array($config['default_host'])) { |
| | | $allowed = false; |
| | | foreach ($config['default_host'] as $key => $host_allowed) { |
| | | if (!is_numeric($key)) |
| | | $host_allowed = $key; |
| | | if ($host == $host_allowed) { |
| | | $allowed = true; |
| | | break; |
| | | // set current task to session |
| | | $_SESSION['task'] = $this->task; |
| | | } |
| | | } |
| | | if (!$allowed) { |
| | | $host = null; |
| | | } |
| | | } |
| | | else if (!empty($config['default_host']) && $host != rcube_utils::parse_host($config['default_host'])) { |
| | | $host = null; |
| | | |
| | | // init output class |
| | | if (!empty($_REQUEST['_remote'])) |
| | | $GLOBALS['OUTPUT'] = $this->json_init(); |
| | | else |
| | | $GLOBALS['OUTPUT'] = $this->load_gui(!empty($_REQUEST['_framed'])); |
| | | |
| | | // load plugins |
| | | $this->plugins->init($this, $this->task); |
| | | $this->plugins->load_plugins((array)$this->config->get('plugins', array()), |
| | | array('filesystem_attachments', 'jqueryui')); |
| | | } |
| | | |
| | | if (!$host) { |
| | | $this->login_error = self::ERROR_INVALID_HOST; |
| | | return false; |
| | | } |
| | | /** |
| | | * Setter for application task |
| | | * |
| | | * @param string Task to set |
| | | */ |
| | | public function set_task($task) |
| | | { |
| | | $task = asciiwords($task, true); |
| | | |
| | | // parse $host URL |
| | | $a_host = parse_url($host); |
| | | if ($a_host['host']) { |
| | | $host = $a_host['host']; |
| | | $ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null; |
| | | if (!empty($a_host['port'])) |
| | | $port = $a_host['port']; |
| | | else if ($ssl && $ssl != 'tls' && (!$config['default_port'] || $config['default_port'] == 143)) |
| | | $port = 993; |
| | | } |
| | | if ($this->user && $this->user->ID) |
| | | $task = !$task ? 'mail' : $task; |
| | | else if (php_sapi_name() == 'cli') |
| | | $task = 'cli'; |
| | | else |
| | | $task = 'login'; |
| | | |
| | | if (!$port) { |
| | | $port = $config['default_port']; |
| | | } |
| | | $this->task = $task; |
| | | $this->comm_path = $this->url(array('task' => $this->task)); |
| | | |
| | | // Check if we need to add/force domain to username |
| | | if (!empty($config['username_domain'])) { |
| | | $domain = is_array($config['username_domain']) ? $config['username_domain'][$host] : $config['username_domain']; |
| | | |
| | | if ($domain = rcube_utils::parse_host((string)$domain, $host)) { |
| | | $pos = strpos($username, '@'); |
| | | |
| | | // force configured domains |
| | | if (!empty($config['username_domain_forced']) && $pos !== false) { |
| | | $username = substr($username, 0, $pos) . '@' . $domain; |
| | | if ($this->output) { |
| | | $this->output->set_env('task', $this->task); |
| | | } |
| | | // just add domain if not specified |
| | | else if ($pos === false) { |
| | | $username .= '@' . $domain; |
| | | } |
| | | |
| | | /** |
| | | * Setter for system user object |
| | | * |
| | | * @param rcube_user Current user instance |
| | | */ |
| | | public function set_user($user) |
| | | { |
| | | parent::set_user($user); |
| | | |
| | | $lang = $this->language_prop($this->config->get('language', $_SESSION['language'])); |
| | | $_SESSION['language'] = $this->user->language = $lang; |
| | | |
| | | // set localization |
| | | 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 (version_compare(PHP_VERSION, '5.5.0', '<') && in_array($lang, array('tr_TR', 'ku', 'az_AZ'))) { |
| | | setlocale(LC_CTYPE, 'en_US.utf8', 'en_US.UTF-8'); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (!isset($config['login_lc'])) { |
| | | $config['login_lc'] = 2; // default |
| | | /** |
| | | * Return instance of the internal address book class |
| | | * |
| | | * @param string Address book identifier (-1 for default addressbook) |
| | | * @param boolean True if the address book needs to be writeable |
| | | * |
| | | * @return rcube_contacts Address book object |
| | | */ |
| | | public function get_address_book($id, $writeable = false) |
| | | { |
| | | $contacts = null; |
| | | $ldap_config = (array)$this->config->get('ldap_public'); |
| | | |
| | | // 'sql' is the alias for '0' used by autocomplete |
| | | if ($id == 'sql') |
| | | $id = '0'; |
| | | else if ($id == -1) { |
| | | $id = $this->config->get('default_addressbook'); |
| | | $default = true; |
| | | } |
| | | |
| | | // use existing instance |
| | | if (isset($this->address_books[$id]) && ($this->address_books[$id] instanceof rcube_addressbook)) { |
| | | $contacts = $this->address_books[$id]; |
| | | } |
| | | else if ($id && $ldap_config[$id]) { |
| | | $domain = $this->config->mail_domain($_SESSION['storage_host']); |
| | | $contacts = new rcube_ldap($ldap_config[$id], $this->config->get('ldap_debug'), $domain); |
| | | } |
| | | else if ($id === '0') { |
| | | $contacts = new rcube_contacts($this->db, $this->get_user_id()); |
| | | } |
| | | else { |
| | | $plugin = $this->plugins->exec_hook('addressbook_get', array('id' => $id, 'writeable' => $writeable)); |
| | | |
| | | // plugin returned instance of a rcube_addressbook |
| | | if ($plugin['instance'] instanceof rcube_addressbook) { |
| | | $contacts = $plugin['instance']; |
| | | } |
| | | } |
| | | |
| | | // when user requested default writeable addressbook |
| | | // we need to check if default is writeable, if not we |
| | | // will return first writeable book (if any exist) |
| | | if ($contacts && $default && $contacts->readonly && $writeable) { |
| | | $contacts = null; |
| | | } |
| | | |
| | | // Get first addressbook from the list if configured default doesn't exist |
| | | // This can happen when user deleted the addressbook (e.g. Kolab folder) |
| | | if (!$contacts && (!$id || $default)) { |
| | | $source = reset($this->get_address_sources($writeable, !$default)); |
| | | if (!empty($source)) { |
| | | $contacts = $this->get_address_book($source['id']); |
| | | if ($contacts) { |
| | | $id = $source['id']; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (!$contacts) { |
| | | // there's no default, just return |
| | | if ($default) { |
| | | return null; |
| | | } |
| | | |
| | | self::raise_error(array( |
| | | 'code' => 700, |
| | | 'file' => __FILE__, |
| | | 'line' => __LINE__, |
| | | 'message' => "Addressbook source ($id) not found!" |
| | | ), |
| | | true, true); |
| | | } |
| | | |
| | | // add to the 'books' array for shutdown function |
| | | $this->address_books[$id] = $contacts; |
| | | |
| | | if ($writeable && $contacts->readonly) { |
| | | return null; |
| | | } |
| | | |
| | | // set configured sort order |
| | | if ($sort_col = $this->config->get('addressbook_sort_col')) { |
| | | $contacts->set_sort_order($sort_col); |
| | | } |
| | | |
| | | return $contacts; |
| | | } |
| | | |
| | | // Convert username to lowercase. If storage backend |
| | | // is case-insensitive we need to store always the same username (#1487113) |
| | | if ($config['login_lc']) { |
| | | 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); |
| | | } |
| | | /** |
| | | * Return identifier of the address book object |
| | | * |
| | | * @param rcube_addressbook Addressbook source object |
| | | * |
| | | * @return string Source identifier |
| | | */ |
| | | public function get_address_book_id($object) |
| | | { |
| | | foreach ($this->address_books as $index => $book) { |
| | | if ($book === $object) { |
| | | return $index; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // try to resolve email address from virtuser table |
| | | if (strpos($username, '@') && ($virtuser = rcube_user::email2user($username))) { |
| | | $username = $virtuser; |
| | | /** |
| | | * Return address books list |
| | | * |
| | | * @param boolean True if the address book needs to be writeable |
| | | * @param boolean True if the address book needs to be not hidden |
| | | * |
| | | * @return array Address books array |
| | | */ |
| | | public function get_address_sources($writeable = false, $skip_hidden = false) |
| | | { |
| | | $abook_type = (string) $this->config->get('address_book_type'); |
| | | $ldap_config = (array) $this->config->get('ldap_public'); |
| | | $autocomplete = (array) $this->config->get('autocomplete_addressbooks'); |
| | | $list = array(); |
| | | |
| | | // We are using the DB address book or a plugin address book |
| | | if (!empty($abook_type) && strtolower($abook_type) != 'ldap') { |
| | | if (!isset($this->address_books['0'])) { |
| | | $this->address_books['0'] = new rcube_contacts($this->db, $this->get_user_id()); |
| | | } |
| | | |
| | | $list['0'] = array( |
| | | 'id' => '0', |
| | | 'name' => $this->gettext('personaladrbook'), |
| | | 'groups' => $this->address_books['0']->groups, |
| | | 'readonly' => $this->address_books['0']->readonly, |
| | | 'undelete' => $this->address_books['0']->undelete && $this->config->get('undo_timeout'), |
| | | 'autocomplete' => in_array('sql', $autocomplete), |
| | | ); |
| | | } |
| | | |
| | | if (!empty($ldap_config)) { |
| | | foreach ($ldap_config as $id => $prop) { |
| | | // handle misconfiguration |
| | | if (empty($prop) || !is_array($prop)) { |
| | | continue; |
| | | } |
| | | |
| | | $list[$id] = array( |
| | | 'id' => $id, |
| | | 'name' => html::quote($prop['name']), |
| | | 'groups' => !empty($prop['groups']) || !empty($prop['group_filters']), |
| | | 'readonly' => !$prop['writable'], |
| | | 'hidden' => $prop['hidden'], |
| | | 'autocomplete' => in_array($id, $autocomplete) |
| | | ); |
| | | } |
| | | } |
| | | |
| | | $plugin = $this->plugins->exec_hook('addressbooks_list', array('sources' => $list)); |
| | | $list = $plugin['sources']; |
| | | |
| | | foreach ($list as $idx => $item) { |
| | | // register source for shutdown function |
| | | if (!is_object($this->address_books[$item['id']])) { |
| | | $this->address_books[$item['id']] = $item; |
| | | } |
| | | // remove from list if not writeable as requested |
| | | if ($writeable && $item['readonly']) { |
| | | unset($list[$idx]); |
| | | } |
| | | // remove from list if hidden as requested |
| | | else if ($skip_hidden && $item['hidden']) { |
| | | unset($list[$idx]); |
| | | } |
| | | } |
| | | |
| | | return $list; |
| | | } |
| | | |
| | | // Here we need IDNA ASCII |
| | | // Only rcube_contacts class is using domain names in Unicode |
| | | $host = rcube_utils::idn_to_ascii($host); |
| | | $username = rcube_utils::idn_to_ascii($username); |
| | | /** |
| | | * Getter for compose responses. |
| | | * These are stored in local config and user preferences. |
| | | * |
| | | * @param boolean True to sort the list alphabetically |
| | | * @param boolean True if only this user's responses shall be listed |
| | | * |
| | | * @return array List of the current user's stored responses |
| | | */ |
| | | public function get_compose_responses($sorted = false, $user_only = false) |
| | | { |
| | | $responses = array(); |
| | | |
| | | // user already registered -> overwrite username |
| | | if ($user = rcube_user::query($username, $host)) { |
| | | $username = $user->data['username']; |
| | | if (!$user_only) { |
| | | foreach ($this->config->get('compose_responses_static', array()) as $response) { |
| | | if (empty($response['key'])) { |
| | | $response['key'] = substr(md5($response['name']), 0, 16); |
| | | } |
| | | |
| | | $response['static'] = true; |
| | | $response['class'] = 'readonly'; |
| | | |
| | | $k = $sorted ? '0000-' . strtolower($response['name']) : $response['key']; |
| | | $responses[$k] = $response; |
| | | } |
| | | } |
| | | |
| | | foreach ($this->config->get('compose_responses', array()) as $response) { |
| | | if (empty($response['key'])) { |
| | | $response['key'] = substr(md5($response['name']), 0, 16); |
| | | } |
| | | |
| | | $k = $sorted ? strtolower($response['name']) : $response['key']; |
| | | $responses[$k] = $response; |
| | | } |
| | | |
| | | // sort list by name |
| | | if ($sorted) { |
| | | ksort($responses, SORT_LOCALE_STRING); |
| | | } |
| | | |
| | | return array_values($responses); |
| | | } |
| | | |
| | | $storage = $this->get_storage(); |
| | | /** |
| | | * Init output object for GUI and add common scripts. |
| | | * This will instantiate a rcmail_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 |
| | | * |
| | | * @return rcube_output Reference to HTML output object |
| | | */ |
| | | public function load_gui($framed = false) |
| | | { |
| | | // init output page |
| | | if (!($this->output instanceof rcmail_output_html)) { |
| | | $this->output = new rcmail_output_html($this->task, $framed); |
| | | } |
| | | |
| | | // try to log in |
| | | if (!$storage->connect($host, $username, $pass, $port, $ssl)) { |
| | | return false; |
| | | // set refresh interval |
| | | $this->output->set_env('refresh_interval', $this->config->get('refresh_interval', 0)); |
| | | $this->output->set_env('session_lifetime', $this->config->get('session_lifetime', 0) * 60); |
| | | |
| | | if ($framed) { |
| | | $this->comm_path .= '&_framed=1'; |
| | | $this->output->set_env('framed', true); |
| | | } |
| | | |
| | | $this->output->set_env('task', $this->task); |
| | | $this->output->set_env('action', $this->action); |
| | | $this->output->set_env('comm_path', $this->comm_path); |
| | | $this->output->set_charset(RCUBE_CHARSET); |
| | | |
| | | if ($this->user && $this->user->ID) { |
| | | $this->output->set_env('user_id', $this->user->get_hash()); |
| | | } |
| | | |
| | | // add some basic labels to client |
| | | $this->output->add_label('loading', 'servererror', 'connerror', 'requesttimedout', |
| | | 'refreshing', 'windowopenerror'); |
| | | |
| | | return $this->output; |
| | | } |
| | | |
| | | // user already registered -> update user's record |
| | | if (is_object($user)) { |
| | | // update last login timestamp |
| | | $user->touch(); |
| | | } |
| | | // create new system user |
| | | else if ($config['auto_create_user']) { |
| | | if ($created = rcube_user::create($username, $host)) { |
| | | $user = $created; |
| | | } |
| | | else { |
| | | self::raise_error(array( |
| | | 'code' => 620, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Failed to create a user record. Maybe aborted by a plugin?" |
| | | ), true, false); |
| | | } |
| | | } |
| | | else { |
| | | self::raise_error(array( |
| | | 'code' => 621, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Access denied for new user $username. 'auto_create_user' is disabled" |
| | | ), true, false); |
| | | /** |
| | | * Create an output object for JSON responses |
| | | * |
| | | * @return rcube_output Reference to JSON output object |
| | | */ |
| | | public function json_init() |
| | | { |
| | | if (!($this->output instanceof rcmail_output_json)) { |
| | | $this->output = new rcmail_output_json($this->task); |
| | | } |
| | | |
| | | return $this->output; |
| | | } |
| | | |
| | | // login succeeded |
| | | if (is_object($user) && $user->ID) { |
| | | // Configure environment |
| | | $this->set_user($user); |
| | | $this->set_storage_prop(); |
| | | /** |
| | | * Create session object and start the session. |
| | | */ |
| | | public function session_init() |
| | | { |
| | | parent::session_init(); |
| | | |
| | | // fix some old settings according to namespace prefix |
| | | $this->fix_namespace_settings($user); |
| | | // set initial session vars |
| | | if (!$_SESSION['user_id']) { |
| | | $_SESSION['temp'] = true; |
| | | } |
| | | |
| | | // create default folders on first login |
| | | if ($config['create_default_folders'] && (!empty($created) || empty($user->data['last_login']))) { |
| | | $storage->create_default_folders(); |
| | | } |
| | | |
| | | // set session vars |
| | | $_SESSION['user_id'] = $user->ID; |
| | | $_SESSION['username'] = $user->data['username']; |
| | | $_SESSION['storage_host'] = $host; |
| | | $_SESSION['storage_port'] = $port; |
| | | $_SESSION['storage_ssl'] = $ssl; |
| | | $_SESSION['password'] = $this->encrypt($pass); |
| | | $_SESSION['login_time'] = time(); |
| | | |
| | | if (isset($_REQUEST['_timezone']) && $_REQUEST['_timezone'] != '_default_') |
| | | $_SESSION['timezone'] = rcube_utils::get_input_value('_timezone', rcube_utils::INPUT_GPC); |
| | | |
| | | // force reloading complete list of subscribed mailboxes |
| | | $storage->clear_cache('mailboxes', true); |
| | | |
| | | return true; |
| | | // restore skin selection after logout |
| | | if ($_SESSION['temp'] && !empty($_SESSION['skin'])) { |
| | | $this->config->set('skin', $_SESSION['skin']); |
| | | } |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | /** |
| | | * Perfom login to the mail server and to the webmail service. |
| | | * This will also create a new user entry if auto_create_user is configured. |
| | | * |
| | | * @param string Mail storage (IMAP) user name |
| | | * @param string Mail storage (IMAP) password |
| | | * @param string Mail storage (IMAP) host |
| | | * @param bool Enables cookie check |
| | | * |
| | | * @return boolean True on success, False on failure |
| | | */ |
| | | function login($username, $pass, $host = null, $cookiecheck = false) |
| | | { |
| | | $this->login_error = null; |
| | | |
| | | if (empty($username)) { |
| | | return false; |
| | | } |
| | | |
| | | if ($cookiecheck && empty($_COOKIE)) { |
| | | $this->login_error = self::ERROR_COOKIES_DISABLED; |
| | | return false; |
| | | } |
| | | |
| | | $default_host = $this->config->get('default_host'); |
| | | $default_port = $this->config->get('default_port'); |
| | | $username_domain = $this->config->get('username_domain'); |
| | | $login_lc = $this->config->get('login_lc', 2); |
| | | |
| | | // host is validated in rcmail::autoselect_host(), so here |
| | | // we'll only handle unset host (if possible) |
| | | if (!$host && !empty($default_host)) { |
| | | if (is_array($default_host)) { |
| | | list($key, $val) = each($default_host); |
| | | $host = is_numeric($key) ? $val : $key; |
| | | } |
| | | else { |
| | | $host = $default_host; |
| | | } |
| | | |
| | | $host = rcube_utils::parse_host($host); |
| | | } |
| | | |
| | | if (!$host) { |
| | | $this->login_error = self::ERROR_INVALID_HOST; |
| | | return false; |
| | | } |
| | | |
| | | // parse $host URL |
| | | $a_host = parse_url($host); |
| | | if ($a_host['host']) { |
| | | $host = $a_host['host']; |
| | | $ssl = (isset($a_host['scheme']) && in_array($a_host['scheme'], array('ssl','imaps','tls'))) ? $a_host['scheme'] : null; |
| | | |
| | | if (!empty($a_host['port'])) |
| | | $port = $a_host['port']; |
| | | else if ($ssl && $ssl != 'tls' && (!$default_port || $default_port == 143)) |
| | | $port = 993; |
| | | } |
| | | |
| | | if (!$port) { |
| | | $port = $default_port; |
| | | } |
| | | |
| | | // Check if we need to add/force domain to username |
| | | if (!empty($username_domain)) { |
| | | $domain = is_array($username_domain) ? $username_domain[$host] : $username_domain; |
| | | |
| | | if ($domain = rcube_utils::parse_host((string)$domain, $host)) { |
| | | $pos = strpos($username, '@'); |
| | | |
| | | // force configured domains |
| | | if ($pos !== false && $this->config->get('username_domain_forced')) { |
| | | $username = substr($username, 0, $pos) . '@' . $domain; |
| | | } |
| | | // just add domain if not specified |
| | | else if ($pos === false) { |
| | | $username .= '@' . $domain; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // Convert username to lowercase. If storage backend |
| | | // is case-insensitive we need to store always the same username (#1487113) |
| | | if ($login_lc) { |
| | | if ($login_lc == 2 || $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 |
| | | if (strpos($username, '@') && ($virtuser = rcube_user::email2user($username))) { |
| | | $username = $virtuser; |
| | | } |
| | | |
| | | // Here we need IDNA ASCII |
| | | // Only rcube_contacts class is using domain names in Unicode |
| | | $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)) { |
| | | $username = $user->data['username']; |
| | | } |
| | | |
| | | $storage = $this->get_storage(); |
| | | |
| | | // try to log in |
| | | if (!$storage->connect($host, $username, $pass, $port, $ssl)) { |
| | | return false; |
| | | } |
| | | |
| | | // user already registered -> update user's record |
| | | if (is_object($user)) { |
| | | // update last login timestamp |
| | | $user->touch(); |
| | | } |
| | | // create new system user |
| | | else if ($this->config->get('auto_create_user')) { |
| | | if ($created = rcube_user::create($username, $host)) { |
| | | $user = $created; |
| | | } |
| | | else { |
| | | self::raise_error(array( |
| | | 'code' => 620, |
| | | 'file' => __FILE__, |
| | | 'line' => __LINE__, |
| | | 'message' => "Failed to create a user record. Maybe aborted by a plugin?" |
| | | ), |
| | | true, false); |
| | | } |
| | | } |
| | | else { |
| | | self::raise_error(array( |
| | | 'code' => 621, |
| | | 'file' => __FILE__, |
| | | 'line' => __LINE__, |
| | | 'message' => "Access denied for new user $username. 'auto_create_user' is disabled" |
| | | ), |
| | | true, false); |
| | | } |
| | | |
| | | // login succeeded |
| | | if (is_object($user) && $user->ID) { |
| | | // Configure environment |
| | | $this->set_user($user); |
| | | $this->set_storage_prop(); |
| | | |
| | | // set session vars |
| | | $_SESSION['user_id'] = $user->ID; |
| | | $_SESSION['username'] = $user->data['username']; |
| | | $_SESSION['storage_host'] = $host; |
| | | $_SESSION['storage_port'] = $port; |
| | | $_SESSION['storage_ssl'] = $ssl; |
| | | $_SESSION['password'] = $this->encrypt($pass); |
| | | $_SESSION['login_time'] = time(); |
| | | |
| | | if (isset($_REQUEST['_timezone']) && $_REQUEST['_timezone'] != '_default_') { |
| | | $_SESSION['timezone'] = rcube_utils::get_input_value('_timezone', rcube_utils::INPUT_GPC); |
| | | } |
| | | |
| | | // fix some old settings according to namespace prefix |
| | | $this->fix_namespace_settings($user); |
| | | |
| | | // set/create special folders |
| | | $this->set_special_folders(); |
| | | |
| | | // clear all mailboxes related cache(s) |
| | | $storage->clear_cache('mailboxes', true); |
| | | |
| | | return true; |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * Returns error code of last login operation |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Auto-select IMAP host based on the posted login information |
| | | * |
| | | * @return string Selected IMAP host |
| | | */ |
| | | public function autoselect_host() |
| | | { |
| | | $default_host = $this->config->get('default_host'); |
| | | $host = null; |
| | | |
| | | /** |
| | | * Auto-select IMAP host based on the posted login information |
| | | * |
| | | * @return string Selected IMAP host |
| | | */ |
| | | public function autoselect_host() |
| | | { |
| | | $default_host = $this->config->get('default_host'); |
| | | $host = null; |
| | | if (is_array($default_host)) { |
| | | $post_host = rcube_utils::get_input_value('_host', rcube_utils::INPUT_POST); |
| | | $post_user = rcube_utils::get_input_value('_user', rcube_utils::INPUT_POST); |
| | | |
| | | if (is_array($default_host)) { |
| | | $post_host = rcube_utils::get_input_value('_host', rcube_utils::INPUT_POST); |
| | | $post_user = rcube_utils::get_input_value('_user', rcube_utils::INPUT_POST); |
| | | list(, $domain) = explode('@', $post_user); |
| | | |
| | | list(, $domain) = explode('@', $post_user); |
| | | // direct match in default_host array |
| | | if ($default_host[$post_host] || in_array($post_host, array_values($default_host))) { |
| | | $host = $post_host; |
| | | } |
| | | // try to select host by mail domain |
| | | else if (!empty($domain)) { |
| | | foreach ($default_host as $storage_host => $mail_domains) { |
| | | if (is_array($mail_domains) && in_array_nocase($domain, $mail_domains)) { |
| | | $host = $storage_host; |
| | | break; |
| | | } |
| | | else if (stripos($storage_host, $domain) !== false || stripos(strval($mail_domains), $domain) !== false) { |
| | | $host = is_numeric($storage_host) ? $mail_domains : $storage_host; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // direct match in default_host array |
| | | if ($default_host[$post_host] || in_array($post_host, array_values($default_host))) { |
| | | $host = $post_host; |
| | | } |
| | | // try to select host by mail domain |
| | | else if (!empty($domain)) { |
| | | foreach ($default_host as $storage_host => $mail_domains) { |
| | | if (is_array($mail_domains) && in_array_nocase($domain, $mail_domains)) { |
| | | $host = $storage_host; |
| | | break; |
| | | } |
| | | else if (stripos($storage_host, $domain) !== false || stripos(strval($mail_domains), $domain) !== false) { |
| | | $host = is_numeric($storage_host) ? $mail_domains : $storage_host; |
| | | break; |
| | | } |
| | | // take the first entry if $host is still not set |
| | | if (empty($host)) { |
| | | list($key, $val) = each($default_host); |
| | | $host = is_numeric($key) ? $val : $key; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // take the first entry if $host is still not set |
| | | if (empty($host)) { |
| | | list($key, $val) = each($default_host); |
| | | $host = is_numeric($key) ? $val : $key; |
| | | } |
| | | } |
| | | else if (empty($default_host)) { |
| | | $host = rcube_utils::get_input_value('_host', rcube_utils::INPUT_POST); |
| | | } |
| | | else |
| | | $host = rcube_utils::parse_host($default_host); |
| | | |
| | | return $host; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Destroy session data and remove cookie |
| | | */ |
| | | public function kill_session() |
| | | { |
| | | $this->plugins->exec_hook('session_destroy'); |
| | | |
| | | $this->session->kill(); |
| | | $_SESSION = array('language' => $this->user->language, 'temp' => true, 'skin' => $this->config->get('skin')); |
| | | $this->user->reset(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Do server side actions on logout |
| | | */ |
| | | public function logout_actions() |
| | | { |
| | | $config = $this->config->all(); |
| | | $storage = $this->get_storage(); |
| | | |
| | | if ($config['logout_purge'] && !empty($config['trash_mbox'])) { |
| | | $storage->clear_folder($config['trash_mbox']); |
| | | } |
| | | |
| | | if ($config['logout_expunge']) { |
| | | $storage->expunge_folder('INBOX'); |
| | | } |
| | | |
| | | // Try to save unsaved user preferences |
| | | if (!empty($_SESSION['preferences'])) { |
| | | $this->user->save_prefs(unserialize($_SESSION['preferences'])); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Generate a unique token to be used in a form request |
| | | * |
| | | * @return string The request token |
| | | */ |
| | | public function get_request_token() |
| | | { |
| | | $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->get_user_id() . $this->config->get('des_key') . $sess_id))); |
| | | |
| | | return $plugin['value']; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Check if the current request contains a valid token |
| | | * |
| | | * @param int Request method |
| | | * @return boolean True if request token is valid false if not |
| | | */ |
| | | public function check_request($mode = rcube_utils::INPUT_POST) |
| | | { |
| | | $token = rcube_utils::get_input_value('_token', $mode); |
| | | $sess_id = $_COOKIE[ini_get('session.name')]; |
| | | return !empty($sess_id) && $token == $this->get_request_token(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 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) |
| | | { |
| | | if (!is_array($p)) { |
| | | if (strpos($p, 'http') === 0) |
| | | return $p; |
| | | |
| | | $p = array('_action' => @func_get_arg(0)); |
| | | } |
| | | |
| | | $task = $p['_task'] ? $p['_task'] : ($p['task'] ? $p['task'] : $this->task); |
| | | $p['_task'] = $task; |
| | | unset($p['task']); |
| | | |
| | | $url = './' . $this->filename; |
| | | $delm = '?'; |
| | | foreach (array_reverse($p) as $key => $val) { |
| | | if ($val !== '' && $val !== null) { |
| | | $par = $key[0] == '_' ? $key : '_'.$key; |
| | | $url .= $delm.urlencode($par).'='.urlencode($val); |
| | | $delm = '&'; |
| | | } |
| | | } |
| | | return $url; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Function to be executed in script shutdown |
| | | */ |
| | | public function shutdown() |
| | | { |
| | | parent::shutdown(); |
| | | |
| | | foreach ($this->address_books as $book) { |
| | | if (is_object($book) && is_a($book, 'rcube_addressbook')) |
| | | $book->close(); |
| | | } |
| | | |
| | | // write performance stats to logs/console |
| | | if ($this->config->get('devel_mode')) { |
| | | if (function_exists('memory_get_usage')) |
| | | $mem = $this->show_bytes(memory_get_usage()); |
| | | if (function_exists('memory_get_peak_usage')) |
| | | $mem .= '/'.$this->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); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Registers action aliases for current task |
| | | * |
| | | * @param array $map Alias-to-filename hash array |
| | | */ |
| | | public function register_action_map($map) |
| | | { |
| | | if (is_array($map)) { |
| | | foreach ($map as $idx => $val) { |
| | | $this->action_map[$idx] = $val; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns current action filename |
| | | * |
| | | * @param array $map Alias-to-filename hash array |
| | | */ |
| | | public function get_action_file() |
| | | { |
| | | if (!empty($this->action_map[$this->action])) { |
| | | return $this->action_map[$this->action]; |
| | | } |
| | | |
| | | return strtr($this->action, '-', '_') . '.inc'; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Fixes some user preferences according to namespace handling change. |
| | | * Old Roundcube versions were using folder names with removed namespace prefix. |
| | | * Now we need to add the prefix on servers where personal namespace has prefix. |
| | | * |
| | | * @param rcube_user $user User object |
| | | */ |
| | | private function fix_namespace_settings($user) |
| | | { |
| | | $prefix = $this->storage->get_namespace('prefix'); |
| | | $prefix_len = strlen($prefix); |
| | | |
| | | if (!$prefix_len) |
| | | return; |
| | | |
| | | $prefs = $this->config->all(); |
| | | if (!empty($prefs['namespace_fixed'])) |
| | | return; |
| | | |
| | | // Build namespace prefix regexp |
| | | $ns = $this->storage->get_namespace(); |
| | | $regexp = array(); |
| | | |
| | | foreach ($ns as $entry) { |
| | | if (!empty($entry)) { |
| | | foreach ($entry as $item) { |
| | | if (strlen($item[0])) { |
| | | $regexp[] = preg_quote($item[0], '/'); |
| | | } |
| | | else if (empty($default_host)) { |
| | | $host = rcube_utils::get_input_value('_host', rcube_utils::INPUT_POST); |
| | | } |
| | | } |
| | | } |
| | | $regexp = '/^('. implode('|', $regexp).')/'; |
| | | |
| | | // Fix preferences |
| | | $opts = array('drafts_mbox', 'junk_mbox', 'sent_mbox', 'trash_mbox', 'archive_mbox'); |
| | | foreach ($opts as $opt) { |
| | | if ($value = $prefs[$opt]) { |
| | | if ($value != 'INBOX' && !preg_match($regexp, $value)) { |
| | | $prefs[$opt] = $prefix.$value; |
| | | else { |
| | | $host = rcube_utils::parse_host($default_host); |
| | | } |
| | | } |
| | | |
| | | return $host; |
| | | } |
| | | |
| | | if (!empty($prefs['default_folders'])) { |
| | | foreach ($prefs['default_folders'] as $idx => $name) { |
| | | if ($name != 'INBOX' && !preg_match($regexp, $name)) { |
| | | $prefs['default_folders'][$idx] = $prefix.$name; |
| | | /** |
| | | * Destroy session data and remove cookie |
| | | */ |
| | | public function kill_session() |
| | | { |
| | | $this->plugins->exec_hook('session_destroy'); |
| | | |
| | | $this->session->kill(); |
| | | $_SESSION = array('language' => $this->user->language, 'temp' => true, 'skin' => $this->config->get('skin')); |
| | | $this->user->reset(); |
| | | } |
| | | |
| | | /** |
| | | * Do server side actions on logout |
| | | */ |
| | | public function logout_actions() |
| | | { |
| | | $storage = $this->get_storage(); |
| | | $logout_expunge = $this->config->get('logout_expunge'); |
| | | $logout_purge = $this->config->get('logout_purge'); |
| | | $trash_mbox = $this->config->get('trash_mbox'); |
| | | |
| | | if ($logout_purge && !empty($trash_mbox)) { |
| | | $storage->clear_folder($trash_mbox); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (!empty($prefs['search_mods'])) { |
| | | $folders = array(); |
| | | foreach ($prefs['search_mods'] as $idx => $value) { |
| | | if ($idx != 'INBOX' && $idx != '*' && !preg_match($regexp, $idx)) { |
| | | $idx = $prefix.$idx; |
| | | if ($logout_expunge) { |
| | | $storage->expunge_folder('INBOX'); |
| | | } |
| | | $folders[$idx] = $value; |
| | | } |
| | | $prefs['search_mods'] = $folders; |
| | | } |
| | | |
| | | if (!empty($prefs['message_threading'])) { |
| | | $folders = array(); |
| | | foreach ($prefs['message_threading'] as $idx => $value) { |
| | | if ($idx != 'INBOX' && !preg_match($regexp, $idx)) { |
| | | $idx = $prefix.$idx; |
| | | // Try to save unsaved user preferences |
| | | if (!empty($_SESSION['preferences'])) { |
| | | $this->user->save_prefs(unserialize($_SESSION['preferences'])); |
| | | } |
| | | $folders[$prefix.$idx] = $value; |
| | | } |
| | | $prefs['message_threading'] = $folders; |
| | | } |
| | | |
| | | if (!empty($prefs['collapsed_folders'])) { |
| | | $folders = explode('&&', $prefs['collapsed_folders']); |
| | | $count = count($folders); |
| | | $folders_str = ''; |
| | | /** |
| | | * Generate a unique token to be used in a form request |
| | | * |
| | | * @return string The request token |
| | | */ |
| | | public function get_request_token() |
| | | { |
| | | $sess_id = $_COOKIE[ini_get('session.name')]; |
| | | |
| | | if ($count) { |
| | | $folders[0] = substr($folders[0], 1); |
| | | $folders[$count-1] = substr($folders[$count-1], 0, -1); |
| | | } |
| | | |
| | | foreach ($folders as $value) { |
| | | if ($value != 'INBOX' && !preg_match($regexp, $value)) { |
| | | $value = $prefix.$value; |
| | | if (!$sess_id) { |
| | | $sess_id = session_id(); |
| | | } |
| | | $folders_str .= '&'.$value.'&'; |
| | | } |
| | | $prefs['collapsed_folders'] = $folders_str; |
| | | |
| | | $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']; |
| | | } |
| | | |
| | | $prefs['namespace_fixed'] = true; |
| | | /** |
| | | * Check if the current request contains a valid token |
| | | * |
| | | * @param int Request method |
| | | * |
| | | * @return boolean True if request token is valid false if not |
| | | */ |
| | | public function check_request($mode = rcube_utils::INPUT_POST) |
| | | { |
| | | $token = rcube_utils::get_input_value('_token', $mode); |
| | | $sess_id = $_COOKIE[ini_get('session.name')]; |
| | | |
| | | // save updated preferences and reset imap settings (default folders) |
| | | $user->save_prefs($prefs); |
| | | $this->set_storage_prop(); |
| | | } |
| | | return !empty($sess_id) && $token == $this->get_request_token(); |
| | | } |
| | | |
| | | /** |
| | | * 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) |
| | | { |
| | | if (!is_array($p)) { |
| | | if (strpos($p, 'http') === 0) { |
| | | return $p; |
| | | } |
| | | |
| | | $p = array('_action' => @func_get_arg(0)); |
| | | } |
| | | |
| | | $task = $p['_task'] ? $p['_task'] : ($p['task'] ? $p['task'] : $this->task); |
| | | $p['_task'] = $task; |
| | | unset($p['task']); |
| | | |
| | | $url = './' . $this->filename; |
| | | $delm = '?'; |
| | | |
| | | foreach (array_reverse($p) as $key => $val) { |
| | | if ($val !== '' && $val !== null) { |
| | | $par = $key[0] == '_' ? $key : '_'.$key; |
| | | $url .= $delm.urlencode($par).'='.urlencode($val); |
| | | $delm = '&'; |
| | | } |
| | | } |
| | | |
| | | return $url; |
| | | } |
| | | |
| | | /** |
| | | * Function to be executed in script shutdown |
| | | */ |
| | | public function shutdown() |
| | | { |
| | | parent::shutdown(); |
| | | |
| | | foreach ($this->address_books as $book) { |
| | | if (is_object($book) && is_a($book, 'rcube_addressbook')) |
| | | $book->close(); |
| | | } |
| | | |
| | | // write performance stats to logs/console |
| | | if ($this->config->get('devel_mode') || $this->config->get('performance_stats')) { |
| | | // make sure logged numbers use unified format |
| | | setlocale(LC_NUMERIC, 'en_US.utf8', 'en_US.UTF-8', 'en_US', 'C'); |
| | | |
| | | if (function_exists('memory_get_usage')) |
| | | $mem = $this->show_bytes(memory_get_usage()); |
| | | if (function_exists('memory_get_peak_usage')) |
| | | $mem .= '/'.$this->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); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Registers action aliases for current task |
| | | * |
| | | * @param array $map Alias-to-filename hash array |
| | | */ |
| | | public function register_action_map($map) |
| | | { |
| | | if (is_array($map)) { |
| | | foreach ($map as $idx => $val) { |
| | | $this->action_map[$idx] = $val; |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Returns current action filename |
| | | * |
| | | * @param array $map Alias-to-filename hash array |
| | | */ |
| | | public function get_action_file() |
| | | { |
| | | if (!empty($this->action_map[$this->action])) { |
| | | return $this->action_map[$this->action]; |
| | | } |
| | | |
| | | return strtr($this->action, '-', '_') . '.inc'; |
| | | } |
| | | |
| | | /** |
| | | * Fixes some user preferences according to namespace handling change. |
| | | * Old Roundcube versions were using folder names with removed namespace prefix. |
| | | * Now we need to add the prefix on servers where personal namespace has prefix. |
| | | * |
| | | * @param rcube_user $user User object |
| | | */ |
| | | private function fix_namespace_settings($user) |
| | | { |
| | | $prefix = $this->storage->get_namespace('prefix'); |
| | | $prefix_len = strlen($prefix); |
| | | |
| | | if (!$prefix_len) { |
| | | return; |
| | | } |
| | | |
| | | if ($this->config->get('namespace_fixed')) { |
| | | return; |
| | | } |
| | | |
| | | $prefs = array(); |
| | | |
| | | // Build namespace prefix regexp |
| | | $ns = $this->storage->get_namespace(); |
| | | $regexp = array(); |
| | | |
| | | foreach ($ns as $entry) { |
| | | if (!empty($entry)) { |
| | | foreach ($entry as $item) { |
| | | if (strlen($item[0])) { |
| | | $regexp[] = preg_quote($item[0], '/'); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | $regexp = '/^('. implode('|', $regexp).')/'; |
| | | |
| | | // Fix preferences |
| | | $opts = array('drafts_mbox', 'junk_mbox', 'sent_mbox', 'trash_mbox', 'archive_mbox'); |
| | | foreach ($opts as $opt) { |
| | | if ($value = $this->config->get($opt)) { |
| | | if ($value != 'INBOX' && !preg_match($regexp, $value)) { |
| | | $prefs[$opt] = $prefix.$value; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (($search_mods = $this->config->get('search_mods')) && !empty($search_mods)) { |
| | | $folders = array(); |
| | | foreach ($search_mods as $idx => $value) { |
| | | if ($idx != 'INBOX' && $idx != '*' && !preg_match($regexp, $idx)) { |
| | | $idx = $prefix.$idx; |
| | | } |
| | | $folders[$idx] = $value; |
| | | } |
| | | |
| | | $prefs['search_mods'] = $folders; |
| | | } |
| | | |
| | | if (($threading = $this->config->get('message_threading')) && !empty($threading)) { |
| | | $folders = array(); |
| | | foreach ($threading as $idx => $value) { |
| | | if ($idx != 'INBOX' && !preg_match($regexp, $idx)) { |
| | | $idx = $prefix.$idx; |
| | | } |
| | | $folders[$prefix.$idx] = $value; |
| | | } |
| | | |
| | | $prefs['message_threading'] = $folders; |
| | | } |
| | | |
| | | if ($collapsed = $this->config->get('collapsed_folders')) { |
| | | $folders = explode('&&', $collapsed); |
| | | $count = count($folders); |
| | | $folders_str = ''; |
| | | |
| | | if ($count) { |
| | | $folders[0] = substr($folders[0], 1); |
| | | $folders[$count-1] = substr($folders[$count-1], 0, -1); |
| | | } |
| | | |
| | | foreach ($folders as $value) { |
| | | if ($value != 'INBOX' && !preg_match($regexp, $value)) { |
| | | $value = $prefix.$value; |
| | | } |
| | | $folders_str .= '&'.$value.'&'; |
| | | } |
| | | |
| | | $prefs['collapsed_folders'] = $folders_str; |
| | | } |
| | | |
| | | $prefs['namespace_fixed'] = true; |
| | | |
| | | // save updated preferences and reset imap settings (default folders) |
| | | $user->save_prefs($prefs); |
| | | $this->set_storage_prop(); |
| | | } |
| | | |
| | | /** |
| | | * Overwrite action variable |
| | |
| | | $this->output->set_env('action', $action); |
| | | } |
| | | |
| | | /** |
| | | * Set environment variables for specified config options |
| | | */ |
| | | public function set_env_config($options) |
| | | { |
| | | foreach ((array) $options as $option) { |
| | | if ($this->config->get($option)) { |
| | | $this->output->set_env($option, true); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Returns RFC2822 formatted current date in user's timezone |
| | |
| | | |
| | | return $date->format('r'); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Write login data (name, ID, IP address) to the 'userlogins' log file. |
| | |
| | | } |
| | | |
| | | $message = sprintf('Successful login for %s (ID: %d) from %s in session %s', |
| | | $user_name, $user_id, rcube_utils::remote_ip(), session_id()); |
| | | $user_name, $user_id, rcube_utils::remote_ip(), session_id()); |
| | | } |
| | | |
| | | // log login |
| | | self::write_log('userlogins', $message); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create a HTML table based on the given data |
| | |
| | | } |
| | | else { |
| | | foreach ($table_data as $row_data) { |
| | | $class = !empty($row_data['class']) ? $row_data['class'] : ''; |
| | | $class = !empty($row_data['class']) ? $row_data['class'] : null; |
| | | if (!empty($attrib['rowclass'])) |
| | | $class = trim($class . ' ' . $attrib['rowclass']); |
| | | $rowid = 'rcmrow' . rcube_utils::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, $this->Q(is_array($row_data[$col]) ? $row_data[$col][0] : $row_data[$col])); |
| | | $val = is_array($row_data[$col]) ? $row_data[$col][0] : $row_data[$col]; |
| | | $table->add($col, empty($attrib['ishtml']) ? $this->Q($val) : $val); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return $table->show($attrib); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Convert the given date to a human readable form |
| | |
| | | $week_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday']-6, $now_date['year']); |
| | | $pretty_date = $this->config->get('prettydate'); |
| | | |
| | | if ($pretty_date && $timestamp > $today_limit && $timestamp < $now) { |
| | | if ($pretty_date && $timestamp > $today_limit && $timestamp <= $now) { |
| | | $format = $this->config->get('date_today', $this->config->get('time_format', 'H:i')); |
| | | $today = true; |
| | | } |
| | | else if ($pretty_date && $timestamp > $week_limit && $timestamp < $now) { |
| | | else if ($pretty_date && $timestamp > $week_limit && $timestamp <= $now) { |
| | | $format = $this->config->get('date_short', 'D H:i'); |
| | | } |
| | | else { |
| | |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return folders list in HTML |
| | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return folders list as html_select object |
| | | * |
| | |
| | | */ |
| | | public function folder_selector($p = array()) |
| | | { |
| | | $p += array('maxlength' => 100, 'realnames' => false, 'is_escaped' => true); |
| | | $realnames = $this->config->get('show_real_foldernames'); |
| | | $p += array('maxlength' => 100, 'realnames' => $realnames, 'is_escaped' => true); |
| | | $a_mailboxes = array(); |
| | | $storage = $this->get_storage(); |
| | | |
| | |
| | | |
| | | $delimiter = $storage->get_hierarchy_delimiter(); |
| | | |
| | | foreach ($list as $folder) { |
| | | if (empty($p['exceptions']) || !in_array($folder, $p['exceptions'])) { |
| | | $this->build_folder_tree($a_mailboxes, $folder, $delimiter); |
| | | if (!empty($p['exceptions'])) { |
| | | $list = array_diff($list, (array) $p['exceptions']); |
| | | } |
| | | |
| | | if (!empty($p['additional'])) { |
| | | foreach ($p['additional'] as $add_folder) { |
| | | $add_items = explode($delimiter, $add_folder); |
| | | $folder = ''; |
| | | while (count($add_items)) { |
| | | $folder .= array_shift($add_items); |
| | | |
| | | // @TODO: sorting |
| | | if (!in_array($folder, $list)) { |
| | | $list[] = $folder; |
| | | } |
| | | |
| | | $folder .= $delimiter; |
| | | } |
| | | } |
| | | } |
| | | |
| | | foreach ($list as $folder) { |
| | | $this->build_folder_tree($a_mailboxes, $folder, $delimiter); |
| | | } |
| | | |
| | | $select = new html_select($p); |
| | |
| | | |
| | | return $select; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create a hierarchical array of the mailbox list |
| | |
| | | $this->build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return html for a structured list <ul> for the mailbox tree |
| | |
| | | $html_name = $this->Q($foldername) . ($unread ? html::span('unreadcount', sprintf($attrib['unreadwrap'], $unread)) : ''); |
| | | $link_attrib = $folder['virtual'] ? array() : array( |
| | | 'href' => $this->url(array('_mbox' => $folder['id'])), |
| | | 'onclick' => sprintf("return %s.command('list','%s',this)", rcmail_output::JS_OBJECT_NAME, $js_name), |
| | | 'onclick' => sprintf("return %s.command('list','%s',this,event)", rcmail_output::JS_OBJECT_NAME, $js_name), |
| | | 'rel' => $folder['id'], |
| | | 'title' => $title, |
| | | ); |
| | |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return html for a flat list <select> for the mailbox tree |
| | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return internal name for the given folder if it matches the configured special folders |
| | | */ |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Try to localize the given IMAP folder name. |
| | | * UTF-7 decode it in case no localized text was found |
| | |
| | | * |
| | | * @return string Localized folder name in UTF-8 encoding |
| | | */ |
| | | public function localize_foldername($name, $with_path = true) |
| | | public function localize_foldername($name, $with_path = false) |
| | | { |
| | | $realnames = $this->config->get('show_real_foldernames'); |
| | | |
| | | if (!$realnames && ($folder_class = $this->folder_classname($name))) { |
| | | return $this->gettext($folder_class); |
| | | } |
| | | |
| | | // try to localize path of the folder |
| | | if ($with_path && !$realnames) { |
| | |
| | | $count = count($path); |
| | | |
| | | if ($count > 1) { |
| | | for ($i = 0; $i < $count; $i++) { |
| | | for ($i = 1; $i < $count; $i++) { |
| | | $folder = implode($delimiter, array_slice($path, 0, -$i)); |
| | | if ($folder_class = $this->folder_classname($folder)) { |
| | | $name = implode($delimiter, array_slice($path, $count - $i)); |
| | |
| | | } |
| | | } |
| | | |
| | | if (!$realnames && ($folder_class = $this->folder_classname($name))) { |
| | | return $this->gettext($folder_class); |
| | | } |
| | | |
| | | return rcube_charset::convert($name, 'UTF7-IMAP'); |
| | | } |
| | | |
| | |
| | | public function localize_folderpath($path) |
| | | { |
| | | $protect_folders = $this->config->get('protect_default_folders'); |
| | | $default_folders = (array) $this->config->get('default_folders'); |
| | | $delimiter = $this->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)) { |
| | | if ($protect_folders && $this->storage->is_special_folder($directory)) { |
| | | unset($result); |
| | | $result[] = $this->localize_foldername($directory); |
| | | } |
| | |
| | | $quota_result['width'] = $attrib['width']; |
| | | } |
| | | if ($attrib['height']) { |
| | | $quota_result['height'] = $attrib['height']; |
| | | $quota_result['height'] = $attrib['height']; |
| | | } |
| | | |
| | | // build a table of quota types/roots info |
| | | if (($root_cnt = count($quota_result['all'])) > 1 || count($quota_result['all'][key($quota_result['all'])]) > 1) { |
| | | $table = new html_table(array('cols' => 3, 'class' => 'quota-info')); |
| | | |
| | | $table->add_header(null, self::Q($this->gettext('quotatype'))); |
| | | $table->add_header(null, self::Q($this->gettext('quotatotal'))); |
| | | $table->add_header(null, self::Q($this->gettext('quotaused'))); |
| | | |
| | | foreach ($quota_result['all'] as $root => $data) { |
| | | if ($root_cnt > 1 && $root) { |
| | | $table->add(array('colspan' => 3, 'class' => 'root'), self::Q($root)); |
| | | } |
| | | |
| | | if ($storage = $data['storage']) { |
| | | $percent = min(100, round(($storage['used']/max(1,$storage['total']))*100)); |
| | | |
| | | $table->add('name', self::Q($this->gettext('quotastorage'))); |
| | | $table->add(null, $this->show_bytes($storage['total'] * 1024)); |
| | | $table->add(null, sprintf('%s (%.0f%%)', $this->show_bytes($storage['used'] * 1024), $percent)); |
| | | } |
| | | if ($message = $data['message']) { |
| | | $percent = min(100, round(($message['used']/max(1,$message['total']))*100)); |
| | | |
| | | $table->add('name', self::Q($this->gettext('quotamessage'))); |
| | | $table->add(null, intval($message['total'])); |
| | | $table->add(null, sprintf('%d (%.0f%%)', $message['used'], $percent)); |
| | | } |
| | | } |
| | | |
| | | $quota_result['table'] = $table->show(); |
| | | } |
| | | } |
| | | else { |
| | |
| | | $quota_result['percent'] = 0; |
| | | } |
| | | |
| | | // cleanup |
| | | unset($quota_result['abort']); |
| | | if (empty($quota_result['table'])) { |
| | | unset($quota_result['all']); |
| | | } |
| | | |
| | | return $quota_result; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Outputs error message according to server error/response codes |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Output HTML editor scripts |
| | | * |
| | |
| | | return; |
| | | } |
| | | |
| | | $lang = strtolower($_SESSION['language']); |
| | | $lang_codes = array($_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 ($pos = strpos($_SESSION['language'], '_')) { |
| | | $lang_codes[] = substr($_SESSION['language'], 0, $pos); |
| | | } |
| | | |
| | | if (!file_exists(INSTALL_PATH . 'program/js/tiny_mce/langs/'.$lang.'.js')) { |
| | | foreach ($lang_codes as $code) { |
| | | if (file_exists(INSTALL_PATH . 'program/js/tinymce/langs/'.$code.'.js')) { |
| | | $lang = $code; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (empty($lang)) { |
| | | $lang = 'en'; |
| | | } |
| | | |
| | | $script = json_encode(array( |
| | | $config = array( |
| | | 'mode' => $mode, |
| | | 'lang' => $lang, |
| | | 'skin_path' => $this->output->get_skin_path(), |
| | | 'spellcheck' => intval($this->config->get('enable_spellcheck')), |
| | | 'spelldict' => intval($this->config->get('spellcheck_dictionary')) |
| | | )); |
| | | ); |
| | | |
| | | $this->output->include_script('tiny_mce/tiny_mce.js'); |
| | | $this->output->add_label('selectimage', 'addimage', 'selectmedia', 'addmedia'); |
| | | $this->output->set_env('editor_config', $config); |
| | | $this->output->include_css('program/js/tinymce/roundcube/browser.css'); |
| | | $this->output->include_script('tinymce/tinymce.min.js'); |
| | | $this->output->include_script('editor.js'); |
| | | $this->output->add_script("rcmail_editor_init($script)", 'docready'); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Replaces TinyMCE's emoticon images with plain-text representation |
| | |
| | | ); |
| | | |
| | | 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'; |
| | | // <img title="Cry" src="http://.../program/js/tinymce/plugins/emoticons/img/smiley-cry.gif" border="0" alt="Cry" /> |
| | | $search[] = '/<img title="[a-z ]+" src="https?:\/\/[a-z0-9_.\/-]+\/tinymce\/plugins\/emoticons\/img\/'.$file.'.gif"[^>]+\/>/i'; |
| | | $replace[] = $idx; |
| | | } |
| | | |
| | | return preg_replace($search, $replace, $html); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * File upload progress handler. |
| | | */ |
| | | public function upload_progress() |
| | | { |
| | | $prefix = ini_get('apc.rfc1867_prefix'); |
| | | $params = array( |
| | | 'action' => $this->action, |
| | | 'name' => rcube_utils::get_input_value('_progress', rcube_utils::INPUT_GET), |
| | | 'name' => rcube_utils::get_input_value('_progress', rcube_utils::INPUT_GET), |
| | | ); |
| | | |
| | | if (function_exists('apc_fetch')) { |
| | | $status = apc_fetch($prefix . $params['name']); |
| | | if (function_exists('uploadprogress_get_info')) { |
| | | $status = uploadprogress_get_info($params['name']); |
| | | |
| | | if (!empty($status)) { |
| | | $status['percent'] = round($status['current']/$status['total']*100); |
| | | $params = array_merge($status, $params); |
| | | $params['current'] = $status['bytes_uploaded']; |
| | | $params['total'] = $status['bytes_total']; |
| | | } |
| | | } |
| | | |
| | | if (isset($params['percent'])) |
| | | $params['text'] = $this->gettext(array('name' => 'uploadprogress', 'vars' => array( |
| | | 'percent' => $params['percent'] . '%', |
| | | 'current' => $this->show_bytes($params['current']), |
| | | 'total' => $this->show_bytes($params['total']) |
| | | ))); |
| | | if (!isset($status) && filter_var(ini_get('apc.rfc1867'), FILTER_VALIDATE_BOOLEAN) |
| | | && ini_get('apc.rfc1867_name') |
| | | ) { |
| | | $prefix = ini_get('apc.rfc1867_prefix'); |
| | | $status = apc_fetch($prefix . $params['name']); |
| | | |
| | | if (!empty($status)) { |
| | | $params['current'] = $status['current']; |
| | | $params['total'] = $status['total']; |
| | | } |
| | | } |
| | | |
| | | if (!isset($status) && filter_var(ini_get('session.upload_progress.enabled'), FILTER_VALIDATE_BOOLEAN) |
| | | && ini_get('session.upload_progress.name') |
| | | ) { |
| | | $key = ini_get('session.upload_progress.prefix') . $params['name']; |
| | | |
| | | $params['total'] = $_SESSION[$key]['content_length']; |
| | | $params['current'] = $_SESSION[$key]['bytes_processed']; |
| | | } |
| | | |
| | | if (!empty($params['total'])) { |
| | | $params['percent'] = round($status['current']/$status['total']*100); |
| | | $params['text'] = $this->gettext(array( |
| | | 'name' => 'uploadprogress', |
| | | 'vars' => array( |
| | | 'percent' => $params['percent'] . '%', |
| | | 'current' => $this->show_bytes($params['current']), |
| | | 'total' => $this->show_bytes($params['total']) |
| | | ) |
| | | )); |
| | | } |
| | | |
| | | $this->output->command('upload_progress_update', $params); |
| | | $this->output->send(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initializes file uploading interface. |
| | | * |
| | | * @param $int Optional maximum file size in bytes |
| | | */ |
| | | public function upload_init() |
| | | public function upload_init($max_size = null) |
| | | { |
| | | // Enable upload progress bar |
| | | $rfc1867 = filter_var(ini_get('apc.rfc1867'), FILTER_VALIDATE_BOOLEAN); |
| | | if ($rfc1867 && ($seconds = $this->config->get('upload_progress'))) { |
| | | if ($field_name = ini_get('apc.rfc1867_name')) { |
| | | if ($seconds = $this->config->get('upload_progress')) { |
| | | if (function_exists('uploadprogress_get_info')) { |
| | | $field_name = 'UPLOAD_IDENTIFIER'; |
| | | } |
| | | if (!$field_name && filter_var(ini_get('apc.rfc1867'), FILTER_VALIDATE_BOOLEAN)) { |
| | | $field_name = ini_get('apc.rfc1867_name'); |
| | | } |
| | | if (!$field_name && filter_var(ini_get('session.upload_progress.enabled'), FILTER_VALIDATE_BOOLEAN)) { |
| | | $field_name = ini_get('session.upload_progress.name'); |
| | | } |
| | | |
| | | if ($field_name) { |
| | | $this->output->set_env('upload_progress_name', $field_name); |
| | | $this->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; |
| | | } |
| | | |
| | | if ($max_size && $max_size < $max_filesize) { |
| | | $max_filesize = $max_size; |
| | | } |
| | | |
| | | $this->output->set_env('max_filesize', $max_filesize); |
| | |
| | | return $max_filesize; |
| | | } |
| | | |
| | | /** |
| | | * Outputs uploaded file content (with image thumbnails support |
| | | * |
| | | * @param array $file Upload file data |
| | | */ |
| | | public function display_uploaded_file($file) |
| | | { |
| | | if (empty($file)) { |
| | | return; |
| | | } |
| | | |
| | | $file = $this->plugins->exec_hook('attachment_display', $file); |
| | | |
| | | if ($file['status']) { |
| | | if (empty($file['size'])) { |
| | | $file['size'] = $file['data'] ? strlen($file['data']) : @filesize($file['path']); |
| | | } |
| | | |
| | | // generate image thumbnail for file browser in HTML editor |
| | | if (!empty($_GET['_thumbnail'])) { |
| | | $temp_dir = $this->config->get('temp_dir'); |
| | | $thumbnail_size = 80; |
| | | list(,$ext) = explode('/', $file['mimetype']); |
| | | $mimetype = $file['mimetype']; |
| | | $file_ident = $file['id'] . ':' . $file['mimetype'] . ':' . $file['size']; |
| | | $cache_basename = $temp_dir . '/' . md5($file_ident . ':' . $this->user->ID . ':' . $thumbnail_size); |
| | | $cache_file = $cache_basename . '.' . $ext; |
| | | |
| | | // render thumbnail image if not done yet |
| | | if (!is_file($cache_file)) { |
| | | if (!$file['path']) { |
| | | $orig_name = $filename = $cache_basename . '.orig.' . $ext; |
| | | file_put_contents($orig_name, $file['data']); |
| | | } |
| | | else { |
| | | $filename = $file['path']; |
| | | } |
| | | |
| | | $image = new rcube_image($filename); |
| | | if ($imgtype = $image->resize($thumbnail_size, $cache_file, true)) { |
| | | $mimetype = 'image/' . $imgtype; |
| | | |
| | | if ($orig_name) { |
| | | unlink($orig_name); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (is_file($cache_file)) { |
| | | // cache for 1h |
| | | $this->output->future_expire_header(3600); |
| | | header('Content-Type: ' . $mimetype); |
| | | header('Content-Length: ' . filesize($cache_file)); |
| | | |
| | | readfile($cache_file); |
| | | exit; |
| | | } |
| | | } |
| | | |
| | | header('Content-Type: ' . $file['mimetype']); |
| | | header('Content-Length: ' . $file['size']); |
| | | |
| | | if ($file['data']) { |
| | | echo $file['data']; |
| | | } |
| | | else if ($file['path']) { |
| | | readfile($file['path']); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Initializes client-side autocompletion. |
| | |
| | | $this->output->set_env('autocomplete_min_length', $this->config->get('autocomplete_min_length')); |
| | | $this->output->add_label('autocompletechars', 'autocompletemore'); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns supported font-family specifications |
| | |
| | | return $fonts; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create a human readable string for a number of bytes |
| | | * |
| | |
| | | return $str; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns real size (calculated) of the message part |
| | | * |
| | |
| | | return $size; |
| | | } |
| | | |
| | | /** |
| | | * Returns message UID(s) and IMAP folder(s) from GET/POST data |
| | | * |
| | | * @param string UID value to decode |
| | | * @param string Default mailbox value (if not encoded in UIDs) |
| | | * @return array List of message UIDs per folder |
| | | */ |
| | | public static function get_uids($uids = null, $mbox = null) |
| | | { |
| | | // message UID (or comma-separated list of IDs) is provided in |
| | | // the form of <ID>-<MBOX>[,<ID>-<MBOX>]* |
| | | |
| | | $_uid = $uids ?: rcube_utils::get_input_value('_uid', RCUBE_INPUT_GPC); |
| | | $_mbox = $mbox ?: (string)rcube_utils::get_input_value('_mbox', RCUBE_INPUT_GPC); |
| | | |
| | | // already a hash array |
| | | if (is_array($_uid) && !isset($_uid[0])) { |
| | | return $_uid; |
| | | } |
| | | |
| | | $result = array(); |
| | | |
| | | // special case: * |
| | | if ($_uid == '*' && is_object($_SESSION['search'][1]) && $_SESSION['search'][1]->multi) { |
| | | // extract the full list of UIDs per folder from the search set |
| | | foreach ($_SESSION['search'][1]->sets as $subset) { |
| | | $mbox = $subset->get_parameters('MAILBOX'); |
| | | $result[$mbox] = $subset->get(); |
| | | } |
| | | } |
| | | else { |
| | | if (is_string($_uid)) |
| | | $_uid = explode(',', $_uid); |
| | | |
| | | // create a per-folder UIDs array |
| | | foreach ((array)$_uid as $uid) { |
| | | list($uid, $mbox) = explode('-', $uid, 2); |
| | | if (!strlen($mbox)) |
| | | $mbox = $_mbox; |
| | | if ($uid == '*') |
| | | $result[$mbox] = $uid; |
| | | else |
| | | $result[$mbox][] = $uid; |
| | | } |
| | | } |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | |
| | | /************************************************************************ |
| | | ********* Deprecated methods (to be removed) ********* |