| | |
| | | protected $texts; |
| | | protected $caches = array(); |
| | | protected $shutdown_functions = array(); |
| | | protected $expunge_cache = false; |
| | | |
| | | |
| | | /** |
| | | * This implements the 'singleton' design pattern |
| | | * |
| | | * @param integer Options to initialize with this instance. See rcube::INIT_WITH_* constants |
| | | * @param string Environment name to run (e.g. live, dev, test) |
| | | * |
| | | * @return rcube The one and only instance |
| | | */ |
| | | static function get_instance($mode = 0) |
| | | static function get_instance($mode = 0, $env = '') |
| | | { |
| | | if (!self::$instance) { |
| | | self::$instance = new rcube(); |
| | | self::$instance = new rcube($env); |
| | | self::$instance->init($mode); |
| | | } |
| | | |
| | |
| | | /** |
| | | * Private constructor |
| | | */ |
| | | protected function __construct() |
| | | protected function __construct($env = '') |
| | | { |
| | | // load configuration |
| | | $this->config = new rcube_config; |
| | | $this->config = new rcube_config($env); |
| | | $this->plugins = new rcube_dummy_plugin_api; |
| | | |
| | | register_shutdown_function(array($this, 'shutdown')); |
| | |
| | | { |
| | | $shared_name = "shared_$name"; |
| | | |
| | | if (!isset($this->caches[$shared_name])) { |
| | | if (!array_key_exists($shared_name, $this->caches)) { |
| | | $opt = strtolower($name) . '_cache'; |
| | | $type = $this->config->get($opt); |
| | | $ttl = $this->config->get($opt . '_ttl'); |
| | | |
| | | if (!$type) { |
| | | $type = $this->config->get('shared_cache'); |
| | | // cache is disabled |
| | | return $this->caches[$shared_name] = null; |
| | | } |
| | | |
| | | if ($ttl === null) { |
| | | $ttl = $this->config->get('shared_cache_ttl'); |
| | | $ttl = $this->config->get('shared_cache_ttl', '10d'); |
| | | } |
| | | |
| | | $this->caches[$shared_name] = new rcube_cache_shared($type, $name, $ttl, $packed); |
| | |
| | | 'auth_pw' => $this->config->get("{$driver}_auth_pw"), |
| | | 'debug' => (bool) $this->config->get("{$driver}_debug"), |
| | | 'force_caps' => (bool) $this->config->get("{$driver}_force_caps"), |
| | | 'disabled_caps' => $this->config->get("{$driver}_disabled_caps"), |
| | | 'timeout' => (int) $this->config->get("{$driver}_timeout"), |
| | | 'skip_deleted' => (bool) $this->config->get('skip_deleted'), |
| | | 'driver' => $driver, |
| | |
| | | ini_set('session.name', $sess_name ? $sess_name : 'roundcube_sessid'); |
| | | ini_set('session.use_cookies', 1); |
| | | ini_set('session.use_only_cookies', 1); |
| | | ini_set('session.serialize_handler', 'php'); |
| | | ini_set('session.cookie_httponly', 1); |
| | | |
| | | // use database for storing session data |
| | | $this->session = new rcube_session($this->get_dbh(), $this->config); |
| | | |
| | | $this->session->register_gc_handler(array($this, 'temp_gc')); |
| | | $this->session->register_gc_handler(array($this, 'cache_gc')); |
| | | |
| | | $this->session->register_gc_handler(array($this, 'gc')); |
| | | $this->session->set_secret($this->config->get('des_key') . dirname($_SERVER['SCRIPT_NAME'])); |
| | | $this->session->set_ip_check($this->config->get('ip_check')); |
| | | |
| | | if ($this->config->get('session_auth_name')) { |
| | | $this->session->set_cookiename($this->config->get('session_auth_name')); |
| | | } |
| | | |
| | | // start PHP session (if not in CLI mode) |
| | | if ($_SERVER['REMOTE_ADDR']) { |
| | | session_start(); |
| | | $this->session->start(); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Garbage collector - cache/temp cleaner |
| | | */ |
| | | public function gc() |
| | | { |
| | | rcube_cache::gc(); |
| | | rcube_cache_shared::gc(); |
| | | $this->get_storage()->cache_gc(); |
| | | |
| | | $this->gc_temp(); |
| | | } |
| | | |
| | | |
| | |
| | | * Garbage collector function for temp files. |
| | | * Remove temp files older than two days |
| | | */ |
| | | public function temp_gc() |
| | | public function gc_temp() |
| | | { |
| | | $tmp = unslashify($this->config->get('temp_dir')); |
| | | $expire = time() - 172800; // expire in 48 hours |
| | | |
| | | // expire in 48 hours by default |
| | | $temp_dir_ttl = $this->config->get('temp_dir_ttl', '48h'); |
| | | $temp_dir_ttl = get_offset_sec($temp_dir_ttl); |
| | | if ($temp_dir_ttl < 6*3600) |
| | | $temp_dir_ttl = 6*3600; // 6 hours sensible lower bound. |
| | | |
| | | $expire = time() - $temp_dir_ttl; |
| | | |
| | | if ($tmp && ($dir = opendir($tmp))) { |
| | | while (($fname = readdir($dir)) !== false) { |
| | | if ($fname{0} == '.') { |
| | | if ($fname[0] == '.') { |
| | | continue; |
| | | } |
| | | |
| | | if (filemtime($tmp.'/'.$fname) < $expire) { |
| | | if (@filemtime($tmp.'/'.$fname) < $expire) { |
| | | @unlink($tmp.'/'.$fname); |
| | | } |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * Garbage collector for cache entries. |
| | | * Set flag to expunge caches on shutdown |
| | | * Runs garbage collector with probability based on |
| | | * session settings. This is intended for environments |
| | | * without a session. |
| | | */ |
| | | public function cache_gc() |
| | | public function gc_run() |
| | | { |
| | | // because this gc function is called before storage is initialized, |
| | | // we just set a flag to expunge storage cache on shutdown. |
| | | $this->expunge_cache = true; |
| | | $probability = (int) ini_get('session.gc_probability'); |
| | | $divisor = (int) ini_get('session.gc_divisor'); |
| | | |
| | | if ($divisor > 0 && $probability > 0) { |
| | | $random = mt_rand(1, $divisor); |
| | | if ($random <= $probability) { |
| | | $this->gc(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | /** |
| | | * Load a localization package |
| | | * |
| | | * @param string Language ID |
| | | * @param array Additional text labels/messages |
| | | * @param string $lang Language ID |
| | | * @param array $add Additional text labels/messages |
| | | * @param array $merge Additional text labels/messages to merge |
| | | */ |
| | | public function load_language($lang = null, $add = array()) |
| | | public function load_language($lang = null, $add = array(), $merge = array()) |
| | | { |
| | | $lang = $this->language_prop(($lang ? $lang : $_SESSION['language'])); |
| | | |
| | |
| | | if (is_array($add) && !empty($add)) { |
| | | $this->texts += $add; |
| | | } |
| | | |
| | | // merge additional texts (from plugin) |
| | | if (is_array($merge) && !empty($merge)) { |
| | | $this->texts = array_merge($this->texts, $merge); |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | // user HTTP_ACCEPT_LANGUAGE if no language is specified |
| | | if (empty($lang) || $lang == 'auto') { |
| | | $accept_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']); |
| | | $lang = str_replace('-', '_', $accept_langs[0]); |
| | | $lang = $accept_langs[0]; |
| | | |
| | | if (preg_match('/^([a-z]+)[_-]([a-z]+)$/i', $lang, $m)) { |
| | | $lang = $m[1] . '_' . strtoupper($m[2]); |
| | | } |
| | | } |
| | | |
| | | if (empty($rcube_languages)) { |
| | |
| | | call_user_func($function); |
| | | } |
| | | |
| | | // write session data as soon as possible and before |
| | | // closing database connection, don't do this before |
| | | // registered shutdown functions, they may need the session |
| | | // Note: this will run registered gc handlers (ie. cache gc) |
| | | if ($_SERVER['REMOTE_ADDR'] && is_object($this->session)) { |
| | | $this->session->write_close(); |
| | | } |
| | | |
| | | if (is_object($this->smtp)) { |
| | | $this->smtp->disconnect(); |
| | | } |
| | |
| | | } |
| | | |
| | | if (is_object($this->storage)) { |
| | | if ($this->expunge_cache) { |
| | | $this->storage->expunge_cache(); |
| | | } |
| | | $this->storage->close(); |
| | | } |
| | | } |
| | |
| | | // log_driver == 'file' is assumed here |
| | | |
| | | $line = sprintf("[%s]: %s\n", $date, $line); |
| | | $log_dir = self::$instance ? self::$instance->config->get('log_dir') : null; |
| | | $log_dir = null; |
| | | |
| | | // per-user logging is activated |
| | | if (self::$instance && self::$instance->config->get('per_user_logging', false) && self::$instance->get_user_id()) { |
| | | $log_dir = self::$instance->get_user_log_dir(); |
| | | if (empty($log_dir)) |
| | | return false; |
| | | } |
| | | else if (!empty($log['dir'])) { |
| | | $log_dir = $log['dir']; |
| | | } |
| | | else if (self::$instance) { |
| | | $log_dir = self::$instance->config->get('log_dir'); |
| | | } |
| | | |
| | | if (empty($log_dir)) { |
| | | $log_dir = RCUBE_INSTALL_PATH . 'logs'; |
| | |
| | | * - code: Error code (required) |
| | | * - type: Error type [php|db|imap|javascript] (required) |
| | | * - message: Error message |
| | | * - file: File where error occured |
| | | * - line: Line where error occured |
| | | * - file: File where error occurred |
| | | * - line: Line where error occurred |
| | | * @param boolean True to log the error |
| | | * @param boolean Terminate script execution |
| | | */ |
| | |
| | | // handle PHP exceptions |
| | | if (is_object($arg) && is_a($arg, 'Exception')) { |
| | | $arg = array( |
| | | 'type' => 'php', |
| | | 'code' => $arg->getCode(), |
| | | 'line' => $arg->getLine(), |
| | | 'file' => $arg->getFile(), |
| | |
| | | ); |
| | | } |
| | | else if (is_string($arg)) { |
| | | $arg = array('message' => $arg, 'type' => 'php'); |
| | | $arg = array('message' => $arg); |
| | | } |
| | | |
| | | if (empty($arg['code'])) { |
| | |
| | | |
| | | $cli = php_sapi_name() == 'cli'; |
| | | |
| | | if (($log || $terminate) && !$cli && $arg['type'] && $arg['message']) { |
| | | if (($log || $terminate) && !$cli && $arg['message']) { |
| | | $arg['fatal'] = $terminate; |
| | | self::log_bug($arg); |
| | | } |
| | |
| | | */ |
| | | public static function log_bug($arg_arr) |
| | | { |
| | | $program = strtoupper($arg_arr['type']); |
| | | $program = strtoupper(!empty($arg_arr['type']) ? $arg_arr['type'] : 'php'); |
| | | $level = self::get_instance()->config->get('debug_level'); |
| | | |
| | | // disable errors for ajax requests, write to log instead (#1487831) |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Get the per-user log directory |
| | | */ |
| | | protected function get_user_log_dir() |
| | | { |
| | | $log_dir = $this->config->get('log_dir', RCUBE_INSTALL_PATH . 'logs'); |
| | | $user_name = $this->get_user_name(); |
| | | $user_log_dir = $log_dir . '/' . $user_name; |
| | | |
| | | return !empty($user_name) && is_writable($user_log_dir) ? $user_log_dir : false; |
| | | } |
| | | |
| | | /** |
| | | * Getter for logged user language code. |
| | |
| | | 'mailto' => $mailto, |
| | | 'options' => $options, |
| | | )); |
| | | |
| | | if ($plugin['abort']) { |
| | | return isset($plugin['result']) ? $plugin['result'] : false; |
| | | } |
| | | |
| | | $from = $plugin['from']; |
| | | $mailto = $plugin['mailto']; |
| | |
| | | $subject = str_replace("\r\n", $delim, $subject); |
| | | } |
| | | |
| | | if (ini_get('safe_mode')) |
| | | if (filter_var(ini_get('safe_mode'), FILTER_VALIDATE_BOOLEAN)) |
| | | $sent = mail($to, $subject, $msg_body, $header_str); |
| | | else |
| | | $sent = mail($to, $subject, $msg_body, $header_str, "-f$from"); |
| | |
| | | !empty($response) ? join('; ', $response) : '')); |
| | | } |
| | | } |
| | | else { |
| | | // allow plugins to catch sending errors with the same parameters as in 'message_before_send' |
| | | $this->plugins->exec_hook('message_send_error', $plugin + array('error' => $error)); |
| | | } |
| | | |
| | | if (is_resource($msg_body)) { |
| | | fclose($msg_body); |