From 63d6e6dfc35e6d82c4a64f37c408794c163becd4 Mon Sep 17 00:00:00 2001
From: thomascube <thomas@roundcube.net>
Date: Wed, 28 Sep 2011 15:16:41 -0400
Subject: [PATCH] Bump versions to 0.6 stable
---
program/include/rcmail.php | 370 ++++++++++++++++++++++++++++++++++++++++++++--------
1 files changed, 314 insertions(+), 56 deletions(-)
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index 8c80fe2..48c709e 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -5,7 +5,7 @@
| program/include/rcmail.php |
| |
| This file is part of the Roundcube Webmail client |
- | Copyright (C) 2008-2010, The Roundcube Dev Team |
+ | Copyright (C) 2008-2011, The Roundcube Dev Team |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
@@ -64,6 +64,13 @@
public $db;
/**
+ * Instace of Memcache class.
+ *
+ * @var rcube_mdb2
+ */
+ public $memcache;
+
+ /**
* Instace of rcube_session class.
*
* @var rcube_session
@@ -115,7 +122,9 @@
private $texts;
private $address_books = array();
+ private $caches = array();
private $action_map = array();
+ private $shutdown_functions = array();
/**
@@ -310,6 +319,79 @@
return $this->db;
}
+
+
+ /**
+ * Get global handle for memcache access
+ *
+ * @return object Memcache
+ */
+ public function get_memcache()
+ {
+ if (!isset($this->memcache)) {
+ // no memcache support in PHP
+ if (!class_exists('Memcache')) {
+ $this->memcache = false;
+ return false;
+ }
+
+ $this->memcache = new Memcache;
+ $this->mc_available = 0;
+
+ // add alll configured hosts to pool
+ $pconnect = $this->config->get('memcache_pconnect', true);
+ foreach ($this->config->get('memcache_hosts', array()) as $host) {
+ list($host, $port) = explode(':', $host);
+ if (!$port) $port = 11211;
+ $this->mc_available += intval($this->memcache->addServer($host, $port, $pconnect, 1, 1, 15, false, array($this, 'memcache_failure')));
+ }
+
+ // test connection and failover (will result in $this->mc_available == 0 on complete failure)
+ $this->memcache->increment('__CONNECTIONTEST__', 1); // NOP if key doesn't exist
+
+ if (!$this->mc_available)
+ $this->memcache = false;
+ }
+
+ return $this->memcache;
+ }
+
+ /**
+ * Callback for memcache failure
+ */
+ public function memcache_failure($host, $port)
+ {
+ static $seen = array();
+
+ // only report once
+ if (!$seen["$host:$port"]++) {
+ $this->mc_available--;
+ raise_error(array('code' => 604, 'type' => 'db',
+ 'line' => __LINE__, 'file' => __FILE__,
+ 'message' => "Memcache failure on host $host:$port"),
+ true, false);
+ }
+ }
+
+
+ /**
+ * Initialize and get cache object
+ *
+ * @param string $name Cache identifier
+ * @param string $type Cache type ('db', 'apc' or 'memcache')
+ * @param int $ttl Expiration time for cache items in seconds
+ * @param bool $packed Enables/disables data serialization
+ *
+ * @return rcube_cache Cache object
+ */
+ public function get_cache($name, $type='db', $ttl=0, $packed=true)
+ {
+ if (!isset($this->caches[$name])) {
+ $this->caches[$name] = new rcube_cache($type, $_SESSION['user_id'], $name, $ttl, $packed);
+ }
+
+ return $this->caches[$name];
+ }
/**
@@ -317,22 +399,24 @@
*
* @param string Address book identifier
* @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;
+ $contacts = null;
$ldap_config = (array)$this->config->get('ldap_public');
- $abook_type = strtolower($this->config->get('address_book_type'));
+ $abook_type = strtolower($this->config->get('address_book_type'));
- $plugin = $this->plugins->exec_hook('addressbook_get', array('id' => $id, 'writeable' => $writeable));
+ // 'sql' is the alias for '0' used by autocomplete
+ if ($id == 'sql')
+ $id = '0';
- // plugin returned instance of a rcube_addressbook
- if ($plugin['instance'] instanceof rcube_addressbook) {
- $contacts = $plugin['instance'];
- }
// use existing instance
- else if (isset($this->address_books[$id]) && is_a($this->address_books[$id], 'rcube_addressbook') && (!$writeable || !$this->address_books[$id]->readonly)) {
+ if (isset($this->address_books[$id]) && is_object($this->address_books[$id])
+ && is_a($this->address_books[$id], 'rcube_addressbook')
+ && (!$writeable || !$this->address_books[$id]->readonly)
+ ) {
$contacts = $this->address_books[$id];
}
else if ($id && $ldap_config[$id]) {
@@ -341,17 +425,30 @@
else if ($id === '0') {
$contacts = new rcube_contacts($this->db, $this->user->ID);
}
- else if ($abook_type == 'ldap') {
- // Use the first writable LDAP address book.
- foreach ($ldap_config as $id => $prop) {
- if (!$writeable || $prop['writable']) {
- $contacts = new rcube_ldap($prop, $this->config->get('ldap_debug'), $this->config->mail_domain($_SESSION['imap_host']));
- break;
+ 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'];
+ }
+ // get first source from the list
+ else if (!$id) {
+ $source = reset($this->get_address_sources($writeable));
+ if (!empty($source)) {
+ $contacts = $this->get_address_book($source['id']);
+ if ($contacts)
+ $id = $source['id'];
}
}
}
- else { // $id == 'sql'
- $contacts = new rcube_contacts($this->db, $this->user->ID);
+
+ if (!$contacts) {
+ 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
@@ -366,6 +463,7 @@
* Return address books list
*
* @param boolean True if the address book needs to be writeable
+ *
* @return array Address books array
*/
public function get_address_sources($writeable = false)
@@ -380,10 +478,10 @@
if (!isset($this->address_books['0']))
$this->address_books['0'] = new rcube_contacts($this->db, $this->user->ID);
$list['0'] = array(
- 'id' => '0',
- 'name' => rcube_label('personaladrbook'),
- 'groups' => $this->address_books['0']->groups,
- 'readonly' => false,
+ 'id' => '0',
+ 'name' => rcube_label('personaladrbook'),
+ 'groups' => $this->address_books['0']->groups,
+ 'readonly' => $this->address_books['0']->readonly,
'autocomplete' => in_array('sql', $autocomplete)
);
}
@@ -392,11 +490,12 @@
$ldap_config = (array) $ldap_config;
foreach ($ldap_config as $id => $prop)
$list[$id] = array(
- 'id' => $id,
- 'name' => $prop['name'],
- 'groups' => is_array($prop['groups']),
+ 'id' => $id,
+ 'name' => $prop['name'],
+ 'groups' => is_array($prop['groups']),
'readonly' => !$prop['writable'],
- 'autocomplete' => in_array('sql', $autocomplete)
+ 'hidden' => $prop['hidden'],
+ 'autocomplete' => in_array($id, $autocomplete)
);
}
@@ -411,7 +510,7 @@
if ($writeable && $item['readonly'])
unset($list[$idx]);
}
-
+
return $list;
}
@@ -445,7 +544,7 @@
$this->output->set_env('comm_path', $this->comm_path);
$this->output->set_charset(RCMAIL_CHARSET);
- // add some basic label to client
+ // add some basic labels to client
$this->output->add_label('loading', 'servererror');
return $this->output;
@@ -492,14 +591,22 @@
if (is_object($this->imap))
return;
- $this->imap = new rcube_imap($this->db);
+ $this->imap = new rcube_imap();
$this->imap->debug_level = $this->config->get('debug_level');
$this->imap->skip_deleted = $this->config->get('skip_deleted');
// enable caching of imap data
- if ($this->config->get('enable_caching')) {
- $this->imap->set_caching(true);
+ $imap_cache = $this->config->get('imap_cache');
+ $messages_cache = $this->config->get('messages_cache');
+ // for backward compatybility
+ if ($imap_cache === null && $messages_cache === null && $this->config->get('enable_caching')) {
+ $imap_cache = 'db';
+ $messages_cache = true;
}
+ if ($imap_cache)
+ $this->imap->set_caching($imap_cache);
+ if ($messages_cache)
+ $this->imap->set_messages_caching(true);
// set pagesize from config
$this->imap->set_pagesize($this->config->get('pagesize', 50));
@@ -570,13 +677,12 @@
if (session_id())
return;
- $lifetime = $this->config->get('session_lifetime', 0) * 60;
-
// set session domain
if ($domain = $this->config->get('session_domain')) {
ini_set('session.cookie_domain', $domain);
}
// set session garbage collecting time according to session_lifetime
+ $lifetime = $this->config->get('session_lifetime', 0) * 60;
if ($lifetime) {
ini_set('session.gc_maxlifetime', $lifetime * 2);
}
@@ -588,7 +694,7 @@
ini_set('session.serialize_handler', 'php');
// use database for storing session data
- $this->session = new rcube_session($this->get_dbh(), $lifetime);
+ $this->session = new rcube_session($this->get_dbh(), $this->config);
$this->session->register_gc_handler('rcmail_temp_gc');
if ($this->config->get('enable_caching'))
@@ -736,22 +842,30 @@
// user already registered -> update user's record
if (is_object($user)) {
+ // fix some old settings according to namespace prefix
+ $this->fix_namespace_settings($user);
+
// create default folders on first login
if (!$user->data['last_login'] && $config['create_default_folders'])
$this->imap->create_default_folders();
+ // 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;
+
+ // fix default settings according to namespace prefix
+ $this->fix_namespace_settings($user);
+
// create default folders on first login
if ($config['create_default_folders'])
$this->imap->create_default_folders();
}
else {
raise_error(array(
- 'code' => 600, 'type' => 'php',
+ 'code' => 620, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Failed to create a user record. Maybe aborted by a plugin?"
), true, false);
@@ -759,9 +873,9 @@
}
else {
raise_error(array(
- 'code' => 600, 'type' => 'php',
+ 'code' => 621, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
- 'message' => "Acces denied for new user $username. 'auto_create_user' is disabled"
+ 'message' => "Access denied for new user $username. 'auto_create_user' is disabled"
), true, false);
}
@@ -783,7 +897,7 @@
$_SESSION['timezone'] = floatval($_REQUEST['_timezone']);
// force reloading complete list of subscribed mailboxes
- $this->imap->clear_cache('mailboxes');
+ $this->imap->clear_cache('mailboxes', true);
return true;
}
@@ -874,6 +988,10 @@
$nr = is_numeric($attrib['nr']) ? $attrib['nr'] : 1;
$name = $attrib['name'] ? $attrib['name'] : '';
+
+ // attrib contain text values: use them from now
+ if (($setval = $attrib[strtolower($_SESSION['language'])]) || ($setval = $attrib['en_us']))
+ $this->texts[$name] = $setval;
// check for text with domain
if ($domain && ($text_item = $this->texts[$domain.'.'.$name]))
@@ -924,6 +1042,22 @@
return $text;
}
+
+ /**
+ * Check if the given text lable exists
+ *
+ * @param string Label name
+ * @return boolean True if text exists (either in the current language or in en_US)
+ */
+ public function text_exists($name, $domain=null)
+ {
+ // load localization files if not done yet
+ if (empty($this->texts))
+ $this->load_language();
+
+ // check for text with domain first
+ return ($domain && isset($this->texts[$domain.'.'.$name])) || isset($this->texts[$name]);
+ }
/**
* Load a localization package
@@ -1035,6 +1169,11 @@
if ($config['logout_expunge']) {
$this->imap->expunge('INBOX');
}
+
+ // Try to save unsaved user preferences
+ if (!empty($_SESSION['preferences'])) {
+ $this->user->save_prefs(unserialize($_SESSION['preferences']));
+ }
}
@@ -1044,22 +1183,30 @@
*/
public function shutdown()
{
- if (is_object($this->imap))
- $this->imap->close();
+ foreach ($this->shutdown_functions as $function)
+ call_user_func($function);
if (is_object($this->smtp))
$this->smtp->disconnect();
foreach ($this->address_books as $book) {
- if (!is_object($book)) // maybe an address book instance wasn't fetched using get_address_book() yet
- $book = $this->get_address_book($book['id']);
- if (is_a($book, 'rcube_addressbook'))
+ if (is_object($book) && is_a($book, 'rcube_addressbook'))
$book->close();
}
+ foreach ($this->caches as $cache) {
+ if (is_object($cache))
+ $cache->close();
+ }
+
+ if (is_object($this->imap))
+ $this->imap->close();
+
// before closing the database connection, write session data
- if ($_SERVER['REMOTE_ADDR'])
+ if ($_SERVER['REMOTE_ADDR'] && is_object($this->session)) {
+ $this->session->cleanup();
session_write_close();
+ }
// write performance stats to logs/console
if ($this->config->get('devel_mode')) {
@@ -1078,18 +1225,29 @@
/**
+ * Registers shutdown function to be executed on shutdown.
+ * The functions will be executed before destroying any
+ * objects like smtp, imap, session, etc.
+ *
+ * @param callback Function callback
+ */
+ public function add_shutdown_function($function)
+ {
+ $this->shutdown_functions[] = $function;
+ }
+
+
+ /**
* Generate a unique token to be used in a form request
*
* @return string The request token
*/
public function get_request_token()
{
- $key = $this->task;
-
- if (!$_SESSION['request_tokens'][$key])
- $_SESSION['request_tokens'][$key] = md5(uniqid($key . mt_rand(), true));
-
- return $_SESSION['request_tokens'][$key];
+ $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->task . $this->config->get('des_key') . $sess_id)));
+ return $plugin['value'];
}
@@ -1102,7 +1260,8 @@
public function check_request($mode = RCUBE_INPUT_POST)
{
$token = get_input_value('_token', $mode);
- return !empty($token) && $_SESSION['request_tokens'][$this->task] == $token;
+ $sess_id = $_COOKIE[ini_get('session.name')];
+ return !empty($sess_id) && $token == $this->get_request_token();
}
@@ -1158,7 +1317,7 @@
mcrypt_module_close($td);
}
else {
- @include_once('lib/des.inc');
+ @include_once 'des.inc';
if (function_exists('des')) {
$des_iv_size = 8;
@@ -1210,7 +1369,7 @@
mcrypt_module_close($td);
}
else {
- @include_once('lib/des.inc');
+ @include_once 'des.inc';
if (function_exists('des')) {
$des_iv_size = 8;
@@ -1269,9 +1428,8 @@
$url = './';
$delm = '?';
- foreach (array_reverse($p) as $key => $val)
- {
- if (!empty($val)) {
+ foreach (array_reverse($p) as $key => $val) {
+ if ($val !== '') {
$par = $key[0] == '_' ? $key : '_'.$key;
$url .= $delm.urlencode($par).'='.urlencode($val);
$delm = '&';
@@ -1322,7 +1480,7 @@
$rcmail = rcmail::get_instance();
$convert = $rcmail->config->get('im_convert_path', false);
$identify = $rcmail->config->get('im_identify_path', false);
-
+
// imagemagick is required for this
if (!$convert)
return false;
@@ -1433,4 +1591,104 @@
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->imap->get_namespace('prefix');
+ $prefix_len = strlen($prefix);
+
+ if (!$prefix_len)
+ return;
+
+ $prefs = $user->get_prefs();
+ if (empty($prefs) || $prefs['namespace_fixed'])
+ return;
+
+ // Build namespace prefix regexp
+ $ns = $this->imap->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 = $prefs[$opt]) {
+ if ($value != 'INBOX' && !preg_match($regexp, $value)) {
+ $prefs[$opt] = $prefix.$value;
+ }
+ }
+ }
+
+ if (!empty($prefs['default_imap_folders'])) {
+ foreach ($prefs['default_imap_folders'] as $idx => $name) {
+ if ($name != 'INBOX' && !preg_match($regexp, $name)) {
+ $prefs['default_imap_folders'][$idx] = $prefix.$name;
+ }
+ }
+ }
+
+ 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;
+ }
+ $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;
+ }
+ $folders[$prefix.$idx] = $value;
+ }
+ $prefs['message_threading'] = $folders;
+ }
+
+ if (!empty($prefs['collapsed_folders'])) {
+ $folders = explode('&&', $prefs['collapsed_folders']);
+ $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_imap_prop();
+ }
+
}
--
Gitblit v1.9.1