| | |
| | | | 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-2013, The Roundcube Dev Team | |
| | | | Copyright (C) 2011-2013, Kolab Systems AG | |
| | | | | |
| | | | Licensed under the GNU General Public License version 3 or | |
| | | | any later version with exceptions for skins & plugins. | |
| | |
| | | | Author: Aleksander Machniak <alec@alec.pl> | |
| | | +-----------------------------------------------------------------------+ |
| | | */ |
| | | |
| | | |
| | | /** |
| | | * Application class of Roundcube Webmail |
| | |
| | | * 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 |
| | | // init AFTER object was linked with self::$instance |
| | | self::$instance->startup(); |
| | | } |
| | | |
| | | return self::$instance; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initial startup function |
| | |
| | | $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') |
| | | if (($basename = basename($_SERVER['SCRIPT_FILENAME'])) && $basename != 'index.php') { |
| | | $this->filename = $basename; |
| | | } |
| | | |
| | | // start session |
| | | $this->session_init(); |
| | |
| | | // 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)) |
| | | if ($this->session && $_SESSION['task'] != $this->task && empty($this->action)) { |
| | | $this->session->remove('page'); |
| | | } |
| | | |
| | | // set current task to session |
| | | $_SESSION['task'] = $this->task; |
| | | } |
| | |
| | | |
| | | // load plugins |
| | | $this->plugins->init($this, $this->task); |
| | | $this->plugins->load_plugins((array)$this->config->get('plugins', array()), array('filesystem_attachments', 'jqueryui')); |
| | | $this->plugins->load_plugins((array)$this->config->get('plugins', array()), |
| | | array('filesystem_attachments', 'jqueryui')); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Setter for application task |
| | |
| | | $this->task = $task; |
| | | $this->comm_path = $this->url(array('task' => $this->task)); |
| | | |
| | | if ($this->output) |
| | | if ($this->output) { |
| | | $this->output->set_env('task', $this->task); |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * Setter for system user object |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return instance of the internal address book class |
| | | * |
| | |
| | | $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'])); |
| | | $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()); |
| | |
| | | } |
| | | |
| | | self::raise_error(array( |
| | | 'code' => 700, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Addressbook source ($id) not found!"), |
| | | 'code' => 700, |
| | | 'file' => __FILE__, |
| | | 'line' => __LINE__, |
| | | 'message' => "Addressbook source ($id) not found!" |
| | | ), |
| | | true, true); |
| | | } |
| | | |
| | |
| | | return $contacts; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return identifier of the address book object |
| | | * |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return address books list |
| | |
| | | |
| | | // 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'])) |
| | | 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'), |
| | | 'autocomplete' => in_array('sql', $autocomplete), |
| | | ); |
| | | } |
| | | |
| | |
| | | if (empty($prop) || !is_array($prop)) { |
| | | continue; |
| | | } |
| | | |
| | | $list[$id] = array( |
| | | 'id' => $id, |
| | | 'name' => html::quote($prop['name']), |
| | |
| | | * |
| | | * @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) |
| | |
| | | |
| | | if (!$user_only) { |
| | | foreach ($this->config->get('compose_responses_static', array()) as $response) { |
| | | if (empty($response['key'])) |
| | | 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'])) |
| | | if (empty($response['key'])) { |
| | | $response['key'] = substr(md5($response['name']), 0, 16); |
| | | } |
| | | |
| | | $k = $sorted ? strtolower($response['name']) : $response['key']; |
| | | $responses[$k] = $response; |
| | | } |
| | |
| | | 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)) |
| | | 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('comm_path', $this->comm_path); |
| | | $this->output->set_charset(RCUBE_CHARSET); |
| | | |
| | | if ($this->user && $this->user->ID) |
| | | 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 |
| | |
| | | */ |
| | | public function json_init() |
| | | { |
| | | if (!($this->output instanceof rcmail_output_json)) |
| | | 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. |
| | |
| | | parent::session_init(); |
| | | |
| | | // set initial session vars |
| | | if (!$_SESSION['user_id']) |
| | | if (!$_SESSION['user_id']) { |
| | | $_SESSION['temp'] = true; |
| | | |
| | | // restore skin selection after logout |
| | | if ($_SESSION['temp'] && !empty($_SESSION['skin'])) |
| | | $this->config->set('skin', $_SESSION['skin']); |
| | | } |
| | | |
| | | // 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. |
| | |
| | | |
| | | $config = $this->config->all(); |
| | | |
| | | if (!$host) |
| | | 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)) |
| | | if (!is_numeric($key)) { |
| | | $host_allowed = $key; |
| | | } |
| | | if ($host == $host_allowed) { |
| | | $allowed = true; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (!$allowed) { |
| | | $host = null; |
| | | } |
| | |
| | | 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)) |
| | |
| | | } |
| | | else { |
| | | self::raise_error(array( |
| | | 'code' => 620, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'code' => 620, |
| | | 'file' => __FILE__, |
| | | 'line' => __LINE__, |
| | | 'message' => "Failed to create a user record. Maybe aborted by a plugin?" |
| | | ), true, false); |
| | | ), |
| | | true, false); |
| | | } |
| | | } |
| | | else { |
| | | self::raise_error(array( |
| | | 'code' => 621, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'code' => 621, |
| | | 'file' => __FILE__, |
| | | 'line' => __LINE__, |
| | | 'message' => "Access denied for new user $username. 'auto_create_user' is disabled" |
| | | ), true, false); |
| | | ), |
| | | true, false); |
| | | } |
| | | |
| | | // login succeeded |
| | |
| | | $_SESSION['password'] = $this->encrypt($pass); |
| | | $_SESSION['login_time'] = time(); |
| | | |
| | | if (isset($_REQUEST['_timezone']) && $_REQUEST['_timezone'] != '_default_') |
| | | 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 false; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns error code of last login operation |
| | |
| | | return self::ERROR_STORAGE; |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Auto-select IMAP host based on the posted login information |
| | |
| | | else if (empty($default_host)) { |
| | | $host = rcube_utils::get_input_value('_host', rcube_utils::INPUT_POST); |
| | | } |
| | | else |
| | | else { |
| | | $host = rcube_utils::parse_host($default_host); |
| | | } |
| | | |
| | | return $host; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Destroy session data and remove cookie |
| | |
| | | $_SESSION = array('language' => $this->user->language, 'temp' => true, 'skin' => $this->config->get('skin')); |
| | | $this->user->reset(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Do server side actions on logout |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Generate a unique token to be used in a form request |
| | | * |
| | |
| | | public function get_request_token() |
| | | { |
| | | $sess_id = $_COOKIE[ini_get('session.name')]; |
| | | if (!$sess_id) $sess_id = session_id(); |
| | | |
| | | 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 |
| | |
| | | public function url($p) |
| | | { |
| | | if (!is_array($p)) { |
| | | if (strpos($p, 'http') === 0) |
| | | if (strpos($p, 'http') === 0) { |
| | | return $p; |
| | | } |
| | | |
| | | $p = array('_action' => @func_get_arg(0)); |
| | | } |
| | |
| | | |
| | | $url = './' . $this->filename; |
| | | $delm = '?'; |
| | | |
| | | foreach (array_reverse($p) as $key => $val) { |
| | | if ($val !== '' && $val !== null) { |
| | | $par = $key[0] == '_' ? $key : '_'.$key; |
| | |
| | | $delm = '&'; |
| | | } |
| | | } |
| | | |
| | | return $url; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Function to be executed in script shutdown |
| | |
| | | $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 |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns current action filename |
| | | * |
| | |
| | | |
| | | return strtr($this->action, '-', '_') . '.inc'; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Fixes some user preferences according to namespace handling change. |
| | |
| | | } |
| | | $folders[$idx] = $value; |
| | | } |
| | | |
| | | $prefs['search_mods'] = $folders; |
| | | } |
| | | |
| | |
| | | } |
| | | $folders[$prefix.$idx] = $value; |
| | | } |
| | | |
| | | $prefs['message_threading'] = $folders; |
| | | } |
| | | |
| | |
| | | } |
| | | $folders_str .= '&'.$value.'&'; |
| | | } |
| | | |
| | | $prefs['collapsed_folders'] = $folders_str; |
| | | } |
| | | |
| | |
| | | $user->save_prefs($prefs); |
| | | $this->set_storage_prop(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Overwrite action variable |
| | |
| | | $this->output->set_env('action', $action); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Set environment variables for specified config options |
| | | */ |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 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. |
| | |
| | | // log login |
| | | self::write_log('userlogins', $message); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Create a HTML table based on the given data |
| | |
| | | |
| | | return $table->show($attrib); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Convert the given date to a human readable form |
| | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return folders list in HTML |
| | | * |
| | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return folders list as html_select object |
| | | * |
| | |
| | | |
| | | 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 |
| | |
| | | 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. |
| | |
| | | return $quota_result; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Outputs error message according to server error/response codes |
| | | * |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Output HTML editor scripts |
| | | * |
| | |
| | | $this->output->add_script("rcmail_editor_init($script)", 'docready'); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Replaces TinyMCE's emoticon images with plain-text representation |
| | | * |
| | |
| | | return preg_replace($search, $replace, $html); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * File upload progress handler. |
| | | */ |
| | |
| | | $this->output->send(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Initializes file uploading interface. |
| | | */ |
| | |
| | | // 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; |
| | | } |
| | |
| | | |
| | | return $max_filesize; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 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 |