Projects
Kolab:3.4:Updates
kolab-syncroton
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 17
View file
kolab-syncroton.spec
Changed
@@ -13,7 +13,7 @@ %global _ap_sysconfdir %{_sysconfdir}/%{httpd_name} Name: kolab-syncroton -Version: 2.2.1 +Version: 2.2.2 Release: 1%{?dist} Summary: ActiveSync for Kolab Groupware @@ -150,6 +150,9 @@ %attr(0770,%{httpd_user},%{httpd_group}) %{_var}/log/%{name} %changelog +* Fri Oct 18 2013 Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> - 2.2.2-1 +- New upstream version in sync with cache refactoring + * Mon Oct 14 2013 Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> - 2.2.1-1 - New upstream version 2.2.1
View file
debian.changelog
Changed
@@ -1,3 +1,9 @@ +kolab-syncroton (2.2.2-0~kolab1) unstable; urgency=low + + * Check in version 2.2.2 + + -- Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Fri, 18 Oct 2013 15:13:40 +0200 + kolab-syncroton (2.2.1-0~kolab1) unstable; urgency=low * Check in version 2.2.1
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/bootstrap.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/bootstrap.php
Changed
@@ -26,25 +26,25 @@ */ $config = array( - 'error_reporting' => E_ALL &~ (E_NOTICE | E_STRICT), + 'error_reporting' => E_ALL & ~E_NOTICE & ~E_STRICT, // Some users are not using Installer, so we'll check some // critical PHP settings here. Only these, which doesn't provide // an error/warning in the logs later. See (#1486307). 'mbstring.func_overload' => 0, - 'magic_quotes_runtime' => 0, - 'magic_quotes_sybase' => 0, // #1488506 + 'magic_quotes_runtime' => false, + 'magic_quotes_sybase' => false, // #1488506 ); // check these additional ini settings if not called via CLI if (php_sapi_name() != 'cli') { $config += array( - 'suhosin.session.encrypt' => 0, - 'file_uploads' => 1, + 'suhosin.session.encrypt' => false, + 'file_uploads' => true, ); } foreach ($config as $optname => $optval) { - $ini_optval = filter_var(ini_get($optname), FILTER_VALIDATE_BOOLEAN); + $ini_optval = filter_var(ini_get($optname), is_bool($optval) ? FILTER_VALIDATE_BOOLEAN : FILTER_VALIDATE_INT); if ($optval != $ini_optval && @ini_set($optname, $optval) === false) { $error = "ERROR: Wrong '$optname' option value and it wasn't possible to set it to required value ($optval).\n" . "Check your PHP configuration (including php_admin_flag)."; @@ -58,7 +58,7 @@ define('RCUBE_CHARSET', 'UTF-8'); if (!defined('RCUBE_LIB_DIR')) { - define('RCUBE_LIB_DIR', dirname(__FILE__).'/'); + define('RCUBE_LIB_DIR', dirname(__FILE__).DIRECTORY_SEPARATOR); } if (!defined('RCUBE_INSTALL_PATH')) { @@ -83,6 +83,16 @@ @mb_regex_encoding(RCUBE_CHARSET); } +// make sure the Roundcube lib directory is in the include_path +$rcube_path = realpath(RCUBE_LIB_DIR . '..'); +$sep = PATH_SEPARATOR; +$regexp = "!(^|$sep)" . preg_quote($rcube_path, '!') . "($sep|\$)!"; +$path = ini_get('include_path'); + +if (!preg_match($regexp, $path)) { + set_include_path($path . PATH_SEPARATOR . $rcube_path); +} + // Register autoloader spl_autoload_register('rcube_autoload');
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/html.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/html.php
Changed
@@ -604,16 +604,17 @@ * * @param mixed $names Option name or array with option names * @param mixed $values Option value or array with option values + * @param array $attrib Additional attributes for the option entry */ - public function add($names, $values = null) + public function add($names, $values = null, $attrib = array()) { if (is_array($names)) { foreach ($names as $i => $text) { - $this->options[] = array('text' => $text, 'value' => $values[$i]); + $this->options[] = array('text' => $text, 'value' => $values[$i]) + $attrib; } } else { - $this->options[] = array('text' => $names, 'value' => $values); + $this->options[] = array('text' => $names, 'value' => $values) + $attrib; } } @@ -644,7 +645,7 @@ $option_content = self::quote($option_content); } - $this->content .= self::tag('option', $attr, $option_content); + $this->content .= self::tag('option', $attr + $option, $option_content, array('value','label','class','style','title','disabled','selected')); } return parent::show();
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube.php
Changed
@@ -467,6 +467,10 @@ $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']) { $this->session->start(); @@ -494,7 +498,14 @@ 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) { @@ -691,7 +702,11 @@ // 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)) { @@ -1121,8 +1136,8 @@ * - 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 */ @@ -1393,6 +1408,10 @@ 'options' => $options, )); + if ($plugin['abort']) { + return isset($plugin['result']) ? $plugin['result'] : false; + } + $from = $plugin['from']; $mailto = $plugin['mailto']; $options = $plugin['options'];
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_addressbook.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_addressbook.php
Changed
@@ -3,7 +3,7 @@ /* +-----------------------------------------------------------------------+ | This file is part of the Roundcube Webmail client | - | Copyright (C) 2006-2012, The Roundcube Dev Team | + | Copyright (C) 2006-2013, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -35,6 +35,7 @@ /** public properties (mandatory) */ public $primary_key; public $groups = false; + public $export_groups = true; public $readonly = true; public $searchonly = false; public $undelete = false; @@ -133,7 +134,7 @@ abstract function get_record($id, $assoc=false); /** - * Returns the last error occured (e.g. when updating/inserting failed) + * Returns the last error occurred (e.g. when updating/inserting failed) * * @return array Hash array with the following fields: type, message */ @@ -423,7 +424,7 @@ * @param boolean True to return one array with all values, False for hash array with values grouped by type * @return array List of column values */ - function get_col_values($col, $data, $flat = false) + public static function get_col_values($col, $data, $flat = false) { $out = array(); foreach ((array)$data as $c => $values) { @@ -476,7 +477,8 @@ $fn = trim(join(' ', array_filter(array($contact['prefix'], $contact['firstname'], $contact['middlename'], $contact['surname'], $contact['suffix'])))); // use email address part for name - $email = is_array($contact['email']) ? $contact['email'][0] : $contact['email']; + $email = self::get_col_values('email', $contact, true); + $email = $email[0]; if ($email && (empty($fn) || $fn == $email)) { // return full email @@ -523,9 +525,9 @@ $fn = $contact['name']; // fallback to email address - $email = is_array($contact['email']) ? $contact['email'][0] : $contact['email']; - if (empty($fn) && $email) - return $email; + if (empty($fn) && ($email = self::get_col_values('email', $contact, true)) && !empty($email)) { + return $email[0]; + } return $fn; } @@ -538,8 +540,8 @@ $key = $contact[$sort_col] . ':' . $contact['sourceid']; // add email to a key to not skip contacts with the same name (#1488375) - if (!empty($contact['email'])) { - $key .= ':' . implode(':', (array)$contact['email']); + if (($email = self::get_col_values('email', $contact, true)) && !empty($email)) { + $key .= ':' . implode(':', (array)$email); } return $key; @@ -561,9 +563,9 @@ // use only strict comparison (mode = 1) // @TODO: partial search, e.g. match only day and month if (in_array($colname, $this->date_cols)) { - return (($value = rcube_utils::strtotime($value)) - && ($search = rcube_utils::strtotime($search)) - && date('Ymd', $value) == date('Ymd', $search)); + return (($value = rcube_utils::anytodatetime($value)) + && ($search = rcube_utils::anytodatetime($search)) + && $value->format('Ymd') == $search->format('Ymd')); } // composite field, e.g. address
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_base_replacer.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_base_replacer.php
Changed
@@ -90,8 +90,8 @@ if (preg_match_all('/\.\.\//', $path, $matches, PREG_SET_ORDER)) { foreach ($matches as $a_match) { - if (strrpos($base_url, '/')) { - $base_url = substr($base_url, 0, strrpos($base_url, '/')); + if ($pos = strrpos($base_url, '/')) { + $base_url = substr($base_url, 0, $pos); } $path = substr($path, 3); }
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_config.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_config.php
Changed
@@ -3,7 +3,7 @@ /* +-----------------------------------------------------------------------+ | This file is part of the Roundcube Webmail client | - | Copyright (C) 2008-2012, The Roundcube Dev Team | + | Copyright (C) 2008-2013, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -27,7 +27,7 @@ const DEFAULT_SKIN = 'larry'; private $env = ''; - private $basedir = 'config/'; + private $paths = array(); private $prop = array(); private $errors = array(); private $userprefs = array(); @@ -58,7 +58,32 @@ public function __construct($env = '') { $this->env = $env; - $this->basedir = RCUBE_CONFIG_DIR; + + if ($paths = getenv('RCUBE_CONFIG_PATH')) { + $this->paths = explode(PATH_SEPARATOR, $paths); + // make all paths absolute + foreach ($this->paths as $i => $path) { + if (!$this->_is_absolute($path)) { + if ($realpath = realpath(RCUBE_INSTALL_PATH . $path)) { + $this->paths[$i] = unslashify($realpath) . '/'; + } + else { + unset($this->paths[$i]); + } + } + else { + $this->paths[$i] = unslashify($path) . '/'; + } + } + } + + if (defined('RCUBE_CONFIG_DIR') && !in_array(RCUBE_CONFIG_DIR, $this->paths)) { + $this->paths[] = RCUBE_CONFIG_DIR; + } + + if (empty($this->paths)) { + $this->paths[] = RCUBE_INSTALL_PATH . 'config/'; + } $this->load(); @@ -94,8 +119,7 @@ } // load host-specific configuration - if (!empty($_SERVER['HTTP_HOST'])) - $this->load_host_config(); + $this->load_host_config(); // set skin (with fallback to old 'skin_path' property) if (empty($this->prop['skin'])) { @@ -138,17 +162,6 @@ // enable display_errors in 'show' level, but not for ajax requests ini_set('display_errors', intval(empty($_REQUEST['_remote']) && ($this->prop['debug_level'] & 4))); - // set timezone auto settings values - if ($this->prop['timezone'] == 'auto') { - $this->prop['_timezone_value'] = $this->client_timezone(); - } - else if (is_numeric($this->prop['timezone']) && ($tz = timezone_name_from_abbr("", $this->prop['timezone'] * 3600, 0))) { - $this->prop['timezone'] = $tz; - } - else if (empty($this->prop['timezone'])) { - $this->prop['timezone'] = 'UTC'; - } - // remove deprecated properties unset($this->prop['dst_active']); @@ -162,21 +175,31 @@ */ private function load_host_config() { - $fname = null; - - if (is_array($this->prop['include_host_config'])) { - $fname = $this->prop['include_host_config'][$_SERVER['HTTP_HOST']]; - } - else if (!empty($this->prop['include_host_config'])) { - $fname = preg_replace('/[^a-z0-9\.\-_]/i', '', $_SERVER['HTTP_HOST']) . '.inc.php'; + if (empty($this->prop['include_host_config'])) { + return; } - if ($fname) { - $this->load_from_file($fname); + foreach (array('HTTP_HOST', 'SERVER_NAME', 'SERVER_ADDR') as $key) { + $fname = null; + $name = $_SERVER[$key]; + + if (!$name) { + continue; + } + + if (is_array($this->prop['include_host_config'])) { + $fname = $this->prop['include_host_config'][$name]; + } + else { + $fname = preg_replace('/[^a-z0-9\.\-_]/i', '', $name) . '.inc.php'; + } + + if ($fname && $this->load_from_file($fname)) { + return; + } } } - /** * Read configuration from a file * and merge with the already stored config values @@ -186,51 +209,73 @@ */ public function load_from_file($file) { - $fpath = $this->resolve_path($file); - if ($fpath && (is_file($fpath) || file_exists($fpath)) && is_readable($fpath)) { - // use output buffering, we don't need any output here - ob_start(); - include($fpath); - ob_end_clean(); - - if (is_array($config)) { - $this->merge($config); - return true; - } - // deprecated name of config variable - else if (is_array($rcmail_config)) { - $this->merge($rcmail_config); - return true; + $success = false; + + foreach ($this->resolve_paths($file) as $fpath) { + if ($fpath && is_file($fpath) && is_readable($fpath)) { + // use output buffering, we don't need any output here + ob_start(); + include($fpath); + ob_end_clean(); + + if (is_array($config)) { + $this->merge($config); + $success = true; + } + // deprecated name of config variable + if (is_array($rcmail_config)) { + $this->merge($rcmail_config); + $success = true; + } } } - return false; + return $success; } /** - * Helper method to resolve the absolute path to the given config file. + * Helper method to resolve absolute paths to the given config file. * This also takes the 'env' property into account. + * + * @param string Filename or absolute file path + * @param boolean Return -$env file path if exists + * @return array List of candidates in config dir path(s) */ - public function resolve_path($file, $use_env = true) + public function resolve_paths($file, $use_env = true) { - if (strpos($file, '/') === false) { - $file = rtrim($this->basedir, '/') . '/' . $file; + $files = array(); + $abs_path = $this->_is_absolute($file); - if (!realpath($file) === false) { - $file = realpath($file); + foreach ($this->paths as $basepath) { + $realpath = $abs_path ? $file : realpath($basepath . '/' . $file); + + // check if <file>-env.ini exists + if ($realpath && $use_env && !empty($this->env)) { + $envfile = preg_replace('/\.(inc.php)$/', '-' . $this->env . '.\\1', $realpath); + if (is_file($envfile)) + $realpath = $envfile; } - } - // check if <file>-env.ini exists - if ($file && $use_env && !empty($this->env)) { - $envfile = preg_replace('/\.(inc.php)$/', '-' . $this->env . '.\\1', $file); - if (is_file($envfile)) - return $envfile; + if ($realpath) { + $files[] = $realpath; + + // no need to continue the loop if an absolute file path is given + if ($abs_path) { + break; + } + } } - return $file; + return $files; } + /** + * Determine whether the given file path is absolute or relative + */ + private function _is_absolute($path) + { + return $path[0] == DIRECTORY_SEPARATOR || preg_match('!^[a-z]:[\\\\/]!i', $path); + } /** * Getter for a specific config parameter @@ -250,8 +295,10 @@ $rcube = rcube::get_instance(); - if ($name == 'timezone' && isset($this->prop['_timezone_value'])) { - $result = $this->prop['_timezone_value']; + if ($name == 'timezone') { + if (empty($result) || $result == 'auto') { + $result = $this->client_timezone(); + } } else if ($name == 'client_mimetypes') { if ($result == null && $def == null) @@ -286,8 +333,8 @@ */ public function merge($prefs) { + $prefs = $this->fix_legacy_props($prefs); $this->prop = array_merge($this->prop, $prefs, $this->userprefs); - $this->fix_legacy_props(); } @@ -299,6 +346,8 @@ */ public function set_user_prefs($prefs) { + $prefs = $this->fix_legacy_props($prefs); + // Honor the dont_override setting for any existing user preferences $dont_override = $this->get('dont_override'); if (is_array($dont_override) && !empty($dont_override)) { @@ -307,11 +356,6 @@ } } - // convert user's timezone into the new format - if (is_numeric($prefs['timezone']) && ($tz = timezone_name_from_abbr('', $prefs['timezone'] * 3600, 0))) { - $prefs['timezone'] = $tz; - } - // larry is the new default skin :-) if ($prefs['skin'] == 'default') { $prefs['skin'] = self::DEFAULT_SKIN; @@ -319,22 +363,13 @@ $this->userprefs = $prefs; $this->prop = array_merge($this->prop, $prefs); - - $this->fix_legacy_props(); - - // override timezone settings with client values - if ($this->prop['timezone'] == 'auto') { - $this->prop['_timezone_value'] = isset($_SESSION['timezone']) ? $this->client_timezone() : $this->prop['_timezone_value']; - } - else if (isset($this->prop['_timezone_value'])) - unset($this->prop['_timezone_value']); } /** * Getter for all config options * - * @return array Hash array containg all config properties + * @return array Hash array containing all config properties */ public function all() { @@ -468,13 +503,12 @@ */ private function client_timezone() { - if (isset($_SESSION['timezone']) && is_numeric($_SESSION['timezone']) - && ($ctz = timezone_name_from_abbr("", $_SESSION['timezone'] * 3600, 0))) { - return $ctz; - } - else if (!empty($_SESSION['timezone'])) { + // @TODO: remove this legacy timezone handling in the future + $props = $this->fix_legacy_props(array('timezone' => $_SESSION['timezone'])); + + if (!empty($props['timezone'])) { try { - $tz = timezone_open($_SESSION['timezone']); + $tz = new DateTimeZone($props['timezone']); return $tz->getName(); } catch (Exception $e) { /* gracefully ignore */ } @@ -486,16 +520,93 @@ /** * Convert legacy options into new ones + * + * @param array $props Hash array with config props + * + * @return array Converted config props */ - private function fix_legacy_props() + private function fix_legacy_props($props) { foreach ($this->legacy_props as $new => $old) { - if (isset($this->prop[$old])) { - if (!isset($this->prop[$new])) { - $this->prop[$new] = $this->prop[$old]; + if (isset($props[$old])) { + if (!isset($props[$new])) { + $props[$new] = $props[$old]; } - unset($this->prop[$old]); + unset($props[$old]); + } + } + + // convert deprecated numeric timezone value + if (isset($props['timezone']) && is_numeric($props['timezone'])) { + if ($tz = self::timezone_name_from_abbr($props['timezone'])) { + $props['timezone'] = $tz; + } + else { + unset($props['timezone']); } } + + return $props; + } + + /** + * timezone_name_from_abbr() replacement. Converts timezone offset + * into timezone name abbreviation. + * + * @param float $offset Timezone offset (in hours) + * + * @return string Timezone abbreviation + */ + static public function timezone_name_from_abbr($offset) + { + // List of timezones here is not complete - https://bugs.php.net/bug.php?id=44780 + if ($tz = timezone_name_from_abbr('', $offset * 3600, 0)) { + return $tz; + } + + // try with more complete list (#1489261) + $timezones = array( + '-660' => "Pacific/Apia", + '-600' => "Pacific/Honolulu", + '-570' => "Pacific/Marquesas", + '-540' => "America/Anchorage", + '-480' => "America/Los_Angeles", + '-420' => "America/Denver", + '-360' => "America/Chicago", + '-300' => "America/New_York", + '-270' => "America/Caracas", + '-240' => "America/Halifax", + '-210' => "Canada/Newfoundland", + '-180' => "America/Sao_Paulo", + '-60' => "Atlantic/Azores", + '0' => "Europe/London", + '60' => "Europe/Paris", + '120' => "Europe/Helsinki", + '180' => "Europe/Moscow", + '210' => "Asia/Tehran", + '240' => "Asia/Dubai", + '300' => "Asia/Karachi", + '270' => "Asia/Kabul", + '300' => "Asia/Karachi", + '330' => "Asia/Kolkata", + '345' => "Asia/Katmandu", + '360' => "Asia/Yekaterinburg", + '390' => "Asia/Rangoon", + '420' => "Asia/Krasnoyarsk", + '480' => "Asia/Shanghai", + '525' => "Australia/Eucla", + '540' => "Asia/Tokyo", + '570' => "Australia/Adelaide", + '600' => "Australia/Melbourne", + '630' => "Australia/Lord_Howe", + '660' => "Asia/Vladivostok", + '690' => "Pacific/Norfolk", + '720' => "Pacific/Auckland", + '765' => "Pacific/Chatham", + '780' => "Pacific/Enderbury", + '840' => "Pacific/Kiritimati", + ); + + return $timezones[(string) intval($offset * 60)]; } }
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_contacts.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_contacts.php
Changed
@@ -718,6 +718,10 @@ foreach ($save_data as $key => $values) { list($field, $section) = explode(':', $key); $fulltext = in_array($field, $this->fulltext_cols); + // avoid casting DateTime objects to array + if (is_object($values) && is_a($values, 'DateTime')) { + $values = array(0 => $values); + } foreach ((array)$values as $value) { if (isset($value)) $vcard->set($field, $value, $section);
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_csv2vcard.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_csv2vcard.php
Changed
@@ -145,6 +145,7 @@ 'work_mobile' => 'phone:work,cell', 'work_title' => 'jobtitle', 'work_zip' => 'zipcode:work', + 'group' => 'groups', ); /** @@ -268,6 +269,7 @@ 'work_mobile' => "Work Mobile", 'work_title' => "Work Title", 'work_zip' => "Work Zip", + 'groups' => "Group", ); protected $local_label_map = array();
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_db.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_db.php
Changed
@@ -31,7 +31,10 @@ protected $db_dsnr; // DSN for read operations protected $db_connected = false; // Already connected ? protected $db_mode; // Connection mode + protected $db_table_dsn_map = array(); protected $dbh; // Connection handle + protected $dbhs = array(); + protected $table_connections = array(); protected $db_error = false; protected $db_error_msg = ''; @@ -97,9 +100,12 @@ $this->db_dsnw = $db_dsnw; $this->db_dsnr = $db_dsnr; $this->db_pconn = $pconn; + $this->db_dsnw_noread = rcube::get_instance()->config->get('db_dsnw_noread', false); $this->db_dsnw_array = self::parse_dsn($db_dsnw); $this->db_dsnr_array = self::parse_dsn($db_dsnr); + + $this->db_table_dsn_map = array_map(array($this, 'table_name'), rcube::get_instance()->config->get('db_table_dsn', array())); } /** @@ -113,6 +119,13 @@ $this->db_error = false; $this->db_error_msg = null; + // return existing handle + if ($this->dbhs[$mode]) { + $this->dbh = $this->dbhs[$mode]; + $this->db_mode = $mode; + return $this->dbh; + } + // Get database specific connection options $dsn_string = $this->dsn_string($dsn); $dsn_options = $this->dsn_options($dsn); @@ -147,6 +160,7 @@ } $this->dbh = $dbh; + $this->dbhs[$mode] = $dbh; $this->db_mode = $mode; $this->db_connected = true; $this->conn_configure($dsn, $dbh); @@ -175,8 +189,9 @@ * Connect to appropriate database depending on the operation * * @param string $mode Connection mode (r|w) + * @param boolean $force Enforce using the given mode */ - public function db_connect($mode) + public function db_connect($mode, $force = false) { // previous connection failed, don't attempt to connect again if ($this->conn_failure) { @@ -190,14 +205,13 @@ // Already connected if ($this->db_connected) { - // connected to db with the same or "higher" mode - if ($this->db_mode == 'w' || $this->db_mode == $mode) { + // connected to db with the same or "higher" mode (if allowed) + if ($this->db_mode == $mode || $this->db_mode == 'w' && !$force && !$this->db_dsnw_noread) { return; } } $dsn = ($mode == 'r') ? $this->db_dsnr_array : $this->db_dsnw_array; - $this->dsn_connect($dsn, $mode); // use write-master when read-only fails @@ -209,6 +223,46 @@ } /** + * Analyze the given SQL statement and select the appropriate connection to use + */ + protected function dsn_select($query) + { + // no replication + if ($this->db_dsnw == $this->db_dsnr) { + return 'w'; + } + + // Read or write ? + $mode = preg_match('/^(select|show|set)/i', $query) ? 'r' : 'w'; + + // find tables involved in this query + if (preg_match_all('/(?:^|\s)(from|update|into|join)\s+'.$this->options['identifier_start'].'?([a-z0-9._]+)'.$this->options['identifier_end'].'?\s+/i', $query, $matches, PREG_SET_ORDER)) { + foreach ($matches as $m) { + $table = $m[2]; + + // always use direct mapping + if ($this->db_table_dsn_map[$table]) { + $mode = $this->db_table_dsn_map[$table]; + break; // primary table rules + } + else if ($mode == 'r') { + // connected to db with the same or "higher" mode for this table + $db_mode = $this->table_connections[$table]; + if ($db_mode == 'w' && !$this->db_dsnw_noread) { + $mode = $db_mode; + } + } + } + + // remember mode chosen (for primary table) + $table = $matches[0][2]; + $this->table_connections[$table] = $mode; + } + + return $mode; + } + + /** * Activate/deactivate debug mode * * @param boolean $dbg True if SQL queries should be logged @@ -340,10 +394,7 @@ { $query = trim($query); - // Read or write ? - $mode = preg_match('/^(select|show|set)/i', $query) ? 'r' : 'w'; - - $this->db_connect($mode); + $this->db_connect($this->dsn_select($query), true); // check connection before proceeding if (!$this->is_connected()) { @@ -386,17 +437,7 @@ $result = $this->dbh->query($query); if ($result === false) { - $error = $this->dbh->errorInfo(); - - if (empty($this->options['ignore_key_errors']) || $error[0] != '23000') { - $this->db_error = true; - $this->db_error_msg = sprintf('[%s] %s', $error[1], $error[2]); - - rcube::raise_error(array('code' => 500, 'type' => 'db', - 'line' => __LINE__, 'file' => __FILE__, - 'message' => $this->db_error_msg . " (SQL Query: $query)" - ), true, false); - } + $result = $this->handle_error($query); } $this->last_result = $result; @@ -405,6 +446,30 @@ } /** + * Helper method to handle DB errors. + * This by default logs the error but could be overriden by a driver implementation + * + * @param string Query that triggered the error + * @return mixed Result to be stored and returned + */ + protected function handle_error($query) + { + $error = $this->dbh->errorInfo(); + + if (empty($this->options['ignore_key_errors']) || !in_array($error[0], array('23000', '23505'))) { + $this->db_error = true; + $this->db_error_msg = sprintf('[%s] %s', $error[1], $error[2]); + + rcube::raise_error(array('code' => 500, 'type' => 'db', + 'line' => __LINE__, 'file' => __FILE__, + 'message' => $this->db_error_msg . " (SQL Query: $query)" + ), true, false); + } + + return false; + } + + /** * Get number of affected rows for the last query * * @param mixed $result Optional query handle @@ -854,10 +919,14 @@ */ public function table_name($table) { - $rcube = rcube::get_instance(); + static $rcube; + + if (!$rcube) { + $rcube = rcube::get_instance(); + } // add prefix to the table name if configured - if ($prefix = $rcube->config->get('db_prefix')) { + if (($prefix = $rcube->config->get('db_prefix')) && strpos($table, $prefix) !== 0) { return $prefix . $table; } @@ -876,6 +945,17 @@ } /** + * Set DSN connection to be used for the given table + * + * @param string Table name + * @param string DSN connection ('r' or 'w') to be used + */ + public function set_table_dsn($table, $mode) + { + $this->db_table_dsn_map[$this->table_name($table)] = $mode; + } + + /** * MDB2 DSN string parser * * @param string $sequence Secuence name
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_db_mssql.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_db_mssql.php
Changed
@@ -52,7 +52,7 @@ protected function conn_configure($dsn, $dbh) { // Set date format in case of non-default language (#1488918) - $this->query("SET DATEFORMAT ymd"); + $dbh->query("SET DATEFORMAT ymd"); } /**
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_db_mysql.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_db_mysql.php
Changed
@@ -60,7 +60,7 @@ */ protected function conn_configure($dsn, $dbh) { - $this->query("SET NAMES 'utf8'"); + $dbh->query("SET NAMES 'utf8'"); } /** @@ -179,4 +179,29 @@ return isset($this->variables[$varname]) ? $this->variables[$varname] : $default; } + /** + * Handle DB errors, re-issue the query on deadlock errors from InnoDB row-level locking + * + * @param string Query that triggered the error + * @return mixed Result to be stored and returned + */ + protected function handle_error($query) + { + $error = $this->dbh->errorInfo(); + + // retry after "Deadlock found when trying to get lock" errors + $retries = 2; + while ($error[1] == 1213 && $retries >= 0) { + usleep(50000); // wait 50 ms + $result = $this->dbh->query($query); + if ($result !== false) { + return $result; + } + $error = $this->dbh->errorInfo(); + $retries--; + } + + return parent::handle_error($query); + } + }
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_db_pgsql.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_db_pgsql.php
Changed
@@ -36,7 +36,7 @@ */ protected function conn_configure($dsn, $dbh) { - $this->query("SET NAMES 'utf8'"); + $dbh->query("SET NAMES 'utf8'"); } /**
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_db_sqlsrv.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_db_sqlsrv.php
Changed
@@ -52,7 +52,7 @@ protected function conn_configure($dsn, $dbh) { // Set date format in case of non-default language (#1488918) - $this->query("SET DATEFORMAT ymd"); + $dbh->query("SET DATEFORMAT ymd"); } /**
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_html2text.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_html2text.php
Changed
@@ -611,11 +611,13 @@ $body = preg_replace_callback('/((?:^|\n)>*)([^\n]*)/', array($this, 'blockquote_citation_ballback'), trim($body)); $body = '<pre>' . htmlspecialchars($body) . '</pre>'; - $text = substr($text, 0, $start) . $body . "\n" . substr($text, $end + 13); + $text = substr_replace($text, $body . "\n", $start, $end + 13 - $start); $offset = 0; + break; } - } while ($end || $next); + } + while ($end || $next); } } @@ -624,8 +626,9 @@ */ public function blockquote_citation_ballback($m) { - $line = ltrim($m[2]); + $line = ltrim($m[2]); $space = $line[0] == '>' ? '' : ' '; + return $m[1] . '>' . $space . $line; }
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_imap.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_imap.php
Changed
@@ -70,7 +70,7 @@ protected $search_sort_field = ''; protected $search_threads = false; protected $search_sorted = false; - protected $options = array('auth_method' => 'check'); + protected $options = array('auth_type' => 'check'); protected $caching = false; protected $messages_caching = false; protected $threading = false; @@ -391,10 +391,10 @@ public function check_permflag($flag) { $flag = strtoupper($flag); - $imap_flag = $this->conn->flags[$flag]; $perm_flags = $this->get_permflags($this->folder); + $imap_flag = $this->conn->flags[$flag]; - return in_array_nocase($imap_flag, $perm_flags); + return $imap_flag && !empty($perm_flags) && in_array_nocase($imap_flag, $perm_flags); } @@ -410,17 +410,7 @@ if (!strlen($folder)) { return array(); } -/* - Checking PERMANENTFLAGS is rather rare, so we disable caching of it - Re-think when we'll use it for more than only MDNSENT flag - $cache_key = 'mailboxes.permanentflags.' . $folder; - $permflags = $this->get_cache($cache_key); - - if ($permflags !== null) { - return explode(' ', $permflags); - } -*/ if (!$this->check_connection()) { return array(); } @@ -435,10 +425,7 @@ if (!is_array($permflags)) { $permflags = array(); } -/* - // Store permflags as string to limit cached object size - $this->update_cache($cache_key, implode(' ', $permflags)); -*/ + return $permflags; } @@ -3773,12 +3760,17 @@ /** * Enable or disable messages caching * - * @param boolean $set Flag + * @param boolean $set Flag + * @param int $mode Cache mode */ - public function set_messages_caching($set) + public function set_messages_caching($set, $mode = null) { if ($set) { $this->messages_caching = true; + + if ($mode && ($cache = $this->get_mcache_engine())) { + $cache->set_mode($mode); + } } else { if ($this->mcache) { @@ -3798,9 +3790,10 @@ if ($this->messages_caching && !$this->mcache) { $rcube = rcube::get_instance(); if (($dbh = $rcube->get_dbh()) && ($userid = $rcube->get_user_id())) { - $ttl = $rcube->config->get('messages_cache_ttl', '10d'); + $ttl = $rcube->config->get('messages_cache_ttl', '10d'); + $threshold = $rcube->config->get('messages_cache_threshold', 50); $this->mcache = new rcube_imap_cache( - $dbh, $this, $userid, $this->options['skip_deleted'], $ttl); + $dbh, $this, $userid, $this->options['skip_deleted'], $ttl, $threshold); } } @@ -3812,7 +3805,7 @@ * Clears the messages cache. * * @param string $folder Folder name - * @param array $uids Optional message UIDs to remove from cache + * @param array $uids Optional message UIDs to remove from cache */ protected function clear_message_cache($folder = null, $uids = null) {
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_imap_cache.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_imap_cache.php
Changed
@@ -27,6 +27,9 @@ */ class rcube_imap_cache { + const MODE_INDEX = 1; + const MODE_MESSAGE = 2; + /** * Instance of rcube_imap * @@ -56,6 +59,13 @@ private $ttl; /** + * Maximum cached message size + * + * @var int + */ + private $threshold; + + /** * Internal (in-memory) cache * * @var array @@ -63,6 +73,7 @@ private $icache = array(); private $skip_deleted = false; + private $mode; /** * List of known flags. Thanks to this we can handle flag changes @@ -88,6 +99,7 @@ ); + /** * Object constructor. * @@ -96,9 +108,9 @@ * @param int $userid User identifier * @param bool $skip_deleted skip_deleted flag * @param string $ttl Expiration time of memcache/apc items - * + * @param int $threshold Maximum cached message size */ - function __construct($db, $imap, $userid, $skip_deleted, $ttl=0) + function __construct($db, $imap, $userid, $skip_deleted, $ttl=0, $threshold=0) { // convert ttl string to seconds $ttl = get_offset_sec($ttl); @@ -109,6 +121,10 @@ $this->userid = $userid; $this->skip_deleted = $skip_deleted; $this->ttl = $ttl; + $this->threshold = $threshold; + + // cache all possible information by default + $this->mode = self::MODE_INDEX | self::MODE_MESSAGE; } @@ -123,6 +139,17 @@ /** + * Set cache mode + * + * @param int $mode Cache mode + */ + public function set_mode($mode) + { + $this->mode = $mode; + } + + + /** * Return (sorted) messages index (UIDs). * If index doesn't exist or is invalid, will be updated. * @@ -300,38 +327,46 @@ return array(); } - // Fetch messages from cache - $sql_result = $this->db->query( - "SELECT uid, data, flags" - ." FROM ".$this->db->table_name('cache_messages') - ." WHERE user_id = ?" - ." AND mailbox = ?" - ." AND uid IN (".$this->db->array2list($msgs, 'integer').")", - $this->userid, $mailbox); - - $msgs = array_flip($msgs); $result = array(); - while ($sql_arr = $this->db->fetch_assoc($sql_result)) { - $uid = intval($sql_arr['uid']); - $result[$uid] = $this->build_message($sql_arr); + if ($this->mode & self::MODE_MESSAGE) { + // Fetch messages from cache + $sql_result = $this->db->query( + "SELECT uid, data, flags" + ." FROM ".$this->db->table_name('cache_messages') + ." WHERE user_id = ?" + ." AND mailbox = ?" + ." AND uid IN (".$this->db->array2list($msgs, 'integer').")", + $this->userid, $mailbox); + + $msgs = array_flip($msgs); + + while ($sql_arr = $this->db->fetch_assoc($sql_result)) { + $uid = intval($sql_arr['uid']); + $result[$uid] = $this->build_message($sql_arr); - if (!empty($result[$uid])) { - // save memory, we don't need message body here (?) - $result[$uid]->body = null; + if (!empty($result[$uid])) { + // save memory, we don't need message body here (?) + $result[$uid]->body = null; - unset($msgs[$uid]); + unset($msgs[$uid]); + } } + + $msgs = array_flip($msgs); } // Fetch not found messages from IMAP server if (!empty($msgs)) { - $messages = $this->imap->fetch_headers($mailbox, array_keys($msgs), false, true); + $messages = $this->imap->fetch_headers($mailbox, $msgs, false, true); // Insert to DB and add to result list if (!empty($messages)) { foreach ($messages as $msg) { - $this->add_message($mailbox, $msg, !array_key_exists($msg->uid, $result)); + if ($this->mode & self::MODE_MESSAGE) { + $this->add_message($mailbox, $msg, !array_key_exists($msg->uid, $result)); + } + $result[$msg->uid] = $msg; } } @@ -362,17 +397,19 @@ return $this->icache['__message']['object']; } - $sql_result = $this->db->query( - "SELECT flags, data" - ." FROM ".$this->db->table_name('cache_messages') - ." WHERE user_id = ?" - ." AND mailbox = ?" - ." AND uid = ?", - $this->userid, $mailbox, (int)$uid); + if ($this->mode & self::MODE_MESSAGE) { + $sql_result = $this->db->query( + "SELECT flags, data" + ." FROM ".$this->db->table_name('cache_messages') + ." WHERE user_id = ?" + ." AND mailbox = ?" + ." AND uid = ?", + $this->userid, $mailbox, (int)$uid); - if ($sql_arr = $this->db->fetch_assoc($sql_result)) { - $message = $this->build_message($sql_arr); - $found = true; + if ($sql_arr = $this->db->fetch_assoc($sql_result)) { + $message = $this->build_message($sql_arr); + $found = true; + } } // Get the message from IMAP server @@ -381,6 +418,10 @@ // cache will be updated in close(), see below } + if (!($this->mode & self::MODE_MESSAGE)) { + return $message; + } + // Save the message in internal cache, will be written to DB in close() // Common scenario: user opens unseen message // - get message (SELECT) @@ -416,6 +457,10 @@ return; } + if (!($this->mode & self::MODE_MESSAGE)) { + return; + } + $flags = 0; $msg = clone $message; @@ -487,6 +532,10 @@ return; } + if (!($this->mode & self::MODE_MESSAGE)) { + return; + } + $flag = strtoupper($flag); $idx = (int) array_search($flag, $this->flags); $uids = (array) $uids; @@ -527,6 +576,10 @@ */ function remove_message($mailbox = null, $uids = null) { + if (!($this->mode & self::MODE_MESSAGE)) { + return; + } + if (!strlen($mailbox)) { $this->db->query( "DELETE FROM ".$this->db->table_name('cache_messages') @@ -1028,15 +1081,17 @@ $removed = array(); // Get known UIDs - $sql_result = $this->db->query( - "SELECT uid" - ." FROM ".$this->db->table_name('cache_messages') - ." WHERE user_id = ?" - ." AND mailbox = ?", - $this->userid, $mailbox); + if ($this->mode & self::MODE_MESSAGE) { + $sql_result = $this->db->query( + "SELECT uid" + ." FROM ".$this->db->table_name('cache_messages') + ." WHERE user_id = ?" + ." AND mailbox = ?", + $this->userid, $mailbox); - while ($sql_arr = $this->db->fetch_assoc($sql_result)) { - $uids[] = $sql_arr['uid']; + while ($sql_arr = $this->db->fetch_assoc($sql_result)) { + $uids[] = $sql_arr['uid']; + } } // Synchronize messages data @@ -1155,13 +1210,13 @@ // Save current message from internal cache if ($message = $this->icache['__message']) { // clean up some object's data - $object = $this->message_object_prepare($message['object']); + $this->message_object_prepare($message['object']); // calculate current md5 sum - $md5sum = md5(serialize($object)); + $md5sum = md5(serialize($message['object'])); if ($message['md5sum'] != $md5sum) { - $this->add_message($message['mailbox'], $object, !$message['exists']); + $this->add_message($message['mailbox'], $message['object'], !$message['exists']); } $this->icache['__message']['md5sum'] = $md5sum; @@ -1171,12 +1226,19 @@ /** * Prepares message object to be stored in database. + * + * @param rcube_message_header|rcube_message_part */ - private function message_object_prepare($msg) + private function message_object_prepare(&$msg, &$size = 0) { - // Remove body too big (>25kB) - if ($msg->body && strlen($msg->body) > 25 * 1024) { - unset($msg->body); + // Remove body too big + if ($msg->body && ($length = strlen($msg->body))) { + $size += $length; + + if ($size > $this->threshold * 1024) { + $size -= $length; + unset($msg->body); + } } // Fix mimetype which might be broken by some code when message is displayed @@ -1186,13 +1248,19 @@ list($msg->ctype_primary, $msg->ctype_secondary) = explode('/', $msg->mimetype); } + unset($msg->replaces); + if (is_array($msg->structure->parts)) { - foreach ($msg->structure->parts as $idx => $part) { - $msg->structure->parts[$idx] = $this->message_object_prepare($part); + foreach ($msg->structure->parts as $part) { + $this->message_object_prepare($part, $size); } } - return $msg; + if (is_array($msg->parts)) { + foreach ($msg->parts as $part) { + $this->message_object_prepare($part, $size); + } + } }
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_imap_generic.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_imap_generic.php
Changed
@@ -706,22 +706,11 @@ */ function connect($host, $user, $password, $options=null) { - // set options - if (is_array($options)) { - $this->prefs = $options; - } - // set auth method - if (!empty($this->prefs['auth_type'])) { - $auth_method = strtoupper($this->prefs['auth_type']); - } else { - $auth_method = 'CHECK'; - } + // configure + $this->set_prefs($options); - if (!empty($this->prefs['disabled_caps'])) { - $this->prefs['disabled_caps'] = array_map('strtoupper', (array)$this->prefs['disabled_caps']); - } - - $result = false; + $auth_method = $this->prefs['auth_type']; + $result = false; // initialize connection $this->error = ''; @@ -898,6 +887,36 @@ } /** + * Initializes environment + */ + protected function set_prefs($prefs) + { + // set preferences + if (is_array($prefs)) { + $this->prefs = $prefs; + } + + // set auth method + if (!empty($this->prefs['auth_type'])) { + $this->prefs['auth_type'] = strtoupper($this->prefs['auth_type']); + } + else { + $this->prefs['auth_type'] = 'CHECK'; + } + + // disabled capabilities + if (!empty($this->prefs['disabled_caps'])) { + $this->prefs['disabled_caps'] = array_map('strtoupper', (array)$this->prefs['disabled_caps']); + } + + // additional message flags + if (!empty($this->prefs['message_flags'])) { + $this->flags = array_merge($this->flags, $this->prefs['message_flags']); + unset($this->prefs['message_flags']); + } + } + + /** * Checks connection status * * @return bool True if connection is active and user is logged in, False otherwise. @@ -3139,8 +3158,7 @@ } foreach ($data as $entry) { - // If we are running in a murder topology, the entry[2] string needs - // to be escaped. + // Workaround cyrus-murder bug, the entry[2] string needs to be escaped if (self::$mupdate) { $entry[2] = addcslashes($entry[2], '\\"'); }
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_ldap.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_ldap.php
Changed
@@ -34,6 +34,7 @@ public $ready = false; public $group_id = 0; public $coltypes = array(); + public $export_groups = false; // private properties protected $ldap; @@ -288,7 +289,9 @@ $replaces = array('%dn' => '', '%dc' => $dc, '%d' => $d, '%fu' => $fu, '%u' => $u); // Search for the dn to use to authenticate - if ($this->prop['search_base_dn'] && $this->prop['search_filter']) { + if ($this->prop['search_base_dn'] && $this->prop['search_filter'] + && (strstr($bind_dn, '%dn') || strstr($this->base_dn, '%dn') || strstr($this->groups_base_dn, '%dn')) + ) { $search_bind_dn = strtr($this->prop['search_bind_dn'], $replaces); $search_base_dn = strtr($this->prop['search_base_dn'], $replaces); $search_filter = strtr($this->prop['search_filter'], $replaces);
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_ldap_generic.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_ldap_generic.php
Changed
@@ -696,11 +696,17 @@ * Turn an LDAP entry into a regular PHP array with attributes as keys. * * @param array $entry Attributes array as retrieved from ldap_get_attributes() or ldap_get_entries() + * * @return array Hash array with attributes as keys */ public static function normalize_entry($entry) { + if (!isset($entry['count'])) { + return $entry; + } + $rec = array(); + for ($i=0; $i < $entry['count']; $i++) { $attr = $entry[$i]; if ($entry[$attr]['count'] == 1) {
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_message.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_message.php
Changed
@@ -195,8 +195,6 @@ /** * Determine if the message contains a HTML part. This must to be * a real part not an attachment (or its part) - * This must to be - * a real part not an attachment (or its part) * * @param bool $enriched Enables checking for text/enriched parts too * @@ -214,14 +212,15 @@ $level = explode('.', $part->mime_id); - // Check if the part belongs to higher-level's alternative/related + // Check if the part belongs to higher-level's multipart part + // this can be alternative/related/signed/encrypted, but not mixed while (array_pop($level) !== null) { if (!count($level)) { return true; } $parent = $this->mime_parts[join('.', $level)]; - if ($parent->mimetype != 'multipart/alternative' && $parent->mimetype != 'multipart/related') { + if (!preg_match('/^multipart\/(alternative|related|signed|encrypted)$/', $parent->mimetype)) { continue 2; } } @@ -435,17 +434,24 @@ continue; } + // We've encountered (malformed) messages with more than + // one text/plain or text/html part here. There's no way to choose + // which one is better, so we'll display first of them and add + // others as attachments (#1489358) + // check if sub part is if ($is_multipart) $related_part = $p; - else if ($sub_mimetype == 'text/plain') + else if ($sub_mimetype == 'text/plain' && !$plain_part) $plain_part = $p; - else if ($sub_mimetype == 'text/html') + else if ($sub_mimetype == 'text/html' && !$html_part) $html_part = $p; - else if ($sub_mimetype == 'text/enriched') + else if ($sub_mimetype == 'text/enriched' && !$enriched_part) $enriched_part = $p; - else - $attach_part = $p; + else { + // add unsupported/unrecognized parts to attachments list + $this->attachments[] = $sub_part; + } } // parse related part (alternative part could be in here) @@ -486,11 +492,6 @@ $this->parts[] = $c; } - - // add unsupported/unrecognized parts to attachments list - if ($attach_part) { - $this->attachments[] = $structure->parts[$attach_part]; - } } // this is an ecrypted message -> create a plaintext body with the according message else if ($mimetype == 'multipart/encrypted') {
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_mime.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_mime.php
Changed
@@ -637,7 +637,8 @@ if ($nextChar === ' ' || $nextChar === $separator) { $afterNextChar = mb_substr($string, $width + 1, 1); - if ($afterNextChar === false) { + // Note: mb_substr() does never return False + if ($afterNextChar === false || $afterNextChar === '') { $subString .= $nextChar; } @@ -650,24 +651,23 @@ $subString = mb_substr($subString, 0, $spacePos); $cutLength = $spacePos + 1; } - else if ($cut === false && $breakPos === false) { - $subString = $string; - $cutLength = null; - } else if ($cut === false) { $spacePos = mb_strpos($string, ' ', 0); - if ($spacePos !== false && $spacePos < $breakPos) { + if ($spacePos !== false && ($breakPos === false || $spacePos < $breakPos)) { $subString = mb_substr($string, 0, $spacePos); $cutLength = $spacePos + 1; } + else if ($breakPos === false) { + $subString = $string; + $cutLength = null; + } else { $subString = mb_substr($string, 0, $breakPos); $cutLength = $breakPos + 1; } } else { - $subString = mb_substr($subString, 0, $width); $cutLength = $width; } } @@ -708,12 +708,20 @@ */ public static function file_content_type($path, $name, $failover = 'application/octet-stream', $is_stream = false, $skip_suffix = false) { + static $mime_ext = array(); + $mime_type = null; - $mime_magic = rcube::get_instance()->config->get('mime_magic'); - $mime_ext = $skip_suffix ? null : @include(RCUBE_CONFIG_DIR . '/mimetypes.php'); + $config = rcube::get_instance()->config; + $mime_magic = $config->get('mime_magic'); + + if (!$skip_suffix && empty($mime_ext)) { + foreach ($config->resolve_paths('mimetypes.php') as $fpath) { + $mime_ext = array_merge($mime_ext, (array) @include($fpath)); + } + } // use file name suffix with hard-coded mime-type map - if (is_array($mime_ext) && $name) { + if (!$skip_suffix && is_array($mime_ext) && $name) { if ($suffix = substr($name, strrpos($name, '.')+1)) { $mime_type = $mime_ext[strtolower($suffix)]; } @@ -818,7 +826,9 @@ // fallback to some well-known types most important for daily emails if (empty($mime_types)) { - $mime_extensions = (array) @include(RCUBE_CONFIG_DIR . '/mimetypes.php'); + foreach (rcube::get_instance()->config->resolve_paths('mimetypes.php') as $fpath) { + $mime_extensions = array_merge($mime_extensions, (array) @include($fpath)); + } foreach ($mime_extensions as $ext => $mime) { $mime_types[$mime][] = $ext;
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_plugin_api.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_plugin_api.php
Changed
@@ -403,7 +403,7 @@ $args = $ret + $args; } - if ($args['abort']) { + if ($args['break']) { break; } }
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_spellchecker.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_spellchecker.php
Changed
@@ -3,8 +3,8 @@ /* +-----------------------------------------------------------------------+ | This file is part of the Roundcube Webmail client | - | Copyright (C) 2011, Kolab Systems AG | - | Copyright (C) 2008-2011, The Roundcube Dev Team | + | Copyright (C) 2011-2013, Kolab Systems AG | + | Copyright (C) 2008-2013, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -28,21 +28,15 @@ { private $matches = array(); private $engine; + private $backend; private $lang; private $rc; private $error; - private $separator = '/[\s\r\n\t\(\)\/\[\]{}<>\\"]+|[:;?!,\.](?=\W|$)/'; private $options = array(); private $dict; private $have_dict; - // default settings - const GOOGLE_HOST = 'ssl://www.google.com'; - const GOOGLE_PORT = 443; - const MAX_SUGGESTIONS = 10; - - /** * Constructor * @@ -60,6 +54,15 @@ 'ignore_caps' => $this->rc->config->get('spellcheck_ignore_caps'), 'dictionary' => $this->rc->config->get('spellcheck_dictionary'), ); + + $cls = 'rcube_spellcheck_' . $this->engine; + if (class_exists($cls)) { + $this->backend = new $cls($this, $this->lang); + $this->backend->options = $this->options; + } + else { + $this->error = "Unknown spellcheck engine '$this->engine'"; + } } @@ -81,14 +84,8 @@ $this->content = $text; } - if ($this->engine == 'pspell') { - $this->matches = $this->_pspell_check($this->content); - } - else if ($this->engine == 'enchant') { - $this->matches = $this->_enchant_check($this->content); - } - else { - $this->matches = $this->_googie_check($this->content); + if ($this->backend) { + $this->matches = $this->backend->check($this->content); } return $this->found() == 0; @@ -115,14 +112,11 @@ */ function get_suggestions($word) { - if ($this->engine == 'pspell') { - return $this->_pspell_suggestions($word); - } - else if ($this->engine == 'enchant') { - return $this->_enchant_suggestions($word); + if ($this->backend) { + return $this->backend->get_suggestions($word); } - return $this->_googie_suggestions($word); + return array(); } @@ -136,14 +130,15 @@ */ function get_words($text = null, $is_html=false) { - if ($this->engine == 'pspell') { - return $this->_pspell_words($text, $is_html); + if ($is_html) { + $text = $this->html2text($text); } - else if ($this->engine == 'enchant') { - return $this->_enchant_words($text, $is_html); + + if ($this->backend) { + return $this->backend->get_words($text); } - return $this->_googie_words($text, $is_html); + return array(); } @@ -199,394 +194,7 @@ */ function error() { - return $this->error; - } - - - /** - * Checks the text using pspell - * - * @param string $text Text content for spellchecking - */ - private function _pspell_check($text) - { - // init spellchecker - $this->_pspell_init(); - - if (!$this->plink) { - return array(); - } - - // tokenize - $text = preg_split($this->separator, $text, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE); - - $diff = 0; - $matches = array(); - - foreach ($text as $w) { - $word = trim($w[0]); - $pos = $w[1] - $diff; - $len = mb_strlen($word); - - // skip exceptions - if ($this->is_exception($word)) { - } - else if (!pspell_check($this->plink, $word)) { - $suggestions = pspell_suggest($this->plink, $word); - - if (sizeof($suggestions) > self::MAX_SUGGESTIONS) { - $suggestions = array_slice($suggestions, 0, self::MAX_SUGGESTIONS); - } - - $matches[] = array($word, $pos, $len, null, $suggestions); - } - - $diff += (strlen($word) - $len); - } - - return $matches; - } - - - /** - * Returns the misspelled words - */ - private function _pspell_words($text = null, $is_html=false) - { - $result = array(); - - if ($text) { - // init spellchecker - $this->_pspell_init(); - - if (!$this->plink) { - return array(); - } - - // With PSpell we don't need to get suggestions to return misspelled words - if ($is_html) { - $text = $this->html2text($text); - } - - $text = preg_split($this->separator, $text, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE); - - foreach ($text as $w) { - $word = trim($w[0]); - - // skip exceptions - if ($this->is_exception($word)) { - continue; - } - - if (!pspell_check($this->plink, $word)) { - $result[] = $word; - } - } - - return $result; - } - - foreach ($this->matches as $m) { - $result[] = $m[0]; - } - - return $result; - } - - - /** - * Returns suggestions for misspelled word - */ - private function _pspell_suggestions($word) - { - // init spellchecker - $this->_pspell_init(); - - if (!$this->plink) { - return array(); - } - - $suggestions = pspell_suggest($this->plink, $word); - - if (sizeof($suggestions) > self::MAX_SUGGESTIONS) - $suggestions = array_slice($suggestions, 0, self::MAX_SUGGESTIONS); - - return is_array($suggestions) ? $suggestions : array(); - } - - - /** - * Initializes PSpell dictionary - */ - private function _pspell_init() - { - if (!$this->plink) { - if (!extension_loaded('pspell')) { - $this->error = "Pspell extension not available"; - return; - } - - $this->plink = pspell_new($this->lang, null, null, RCUBE_CHARSET, PSPELL_FAST); - } - - if (!$this->plink) { - $this->error = "Unable to load Pspell engine for selected language"; - } - } - - - /** - * Checks the text using enchant - * - * @param string $text Text content for spellchecking - */ - private function _enchant_check($text) - { - // init spellchecker - $this->_enchant_init(); - - if (!$this->enchant_dictionary) { - return array(); - } - - // tokenize - $text = preg_split($this->separator, $text, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE); - - $diff = 0; - $matches = array(); - - foreach ($text as $w) { - $word = trim($w[0]); - $pos = $w[1] - $diff; - $len = mb_strlen($word); - - // skip exceptions - if ($this->is_exception($word)) { - } - else if (!enchant_dict_check($this->enchant_dictionary, $word)) { - $suggestions = enchant_dict_suggest($this->enchant_dictionary, $word); - - if (sizeof($suggestions) > self::MAX_SUGGESTIONS) { - $suggestions = array_slice($suggestions, 0, self::MAX_SUGGESTIONS); - } - - $matches[] = array($word, $pos, $len, null, $suggestions); - } - - $diff += (strlen($word) - $len); - } - - return $matches; - } - - - /** - * Returns the misspelled words - */ - private function _enchant_words($text = null, $is_html=false) - { - $result = array(); - - if ($text) { - // init spellchecker - $this->_enchant_init(); - - if (!$this->enchant_dictionary) { - return array(); - } - - // With Enchant we don't need to get suggestions to return misspelled words - if ($is_html) { - $text = $this->html2text($text); - } - - $text = preg_split($this->separator, $text, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE); - - foreach ($text as $w) { - $word = trim($w[0]); - - // skip exceptions - if ($this->is_exception($word)) { - continue; - } - - if (!enchant_dict_check($this->enchant_dictionary, $word)) { - $result[] = $word; - } - } - - return $result; - } - - foreach ($this->matches as $m) { - $result[] = $m[0]; - } - - return $result; - } - - - /** - * Returns suggestions for misspelled word - */ - private function _enchant_suggestions($word) - { - // init spellchecker - $this->_enchant_init(); - - if (!$this->enchant_dictionary) { - return array(); - } - - $suggestions = enchant_dict_suggest($this->enchant_dictionary, $word); - - if (sizeof($suggestions) > self::MAX_SUGGESTIONS) - $suggestions = array_slice($suggestions, 0, self::MAX_SUGGESTIONS); - - return is_array($suggestions) ? $suggestions : array(); - } - - - /** - * Initializes PSpell dictionary - */ - private function _enchant_init() - { - if (!$this->enchant_broker) { - if (!extension_loaded('enchant')) { - $this->error = "Enchant extension not available"; - return; - } - - $this->enchant_broker = enchant_broker_init(); - } - - if (!enchant_broker_dict_exists($this->enchant_broker, $this->lang)) { - $this->error = "Unable to load dictionary for selected language using Enchant"; - return; - } - - $this->enchant_dictionary = enchant_broker_request_dict($this->enchant_broker, $this->lang); - } - - - private function _googie_check($text) - { - // spell check uri is configured - $url = $this->rc->config->get('spellcheck_uri'); - - if ($url) { - $a_uri = parse_url($url); - $ssl = ($a_uri['scheme'] == 'https' || $a_uri['scheme'] == 'ssl'); - $port = $a_uri['port'] ? $a_uri['port'] : ($ssl ? 443 : 80); - $host = ($ssl ? 'ssl://' : '') . $a_uri['host']; - $path = $a_uri['path'] . ($a_uri['query'] ? '?'.$a_uri['query'] : '') . $this->lang; - } - else { - $host = self::GOOGLE_HOST; - $port = self::GOOGLE_PORT; - $path = '/tbproxy/spell?lang=' . $this->lang; - } - - // Google has some problem with spaces, use \n instead - $gtext = str_replace(' ', "\n", $text); - - $gtext = '<?xml version="1.0" encoding="utf-8" ?>' - .'<spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1">' - .'<text>' . $gtext . '</text>' - .'</spellrequest>'; - - $store = ''; - if ($fp = fsockopen($host, $port, $errno, $errstr, 30)) { - $out = "POST $path HTTP/1.0\r\n"; - $out .= "Host: " . str_replace('ssl://', '', $host) . "\r\n"; - $out .= "Content-Length: " . strlen($gtext) . "\r\n"; - $out .= "Content-Type: application/x-www-form-urlencoded\r\n"; - $out .= "Connection: Close\r\n\r\n"; - $out .= $gtext; - fwrite($fp, $out); - - while (!feof($fp)) - $store .= fgets($fp, 128); - fclose($fp); - } - - // parse HTTP response - if (preg_match('!^HTTP/1.\d (\d+)(.+)!', $store, $m)) { - $http_status = $m[1]; - if ($http_status != '200') - $this->error = 'HTTP ' . $m[1] . $m[2]; - } - - if (!$store) { - $this->error = "Empty result from spelling engine"; - } - else if (preg_match('/<spellresult error="([^"]+)"/', $store, $m) && $m[1]) { - $this->error = "Error code $m[1] returned"; - } - - preg_match_all('/<c o="([^"]*)" l="([^"]*)" s="([^"]*)">([^<]*)<\/c>/', $store, $matches, PREG_SET_ORDER); - - // skip exceptions (if appropriate options are enabled) - if (!empty($this->options['ignore_syms']) || !empty($this->options['ignore_nums']) - || !empty($this->options['ignore_caps']) || !empty($this->options['dictionary']) - ) { - foreach ($matches as $idx => $m) { - $word = mb_substr($text, $m[1], $m[2], RCUBE_CHARSET); - // skip exceptions - if ($this->is_exception($word)) { - unset($matches[$idx]); - } - } - } - - return $matches; - } - - - private function _googie_words($text = null, $is_html=false) - { - if ($text) { - if ($is_html) { - $text = $this->html2text($text); - } - - $matches = $this->_googie_check($text); - } - else { - $matches = $this->matches; - $text = $this->content; - } - - $result = array(); - - foreach ($matches as $m) { - $result[] = mb_substr($text, $m[1], $m[2], RCUBE_CHARSET); - } - - return $result; - } - - - private function _googie_suggestions($word) - { - if ($word) { - $matches = $this->_googie_check($word); - } - else { - $matches = $this->matches; - } - - if ($matches[0][4]) { - $suggestions = explode("\t", $matches[0][4]); - if (sizeof($suggestions) > self::MAX_SUGGESTIONS) { - $suggestions = array_slice($suggestions, 0, MAX_SUGGESTIONS); - } - - return $suggestions; - } - - return array(); + return $this->error ? $this->error : ($this->backend ? $this->backend->error() : false); }
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_storage.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_storage.php
Changed
@@ -39,7 +39,7 @@ protected $default_charset = 'ISO-8859-1'; protected $default_folders = array('INBOX'); protected $search_set; - protected $options = array('auth_method' => 'check'); + protected $options = array('auth_type' => 'check'); protected $page_size = 10; protected $threading = false;
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_string_replacer.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_string_replacer.php
Changed
@@ -24,11 +24,16 @@ */ class rcube_string_replacer { - public static $pattern = '/##str_replacement\[([0-9]+)\]##/'; + public static $pattern = '/##str_replacement_(\d+)##/'; public $mailto_pattern; public $link_pattern; + public $linkref_index; + public $linkref_pattern; + private $values = array(); private $options = array(); + private $linkrefs = array(); + private $urls = array(); function __construct($options = array()) @@ -45,6 +50,8 @@ ."@$utf_domain" // domain-part ."(\?[$url1$url2]+)?" // e.g. ?subject=test... .")/"; + $this->linkref_index = '/\[([^\]#]+)\](:?\s*##str_replacement_(\d+)##)/'; + $this->linkref_pattern = '/\[([^\]#]+)\]/'; $this->options = $options; } @@ -67,7 +74,7 @@ */ public function get_replacement($i) { - return '##str_replacement['.$i.']##'; + return '##str_replacement_' . $i . '##'; } /** @@ -96,6 +103,7 @@ $attrib['href'] = $url_prefix . $url; $i = $this->add(html::a($attrib, rcube::Q($url)) . $suffix); + $this->urls[$i] = $attrib['href']; } // Return valid link for recognized schemes, otherwise @@ -104,6 +112,32 @@ } /** + * Callback to add an entry to the link index + */ + public function linkref_addindex($matches) + { + $key = $matches[1]; + $this->linkrefs[$key] = $this->urls[$matches[3]]; + + return $this->get_replacement($this->add('['.$key.']')) . $matches[2]; + } + + /** + * Callback to replace link references with real links + */ + public function linkref_callback($matches) + { + $i = 0; + if ($url = $this->linkrefs[$matches[1]]) { + $attrib = (array)$this->options['link_attribs']; + $attrib['href'] = $url; + $i = $this->add(html::a($attrib, rcube::Q($matches[1]))); + } + + return $i > 0 ? '['.$this->get_replacement($i).']' : $matches[0]; + } + + /** * Callback function used to build mailto: links around e-mail strings * * @param array Matches result from preg_replace_callback @@ -142,6 +176,9 @@ // search for patterns like links and e-mail addresses $str = preg_replace_callback($this->link_pattern, array($this, 'link_callback'), $str); $str = preg_replace_callback($this->mailto_pattern, array($this, 'mailto_callback'), $str); + // resolve link references + $str = preg_replace_callback($this->linkref_index, array($this, 'linkref_addindex'), $str); + $str = preg_replace_callback($this->linkref_pattern, array($this, 'linkref_callback'), $str); return $str; }
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_utils.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_utils.php
Changed
@@ -390,12 +390,13 @@ * Convert array of request parameters (prefixed with _) * to a regular array with non-prefixed keys. * - * @param int $mode Source to get value from (GPC) - * @param string $ignore PCRE expression to skip parameters by name + * @param int $mode Source to get value from (GPC) + * @param string $ignore PCRE expression to skip parameters by name + * @param boolean $allow_html Allow HTML tags in field value * * @return array Hash array with all request parameters */ - public static function request2param($mode = null, $ignore = 'task|action') + public static function request2param($mode = null, $ignore = 'task|action', $allow_html = false) { $out = array(); $src = $mode == self::INPUT_GET ? $_GET : ($mode == self::INPUT_POST ? $_POST : $_REQUEST); @@ -403,7 +404,7 @@ foreach (array_keys($src) as $key) { $fname = $key[0] == '_' ? substr($key, 1) : $key; if ($ignore && !preg_match('/^(' . $ignore . ')$/', $fname)) { - $out[$fname] = self::get_input_value($key, $mode); + $out[$fname] = self::get_input_value($key, $mode, $allow_html); } } @@ -444,41 +445,45 @@ $source = self::xss_entity_decode($source); $stripped = preg_replace('/[^a-z\(:;]/i', '', $source); $evilexpr = 'expression|behavior|javascript:|import[^a]' . (!$allow_remote ? '|url\(' : ''); + if (preg_match("/$evilexpr/i", $stripped)) { return '/* evil! */'; } + $strict_url_regexp = '!url\s*\([ "\'](https?:)//[a-z0-9/._+-]+["\' ]\)!Uims'; + // cut out all contents between { and } while (($pos = strpos($source, '{', $last_pos)) && ($pos2 = strpos($source, '}', $pos))) { - $styles = substr($source, $pos+1, $pos2-($pos+1)); + $length = $pos2 - $pos - 1; + $styles = substr($source, $pos+1, $length); // check every line of a style block... if ($allow_remote) { $a_styles = preg_split('/;[\r\n]*/', $styles, -1, PREG_SPLIT_NO_EMPTY); + foreach ($a_styles as $line) { $stripped = preg_replace('/[^a-z\(:;]/i', '', $line); // ... and only allow strict url() values - $regexp = '!url\s*\([ "\'](https?:)//[a-z0-9/._+-]+["\' ]\)!Uims'; - if (stripos($stripped, 'url(') && !preg_match($regexp, $line)) { + if (stripos($stripped, 'url(') && !preg_match($strict_url_regexp, $line)) { $a_styles = array('/* evil! */'); break; } } + $styles = join(";\n", $a_styles); } - $key = $replacements->add($styles); - $source = substr($source, 0, $pos+1) - . $replacements->get_replacement($key) - . substr($source, $pos2, strlen($source)-$pos2); - $last_pos = $pos+2; + $key = $replacements->add($styles); + $repl = $replacements->get_replacement($key); + $source = substr_replace($source, $repl, $pos+1, $length); + $last_pos = $pos2 - ($length - strlen($repl)); } // remove html comments and add #container to each tag selector. // also replace body definition because we also stripped off the <body> tag - $styles = preg_replace( + $source = preg_replace( array( - '/(^\s*<!--)|(-->\s*$)/', + '/(^\s*<\!--)|(-->\s*$)/m', '/(^\s*|,\s*|\}\s*)([a-z0-9\._#\*][a-z0-9\.\-_]*)/im', '/'.preg_quote($container_id, '/').'\s+body/i', ), @@ -490,9 +495,9 @@ $source); // put block contents back in - $styles = $replacements->resolve($styles); + $source = $replacements->resolve($source); - return $styles; + return $source; } @@ -739,11 +744,22 @@ */ public static function strtotime($date) { + $date = trim($date); + // check for MS Outlook vCard date format YYYYMMDD - if (preg_match('/^([12][90]\d\d)([01]\d)(\d\d)$/', trim($date), $matches)) { - return mktime(0,0,0, intval($matches[2]), intval($matches[3]), intval($matches[1])); + if (preg_match('/^([12][90]\d\d)([01]\d)([0123]\d)$/', $date, $m)) { + return mktime(0,0,0, intval($m[2]), intval($m[3]), intval($m[1])); + } + + // common little-endian formats, e.g. dd/mm/yyyy (not all are supported by strtotime) + if (preg_match('/^(\d{1,2})[.\/-](\d{1,2})[.\/-](\d{4})$/', $date, $m) + && $m[1] > 0 && $m[1] <= 31 && $m[2] > 0 && $m[2] <= 12 && $m[3] >= 1970 + ) { + return mktime(0,0,0, intval($m[2]), intval($m[1]), intval($m[3])); } - else if (is_numeric($date)) { + + // unix timestamp + if (is_numeric($date)) { return (int) $date; } @@ -776,6 +792,44 @@ return (int) $ts; } + /** + * Date parsing function that turns the given value into a DateTime object + * + * @param string $date Date string + * + * @return object DateTime instance or false on failure + */ + public static function anytodatetime($date) + { + if (is_object($date) && is_a($date, 'DateTime')) { + return $date; + } + + $dt = false; + $date = trim($date); + + // try to parse string with DateTime first + if (!empty($date)) { + try { + $dt = new DateTime($date); + } + catch (Exception $e) { + // ignore + } + } + + // try our advanced strtotime() method + if (!$dt && ($timestamp = self::strtotime($date))) { + try { + $dt = new DateTime("@".$timestamp); + } + catch (Exception $e) { + // ignore + } + } + + return $dt; + } /* * Idn_to_ascii wrapper.
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_vcard.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_vcard.php
Changed
@@ -47,6 +47,7 @@ 'manager' => 'X-MANAGER', 'spouse' => 'X-SPOUSE', 'edit' => 'X-AB-EDIT', + 'groups' => 'CATEGORIES', ); private $typemap = array( 'IPHONE' => 'mobile', @@ -357,8 +358,8 @@ case 'birthday': case 'anniversary': - if (($val = rcube_utils::strtotime($value)) && ($fn = self::$fieldmap[$field])) { - $this->raw[$fn][] = array(0 => date('Y-m-d', $val), 'value' => array('date')); + if (($val = rcube_utils::anytodatetime($value)) && ($fn = self::$fieldmap[$field])) { + $this->raw[$fn][] = array(0 => $val->format('Y-m-d'), 'value' => array('date')); } break; @@ -756,7 +757,7 @@ * * @return string Joined and quoted string */ - private static function vcard_quote($s, $sep = ';') + public static function vcard_quote($s, $sep = ';') { if (is_array($s)) { foreach($s as $part) { @@ -765,7 +766,7 @@ return(implode($sep, (array)$r)); } - return strtr($s, array('\\' => '\\\\', "\r" => '', "\n" => '\n', ',' => '\,', ';' => '\;')); + return strtr($s, array('\\' => '\\\\', "\r" => '', "\n" => '\n', $sep => '\\'.$sep)); } /**
View file
kolab-syncroton-2.2.1.tar.gz/lib/ext/Roundcube/rcube_washtml.php -> kolab-syncroton-2.2.2.tar.gz/lib/ext/Roundcube/rcube_washtml.php
Changed
@@ -377,7 +377,14 @@ // Detect max nesting level (for dumpHTML) (#1489110) $this->max_nesting_level = (int) @ini_get('xdebug.max_nesting_level'); - @$node->loadHTML($html); + // Use optimizations if supported + if (version_compare(PHP_VERSION, '5.4.0', '>=')) { + @$node->loadHTML($html, LIBXML_PARSEHUGE | LIBXML_COMPACT); + } + else { + @$node->loadHTML($html); + } + return $this->dumpHtml($node); } @@ -448,7 +455,7 @@ } // fix (unknown/malformed) HTML tags before "wash" - $html = preg_replace_callback('/(<[\/]*)([^\s>]+)/', array($this, 'html_tag_callback'), $html); + $html = preg_replace_callback('/(<(?!\!)[\/]*)([^\s>]+)/', array($this, 'html_tag_callback'), $html); // Remove invalid HTML comments (#1487759) // Don't remove valid conditional comments
View file
kolab-syncroton-2.2.1.tar.gz/lib/kolab_sync.php -> kolab-syncroton-2.2.2.tar.gz/lib/kolab_sync.php
Changed
@@ -43,7 +43,7 @@ public $user; const CHARSET = 'UTF-8'; - const VERSION = "2.2.1"; + const VERSION = "2.2.2"; /**
View file
kolab-syncroton-2.2.1.tar.gz/lib/plugins/libkolab/SQL/mysql.initial.sql -> kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/SQL/mysql.initial.sql
Changed
@@ -1,29 +1,175 @@ /** * libkolab database schema * - * @version @package_version@ + * @version 1.0 * @author Thomas Bruederli * @licence GNU AGPL **/ + +DROP TABLE IF EXISTS `kolab_folders`; + +CREATE TABLE `kolab_folders` ( + `folder_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + `resource` VARCHAR(255) NOT NULL, + `type` VARCHAR(32) NOT NULL, + `synclock` INT(10) NOT NULL DEFAULT '0', + `ctag` VARCHAR(40) DEFAULT NULL, + PRIMARY KEY(`folder_id`), + INDEX `resource_type` (`resource`, `type`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + DROP TABLE IF EXISTS `kolab_cache`; -CREATE TABLE `kolab_cache` ( - `resource` VARCHAR(255) CHARACTER SET ascii NOT NULL, +DROP TABLE IF EXISTS `kolab_cache_contact`; + +CREATE TABLE `kolab_cache_contact` ( + `folder_id` BIGINT UNSIGNED NOT NULL, + `msguid` BIGINT UNSIGNED NOT NULL, + `uid` VARCHAR(128) CHARACTER SET ascii NOT NULL, + `created` DATETIME DEFAULT NULL, + `changed` DATETIME DEFAULT NULL, + `data` TEXT NOT NULL, + `xml` TEXT NOT NULL, + `tags` VARCHAR(255) NOT NULL, + `words` TEXT NOT NULL, `type` VARCHAR(32) CHARACTER SET ascii NOT NULL, + CONSTRAINT `fk_kolab_cache_contact_folder` FOREIGN KEY (`folder_id`) + REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE, + PRIMARY KEY(`folder_id`,`msguid`), + INDEX `contact_type` (`folder_id`,`type`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +DROP TABLE IF EXISTS `kolab_cache_event`; + +CREATE TABLE `kolab_cache_event` ( + `folder_id` BIGINT UNSIGNED NOT NULL, `msguid` BIGINT UNSIGNED NOT NULL, `uid` VARCHAR(128) CHARACTER SET ascii NOT NULL, `created` DATETIME DEFAULT NULL, `changed` DATETIME DEFAULT NULL, `data` TEXT NOT NULL, `xml` TEXT NOT NULL, + `tags` VARCHAR(255) NOT NULL, + `words` TEXT NOT NULL, `dtstart` DATETIME, `dtend` DATETIME, + CONSTRAINT `fk_kolab_cache_event_folder` FOREIGN KEY (`folder_id`) + REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE, + PRIMARY KEY(`folder_id`,`msguid`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +DROP TABLE IF EXISTS `kolab_cache_task`; + +CREATE TABLE `kolab_cache_task` ( + `folder_id` BIGINT UNSIGNED NOT NULL, + `msguid` BIGINT UNSIGNED NOT NULL, + `uid` VARCHAR(128) CHARACTER SET ascii NOT NULL, + `created` DATETIME DEFAULT NULL, + `changed` DATETIME DEFAULT NULL, + `data` TEXT NOT NULL, + `xml` TEXT NOT NULL, + `tags` VARCHAR(255) NOT NULL, + `words` TEXT NOT NULL, + `dtstart` DATETIME, + `dtend` DATETIME, + CONSTRAINT `fk_kolab_cache_task_folder` FOREIGN KEY (`folder_id`) + REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE, + PRIMARY KEY(`folder_id`,`msguid`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +DROP TABLE IF EXISTS `kolab_cache_journal`; + +CREATE TABLE `kolab_cache_journal` ( + `folder_id` BIGINT UNSIGNED NOT NULL, + `msguid` BIGINT UNSIGNED NOT NULL, + `uid` VARCHAR(128) CHARACTER SET ascii NOT NULL, + `created` DATETIME DEFAULT NULL, + `changed` DATETIME DEFAULT NULL, + `data` TEXT NOT NULL, + `xml` TEXT NOT NULL, + `tags` VARCHAR(255) NOT NULL, + `words` TEXT NOT NULL, + `dtstart` DATETIME, + `dtend` DATETIME, + CONSTRAINT `fk_kolab_cache_journal_folder` FOREIGN KEY (`folder_id`) + REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE, + PRIMARY KEY(`folder_id`,`msguid`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +DROP TABLE IF EXISTS `kolab_cache_note`; + +CREATE TABLE `kolab_cache_note` ( + `folder_id` BIGINT UNSIGNED NOT NULL, + `msguid` BIGINT UNSIGNED NOT NULL, + `uid` VARCHAR(128) CHARACTER SET ascii NOT NULL, + `created` DATETIME DEFAULT NULL, + `changed` DATETIME DEFAULT NULL, + `data` TEXT NOT NULL, + `xml` TEXT NOT NULL, + `tags` VARCHAR(255) NOT NULL, + `words` TEXT NOT NULL, + CONSTRAINT `fk_kolab_cache_note_folder` FOREIGN KEY (`folder_id`) + REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE, + PRIMARY KEY(`folder_id`,`msguid`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +DROP TABLE IF EXISTS `kolab_cache_file`; + +CREATE TABLE `kolab_cache_file` ( + `folder_id` BIGINT UNSIGNED NOT NULL, + `msguid` BIGINT UNSIGNED NOT NULL, + `uid` VARCHAR(128) CHARACTER SET ascii NOT NULL, + `created` DATETIME DEFAULT NULL, + `changed` DATETIME DEFAULT NULL, + `data` TEXT NOT NULL, + `xml` TEXT NOT NULL, `tags` VARCHAR(255) NOT NULL, `words` TEXT NOT NULL, `filename` varchar(255) DEFAULT NULL, - PRIMARY KEY(`resource`,`type`,`msguid`), - INDEX `resource_filename` (`resource`, `filename`) + CONSTRAINT `fk_kolab_cache_file_folder` FOREIGN KEY (`folder_id`) + REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE, + PRIMARY KEY(`folder_id`,`msguid`), + INDEX `folder_filename` (`folder_id`, `filename`) ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; -INSERT INTO `system` (`name`, `value`) VALUES ('libkolab-version', '2013041900'); +DROP TABLE IF EXISTS `kolab_cache_configuration`; + +CREATE TABLE `kolab_cache_configuration` ( + `folder_id` BIGINT UNSIGNED NOT NULL, + `msguid` BIGINT UNSIGNED NOT NULL, + `uid` VARCHAR(128) CHARACTER SET ascii NOT NULL, + `created` DATETIME DEFAULT NULL, + `changed` DATETIME DEFAULT NULL, + `data` TEXT NOT NULL, + `xml` TEXT NOT NULL, + `tags` VARCHAR(255) NOT NULL, + `words` TEXT NOT NULL, + `type` VARCHAR(32) CHARACTER SET ascii NOT NULL, + CONSTRAINT `fk_kolab_cache_configuration_folder` FOREIGN KEY (`folder_id`) + REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE, + PRIMARY KEY(`folder_id`,`msguid`), + INDEX `configuration_type` (`folder_id`,`type`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +DROP TABLE IF EXISTS `kolab_cache_freebusy`; + +CREATE TABLE `kolab_cache_freebusy` ( + `folder_id` BIGINT UNSIGNED NOT NULL, + `msguid` BIGINT UNSIGNED NOT NULL, + `uid` VARCHAR(128) CHARACTER SET ascii NOT NULL, + `created` DATETIME DEFAULT NULL, + `changed` DATETIME DEFAULT NULL, + `data` TEXT NOT NULL, + `xml` TEXT NOT NULL, + `tags` VARCHAR(255) NOT NULL, + `words` TEXT NOT NULL, + `dtstart` DATETIME, + `dtend` DATETIME, + CONSTRAINT `fk_kolab_cache_freebusy_folder` FOREIGN KEY (`folder_id`) + REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE, + PRIMARY KEY(`folder_id`,`msguid`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + + +INSERT INTO `system` (`name`, `value`) VALUES ('libkolab-version', '2013100400');
View file
kolab-syncroton-2.2.1.tar.gz/lib/plugins/libkolab/bin/modcache.sh -> kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/bin/modcache.sh
Changed
@@ -4,7 +4,7 @@ /** * Kolab storage cache modification script * - * @version 3.0 + * @version 3.1 * @author Thomas Bruederli <bruederli@kolabsys.com> * * Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com> @@ -56,7 +56,14 @@ $opts['username'] = !empty($opts[1]) ? $opts[1] : $opts['user']; $action = $opts[0]; -$rcmail = rcube::get_instance(); +$rcmail = rcube::get_instance(rcube::INIT_WITH_DB | rcube::INIT_WITH_PLUGINS); + + +// connect to database +$db = $rcmail->get_dbh(); +$db->db_connect('w'); +if (!$db->is_connected() || $db->is_error()) + die("No DB connection\n"); /* @@ -68,32 +75,42 @@ * Clear/expunge all cache records */ case 'expunge': + $folder_types = $opts['type'] ? explode(',', $opts['type']) : array('contact','configuration','event','file','journal','note','task'); + $folder_types_db = array_map(array($db, 'quote'), $folder_types); $expire = strtotime(!empty($opts[2]) ? $opts[2] : 'now - 10 days'); - $sql_add = " AND created <= '" . date('Y-m-d 00:00:00', $expire) . "'"; + $sql_where = "type IN (" . join(',', $folder_types_db) . ")"; + + if ($opts['username']) { + $sql_where .= ' AND resource LIKE ?'; + } + + $sql_query = "DELETE FROM %s WHERE folder_id IN (SELECT folder_id FROM kolab_folders WHERE $sql_where) AND created <= " . $db->quote(date('Y-m-d 00:00:00', $expire)); if ($opts['limit']) { - $sql_add .= ' LIMIT ' . intval($opts['limit']); + $sql_query = ' LIMIT ' . intval($opts['limit']); + } + foreach ($folder_types as $type) { + $table_name = 'kolab_cache_' . $type; + $db->query(sprintf($sql_query, $table_name), resource_prefix($opts).'%'); + echo $db->affected_rows() . " records deleted from '$table_name'\n"; } -case 'clear': - // connect to database - $db = $rcmail->get_dbh(); - $db->db_connect('w'); - if (!$db->is_connected() || $db->is_error()) - die("No DB connection\n"); + $db->query("UPDATE kolab_folders SET ctag='' WHERE $sql_where", resource_prefix($opts).'%'); + break; - $folder_types = $opts['type'] ? explode(',', $opts['type']) : array('contact','distribution-list','event','task','configuration','file'); +case 'clear': + $folder_types = $opts['type'] ? explode(',', $opts['type']) : array('contact','configuration','event','file','journal','note','task'); $folder_types_db = array_map(array($db, 'quote'), $folder_types); if ($opts['all']) { - $sql_query = "DELETE FROM kolab_cache WHERE type IN (" . join(',', $folder_types_db) . ")"; + $sql_query = "DELETE FROM kolab_folders WHERE 1"; } else if ($opts['username']) { - $sql_query = "DELETE FROM kolab_cache WHERE type IN (" . join(',', $folder_types_db) . ") AND resource LIKE ?"; + $sql_query = "DELETE FROM kolab_folders WHERE type IN (" . join(',', $folder_types_db) . ") AND resource LIKE ?"; } if ($sql_query) { $db->query($sql_query . $sql_add, resource_prefix($opts).'%'); - echo $db->affected_rows() . " records deleted from 'kolab_cache'\n"; + echo $db->affected_rows() . " records deleted from 'kolab_folders'\n"; } break; @@ -106,7 +123,7 @@ $rcmail->plugins->load_plugin('libkolab'); if (authenticate($opts)) { - $folder_types = $opts['type'] ? explode(',', $opts['type']) : array('contact','event','task','configuration','file'); + $folder_types = $opts['type'] ? explode(',', $opts['type']) : array('contact','configuration','event','file','task'); foreach ($folder_types as $type) { // sync every folder of the given type foreach (kolab_storage::get_folders($type) as $folder) { @@ -140,7 +157,7 @@ */ function resource_prefix($opts) { - return 'imap://' . urlencode($opts['username']) . '@' . $opts['host'] . '/'; + return 'imap://' . str_replace('%', '\\%', urlencode($opts['username'])) . '@' . $opts['host'] . '/'; }
View file
kolab-syncroton-2.2.1.tar.gz/lib/plugins/libkolab/config.inc.php.dist -> kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/config.inc.php.dist
Changed
@@ -12,9 +12,6 @@ // Defaults to https://<imap-server->/freebusy $rcmail_config['kolab_freebusy_server'] = 'https://<some-host>/<freebusy-path>'; -// Set this option to disable SSL certificate checks when triggering Free/Busy (enabled by default) -$rcmail_config['kolab_ssl_verify_peer'] = false; - // Enables listing of only subscribed folders. This e.g. will limit // folders in calendar view or available addressbooks $rcmail_config['kolab_use_subscriptions'] = false; @@ -23,4 +20,13 @@ // for displaying resource folder names (experimental!) $rcmail_config['kolab_custom_display_names'] = false; -?> +// Configuration of HTTP requests. +// See http://pear.php.net/manual/en/package.http.http-request2.config.php +// for list of supported configuration options (array keys) +$rcmail_config['kolab_http_request'] = array(); + +// When kolab_cache is enabled Roundcube's messages cache will be redundant +// when working on kolab folders. Here we can: +// 2 - bypass messages/indexes cache completely +// 1 - bypass only messages, but use index cache +$rcmail_config['kolab_messages_cache_bypass'] = 0;
View file
kolab-syncroton-2.2.1.tar.gz/lib/plugins/libkolab/lib/kolab_format.php -> kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/lib/kolab_format.php
Changed
@@ -40,6 +40,7 @@ protected $data; protected $xmldata; protected $xmlobject; + protected $formaterror; protected $loaded = false; protected $version = '3.0'; @@ -104,13 +105,17 @@ } $result = new cDateTime(); - // got a unix timestamp (in UTC) - if (is_numeric($datetime)) { - $datetime = new DateTime('@'.$datetime, new DateTimeZone('UTC')); - if ($tz) $datetime->setTimezone($tz); + try { + // got a unix timestamp (in UTC) + if (is_numeric($datetime)) { + $datetime = new DateTime('@'.$datetime, new DateTimeZone('UTC')); + if ($tz) $datetime->setTimezone($tz); + } + else if (is_string($datetime) && strlen($datetime)) { + $datetime = new DateTime($datetime, $tz ?: null); + } } - else if (is_string($datetime) && strlen($datetime)) - $datetime = new DateTime($datetime, $tz ?: null); + catch (Exception $e) {} if ($datetime instanceof DateTime) { $result->setDate($datetime->format('Y'), $datetime->format('n'), $datetime->format('j')); @@ -244,7 +249,7 @@ $log = "Error"; } - if ($log) { + if ($log && !isset($this->formaterror)) { rcube::raise_error(array( 'code' => 660, 'type' => 'php', @@ -252,6 +257,8 @@ 'line' => __LINE__, 'message' => "kolabformat $log: " . kolabformat::errorMessage(), ), true); + + $this->formaterror = $ret; } return $ret; @@ -338,6 +345,7 @@ */ public function load($xml) { + $this->formaterror = null; $read_func = $this->libfunc($this->read_func); if (is_array($read_func)) @@ -361,6 +369,8 @@ */ public function write($version = null) { + $this->formaterror = null; + $this->init(); $write_func = $this->libfunc($this->write_func); if (is_array($write_func)) @@ -389,24 +399,33 @@ $this->obj->setUid($object['uid']); // set some automatic values if missing - if (method_exists($this->obj, 'setCreated') && !$this->obj->created()) { - if (empty($object['created'])) - $object['created'] = new DateTime('now', self::$timezone); - $this->obj->setCreated(self::get_datetime($object['created'])); + if (empty($object['created']) && method_exists($this->obj, 'setCreated')) { + $cdt = $this->obj->created(); + $object['created'] = $cdt && $cdt->isValid() ? self::php_datetime($cdt) : new DateTime('now', self::$timezone); + if (!$cdt || !$cdt->isValid()) + $this->obj->setCreated(self::get_datetime($object['created'])); } $object['changed'] = new DateTime('now', self::$timezone); $this->obj->setLastModified(self::get_datetime($object['changed'], new DateTimeZone('UTC'))); // Save custom properties of the given object - if (!empty($object['x-custom'])) { + if (isset($object['x-custom'])) { $vcustom = new vectorcs; - foreach ($object['x-custom'] as $cp) { + foreach ((array)$object['x-custom'] as $cp) { if (is_array($cp)) $vcustom->push(new CustomProperty($cp[0], $cp[1])); } $this->obj->setCustomProperties($vcustom); } + else { // load custom properties from XML for caching (#2238) + $object['x-custom'] = array(); + $vcustom = $this->obj->customProperties(); + for ($i=0; $i < $vcustom->size(); $i++) { + $cp = $vcustom->get($i); + $object['x-custom'][] = array($cp->identifier, $cp->value); + } + } } /**
View file
kolab-syncroton-2.2.1.tar.gz/lib/plugins/libkolab/lib/kolab_format_contact.php -> kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/lib/kolab_format_contact.php
Changed
@@ -268,7 +268,7 @@ */ public function is_valid() { - return $this->data || (is_object($this->obj) && $this->obj->uid() /*$this->obj->isValid()*/); + return !$this->formaterror && ($this->data || (is_object($this->obj) && $this->obj->uid() /*$this->obj->isValid()*/)); } /**
View file
kolab-syncroton-2.2.1.tar.gz/lib/plugins/libkolab/lib/kolab_format_distributionlist.php -> kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/lib/kolab_format_distributionlist.php
Changed
@@ -69,7 +69,7 @@ public function is_valid() { - return $this->data || (is_object($this->obj) && $this->obj->isValid()); + return !$this->formaterror && ($this->data || (is_object($this->obj) && $this->obj->isValid())); } /**
View file
kolab-syncroton-2.2.1.tar.gz/lib/plugins/libkolab/lib/kolab_format_event.php -> kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/lib/kolab_format_event.php
Changed
@@ -111,7 +111,8 @@ */ public function is_valid() { - return $this->data || (is_object($this->obj) && $this->obj->isValid() && $this->obj->uid()); + return !$this->formaterror && (($this->data && !empty($this->data['start']) && !empty($this->data['end'])) || + (is_object($this->obj) && $this->obj->isValid() && $this->obj->uid())); } /** @@ -138,6 +139,17 @@ 'attendees' => array(), ); + // derive event end from duration (#1916) + if (!$object['end'] && $object['start'] && ($duration = $this->obj->duration()) && $duration->isValid()) { + $interval = new DateInterval('PT0S'); + $interval->d = $duration->weeks() * 7 + $duration->days(); + $interval->h = $duration->hours(); + $interval->i = $duration->minutes(); + $interval->s = $duration->seconds(); + $object['end'] = clone $object['start']; + $object['end']->add($interval); + } + // organizer is part of the attendees list in Roundcube if ($object['organizer']) { $object['organizer']['role'] = 'ORGANIZER';
View file
kolab-syncroton-2.2.1.tar.gz/lib/plugins/libkolab/lib/kolab_format_file.php -> kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/lib/kolab_format_file.php
Changed
@@ -95,7 +95,7 @@ */ public function is_valid() { - return $this->data || (is_object($this->obj) && $this->obj->isValid()); + return !$this->formaterror && ($this->data || (is_object($this->obj) && $this->obj->isValid())); } /**
View file
kolab-syncroton-2.2.1.tar.gz/lib/plugins/libkolab/lib/kolab_format_journal.php -> kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/lib/kolab_format_journal.php
Changed
@@ -54,7 +54,7 @@ */ public function is_valid() { - return $this->data || (is_object($this->obj) && $this->obj->isValid()); + return !$this->formaterror && ($this->data || (is_object($this->obj) && $this->obj->isValid())); } /**
View file
kolab-syncroton-2.2.1.tar.gz/lib/plugins/libkolab/lib/kolab_format_note.php -> kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/lib/kolab_format_note.php
Changed
@@ -54,7 +54,7 @@ */ public function is_valid() { - return $this->data || (is_object($this->obj) && $this->obj->isValid()); + return !$this->formaterror && ($this->data || (is_object($this->obj) && $this->obj->isValid())); } /**
View file
kolab-syncroton-2.2.1.tar.gz/lib/plugins/libkolab/lib/kolab_format_task.php -> kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/lib/kolab_format_task.php
Changed
@@ -63,7 +63,7 @@ */ public function is_valid() { - return $this->data || (is_object($this->obj) && $this->obj->isValid()); + return !$this->formaterror && ($this->data || (is_object($this->obj) && $this->obj->isValid())); } /**
View file
kolab-syncroton-2.2.1.tar.gz/lib/plugins/libkolab/lib/kolab_format_xcal.php -> kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/lib/kolab_format_xcal.php
Changed
@@ -385,7 +385,7 @@ if (preg_match('/^@(\d+)/', $offset, $d)) { $alarm->setStart(self::get_datetime($d[1], new DateTimeZone('UTC'))); } - else if (preg_match('/^([-+]?)(\d+)([SMHDW])/', $offset, $d)) { + else if (preg_match('/^([-+]?)P?T?(\d+)([SMHDW])/', $offset, $d)) { $days = $hours = $minutes = $seconds = 0; switch ($d[3]) { case 'W': $days = 7*intval($d[2]); break;
View file
kolab-syncroton-2.2.1.tar.gz/lib/plugins/libkolab/lib/kolab_storage.php -> kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/lib/kolab_storage.php
Changed
@@ -31,6 +31,9 @@ const COLOR_KEY_PRIVATE = '/private/vendor/kolab/color'; const NAME_KEY_SHARED = '/shared/vendor/kolab/displayname'; const NAME_KEY_PRIVATE = '/private/vendor/kolab/displayname'; + const UID_KEY_SHARED = '/shared/vendor/kolab/uniqueid'; + const UID_KEY_PRIVATE = '/private/vendor/kolab/uniqueid'; + const UID_KEY_CYRUS = '/shared/vendor/cmu/cyrus-imapd/uniqueid'; public static $version = '3.0'; public static $last_error; @@ -103,15 +106,16 @@ * Get a list of storage folders for the given data type * * @param string Data type to list folders for (contact,distribution-list,event,task,note) + * @param boolean Enable to return subscribed folders only (null to use configured subscription mode) * * @return array List of Kolab_Folder objects (folder names in UTF7-IMAP) */ - public static function get_folders($type) + public static function get_folders($type, $subscribed = null) { $folders = $folderdata = array(); if (self::setup()) { - foreach ((array)self::list_folders('', '*', $type, null, $folderdata) as $foldername) { + foreach ((array)self::list_folders('', '*', $type, $subscribed, $folderdata) as $foldername) { $folders[$foldername] = new kolab_storage_folder($foldername, $folderdata[$foldername]); } } @@ -154,7 +158,7 @@ * This will search all folders storing objects of the given type. * * @param string Object UID - * @param string Object type (contact,distribution-list,event,task,note) + * @param string Object type (contact,event,task,journal,file,note,configuration) * @return array The Kolab object represented as hash array or false if not found */ public static function get_object($uid, $type) @@ -167,7 +171,7 @@ else $folder->set_folder($foldername); - if ($object = $folder->get_object($uid)) + if ($object = $folder->get_object($uid, '*')) return $object; } @@ -276,9 +280,22 @@ { self::setup(); + $oldfolder = self::get_folder($oldname); + $active = self::folder_is_active($oldname); $success = self::$imap->rename_folder($oldname, $newname); self::$last_error = self::$imap->get_error_str(); + // pass active state to new folder name + if ($success && $active) { + self::set_state($oldnam, false); + self::set_state($newname, true); + } + + // assign existing cache entries to new resource uri + if ($success && $oldfolder) { + $oldfolder->cache->rename($newname); + } + return $success; } @@ -388,11 +405,8 @@ self::setup(); // find custom display name in folder METADATA - if (self::$config->get('kolab_custom_display_names', true)) { - $metadata = self::$imap->get_metadata($folder, array(self::NAME_KEY_PRIVATE, self::NAME_KEY_SHARED)); - if (($name = $metadata[$folder][self::NAME_KEY_PRIVATE]) || ($name = $metadata[$folder][self::NAME_KEY_SHARED])) { - return $name; - } + if ($name = self::custom_displayname($folder)) { + return $name; } $found = false; @@ -461,6 +475,21 @@ return $folder; } + /** + * Get custom display name (saved in metadata) for the given folder + */ + public static function custom_displayname($folder) + { + // find custom display name in folder METADATA + if (self::$config->get('kolab_custom_display_names', true)) { + $metadata = self::$imap->get_metadata($folder, array(self::NAME_KEY_PRIVATE, self::NAME_KEY_SHARED)); + if (($name = $metadata[$folder][self::NAME_KEY_PRIVATE]) || ($name = $metadata[$folder][self::NAME_KEY_SHARED])) { + return $name; + } + } + + return false; + } /** * Helper method to generate a truncated folder name to display @@ -475,7 +504,7 @@ $length = strlen($names[$i] . ' » '); $prefix = substr($name, 0, $length); $count = count(explode(' » ', $prefix)); - $name = str_repeat(' ', $count-1) . '» ' . substr($name, $length); + $name = str_repeat(' ', $count-1) . '» ' . substr($name, $length); break; } } @@ -497,7 +526,7 @@ public static function folder_selector($type, $attrs, $current = '') { // get all folders of specified type - $folders = self::get_folders($type); + $folders = self::get_folders($type, false); $delim = self::$imap->get_hierarchy_delimiter(); $names = array(); @@ -570,7 +599,7 @@ * * @param string Optional root folder * @param string Optional name pattern - * @param string Data type to list folders for (contact,distribution-list,event,task,note,mail) + * @param string Data type to list folders for (contact,event,task,journal,file,note,mail,configuration) * @param boolean Enable to return subscribed folders only (null to use configured subscription mode) * @param array Will be filled with folder-types data * @@ -654,11 +683,12 @@ */ public static function sort_folders($folders) { + $pad = ' '; $nsnames = array('personal' => array(), 'shared' => array(), 'other' => array()); foreach ($folders as $folder) { $folders[$folder->name] = $folder; $ns = $folder->get_namespace(); - $nsnames[$ns][$folder->name] = strtolower(html_entity_decode(self::object_name($folder->name, $ns), ENT_COMPAT, RCUBE_CHARSET)); // decode » + $nsnames[$ns][$folder->name] = strtolower(html_entity_decode(self::object_name($folder->name, $ns), ENT_COMPAT, RCUBE_CHARSET)) . $pad; // decode » } $names = array(); @@ -963,7 +993,7 @@ } if (!self::$imap->folder_exists($folder)) { - if (!self::$imap->folder_create($folder)) { + if (!self::$imap->create_folder($folder)) { return; } }
View file
kolab-syncroton-2.2.1.tar.gz/lib/plugins/libkolab/lib/kolab_storage_cache.php -> kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/lib/kolab_storage_cache.php
Changed
@@ -6,7 +6,7 @@ * @version @package_version@ * @author Thomas Bruederli <bruederli@kolabsys.com> * - * Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com> + * Copyright (C) 2012-2013, Kolab Systems AG <contact@kolabsys.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -24,24 +24,44 @@ class kolab_storage_cache { - private $db; - private $imap; - private $folder; - private $uid2msg; - private $objects; - private $index = array(); - private $resource_uri; - private $enabled = true; - private $synched = false; - private $synclock = false; - private $ready = false; - private $max_sql_packet; - private $max_sync_lock_time = 600; - private $binary_items = array( - 'photo' => '|<photo><uri>[^;]+;base64,([^<]+)</uri></photo>|i', - 'pgppublickey' => '|<key><uri>date:application/pgp-keys;base64,([^<]+)</uri></photo>|i', - 'pkcs7publickey' => '|<key><uri>date:application/pkcs7-mime;base64,([^<]+)</uri></photo>|i', - ); + protected $db; + protected $imap; + protected $folder; + protected $uid2msg; + protected $objects; + protected $index = array(); + protected $metadata = array(); + protected $folder_id; + protected $resource_uri; + protected $enabled = true; + protected $synched = false; + protected $synclock = false; + protected $ready = false; + protected $cache_table; + protected $folders_table; + protected $max_sql_packet; + protected $max_sync_lock_time = 600; + protected $binary_items = array(); + protected $extra_cols = array(); + + + /** + * Factory constructor + */ + public static function factory(kolab_storage_folder $storage_folder) + { + $subclass = 'kolab_storage_cache_' . $storage_folder->type; + if (class_exists($subclass)) { + return new $subclass($storage_folder); + } + else { + rcube::raise_error(array( + 'code' => 900, + 'type' => 'php', + 'message' => "No kolab_storage_cache class found for folder of type " . $storage_folder->type + ), true); + } + } /** @@ -55,6 +75,8 @@ $this->enabled = $rcmail->config->get('kolab_cache', false); if ($this->enabled) { + // always read folder cache and lock state from DB master + $this->db->set_table_dsn('kolab_folders', 'w'); // remove sync-lock on script termination $rcmail->add_shutdown_function(array($this, '_sync_unlock')); } @@ -80,9 +102,19 @@ // compose fully qualified ressource uri for this instance $this->resource_uri = $this->folder->get_resource_uri(); + $this->folders_table = $this->db->table_name('kolab_folders'); + $this->cache_table = $this->db->table_name('kolab_cache_' . $this->folder->type); $this->ready = $this->enabled; + $this->folder_id = null; } + /** + * Returns true if this cache supports query by type + */ + public function has_type_col() + { + return in_array('type', $this->extra_cols); + } /** * Synchronize local cache data with remote @@ -96,52 +128,65 @@ // increase time limit @set_time_limit($this->max_sync_lock_time); - // lock synchronization for this folder or wait if locked - $this->_sync_lock(); + // read cached folder metadata + $this->_read_folder_data(); - // synchronize IMAP mailbox cache - $this->imap->folder_sync($this->folder->name); + // check cache status hash first ($this->metadata is set in _read_folder_data()) + if ($this->metadata['ctag'] != $this->folder->get_ctag()) { + // lock synchronization for this folder or wait if locked + $this->_sync_lock(); - // compare IMAP index with object cache index - $imap_index = $this->imap->index($this->folder->name); - $this->index = $imap_index->get(); + // disable messages cache if configured to do so + $this->bypass(true); - // determine objects to fetch or to invalidate - if ($this->ready) { - // read cache index - $sql_result = $this->db->query( - "SELECT msguid, uid FROM kolab_cache WHERE resource=? AND type<>?", - $this->resource_uri, - 'lock' - ); + // synchronize IMAP mailbox cache + $this->imap->folder_sync($this->folder->name); - $old_index = array(); - while ($sql_arr = $this->db->fetch_assoc($sql_result)) { - $old_index[] = $sql_arr['msguid']; - $this->uid2msg[$sql_arr['uid']] = $sql_arr['msguid']; - } + // compare IMAP index with object cache index + $imap_index = $this->imap->index($this->folder->name, null, null, true, true); + $this->index = $imap_index->get(); - // fetch new objects from imap - foreach (array_diff($this->index, $old_index) as $msguid) { - if ($object = $this->folder->read_object($msguid, '*')) { - $this->_extended_insert($msguid, $object); - } - } - $this->_extended_insert(0, null); - - // delete invalid entries from local DB - $del_index = array_diff($old_index, $this->index); - if (!empty($del_index)) { - $quoted_ids = join(',', array_map(array($this->db, 'quote'), $del_index)); - $this->db->query( - "DELETE FROM kolab_cache WHERE resource=? AND msguid IN ($quoted_ids)", - $this->resource_uri + // determine objects to fetch or to invalidate + if ($this->ready) { + // read cache index + $sql_result = $this->db->query( + "SELECT msguid, uid FROM $this->cache_table WHERE folder_id=?", + $this->folder_id ); + + $old_index = array(); + while ($sql_arr = $this->db->fetch_assoc($sql_result)) { + $old_index[] = $sql_arr['msguid']; + $this->uid2msg[$sql_arr['uid']] = $sql_arr['msguid']; + } + + // fetch new objects from imap + foreach (array_diff($this->index, $old_index) as $msguid) { + if ($object = $this->folder->read_object($msguid, '*')) { + $this->_extended_insert($msguid, $object); + } + } + $this->_extended_insert(0, null); + + // delete invalid entries from local DB + $del_index = array_diff($old_index, $this->index); + if (!empty($del_index)) { + $quoted_ids = join(',', array_map(array($this->db, 'quote'), $del_index)); + $this->db->query( + "DELETE FROM $this->cache_table WHERE folder_id=? AND msguid IN ($quoted_ids)", + $this->folder_id + ); + } + + // update ctag value (will be written to database in _sync_unlock()) + $this->metadata['ctag'] = $this->folder->get_ctag(); } - } - // remove lock - $this->_sync_unlock(); + $this->bypass(false); + + // remove lock + $this->_sync_unlock(); + } $this->synched = time(); } @@ -165,11 +210,12 @@ // load object if not in memory if (!isset($this->objects[$msguid])) { if ($this->ready) { + $this->_read_folder_data(); + $sql_result = $this->db->query( - "SELECT * FROM kolab_cache ". - "WHERE resource=? AND type=? AND msguid=?", - $this->resource_uri, - $type ?: $this->folder->type, + "SELECT * FROM $this->cache_table ". + "WHERE folder_id=? AND msguid=?", + $this->folder_id, $msguid ); @@ -210,8 +256,9 @@ // remove old entry if ($this->ready) { - $this->db->query("DELETE FROM kolab_cache WHERE resource=? AND msguid=? AND type<>?", - $this->resource_uri, $msguid, 'lock'); + $this->_read_folder_data(); + $this->db->query("DELETE FROM $this->cache_table WHERE folder_id=? AND msguid=?", + $this->folder_id, $msguid); } if ($object) { @@ -235,27 +282,33 @@ { // write to cache if ($this->ready) { + $this->_read_folder_data(); + $sql_data = $this->_serialize($object); - $objtype = $object['_type'] ? $object['_type'] : $this->folder->type; - $result = $this->db->query( - "INSERT INTO kolab_cache ". - " (resource, type, msguid, uid, created, changed, data, xml, dtstart, dtend, tags, words, filename)". - " VALUES (?, ?, ?, ?, " . $this->db->now() . ", ?, ?, ?, ?, ?, ?, ?, ?)", - $this->resource_uri, - $objtype, + $extra_cols = $this->extra_cols ? ', ' . join(', ', $this->extra_cols) : ''; + $extra_fields = $this->extra_cols ? str_repeat(', ?', count($this->extra_cols)) : ''; + + $args = array( + "INSERT INTO $this->cache_table ". + " (folder_id, msguid, uid, created, changed, data, xml, tags, words $extra_cols)". + " VALUES (?, ?, ?, " . $this->db->now() . ", ?, ?, ?, ?, ? $extra_fields)", + $this->folder_id, $msguid, $object['uid'], $sql_data['changed'], $sql_data['data'], $sql_data['xml'], - $sql_data['dtstart'], - $sql_data['dtend'], $sql_data['tags'], $sql_data['words'], - $sql_data['filename'] ); + foreach ($this->extra_cols as $col) { + $args[] = $sql_data[$col]; + } + + $result = call_user_func_array(array($this->db, 'query'), $args); + if (!$this->db->affected_rows($result)) { rcube::raise_error(array( 'code' => 900, 'type' => 'php', @@ -283,14 +336,15 @@ // resolve new message UID in target folder if ($new_msguid = $target->cache->uid2msguid($uid)) { + $this->_read_folder_data(); + $this->db->query( - "UPDATE kolab_cache SET resource=?, msguid=? ". - "WHERE resource=? AND msguid=? AND type<>?", - $target->get_resource_uri(), + "UPDATE $this->cache_table SET folder_id=?, msguid=? ". + "WHERE folder_id=? AND msguid=?", + $target->folder_id, $new_msguid, - $this->resource_uri, - $msguid, - 'lock' + $this->folder_id, + $msguid ); } else { @@ -307,15 +361,32 @@ */ public function purge($type = null) { + $this->_read_folder_data(); + $result = $this->db->query( - "DELETE FROM kolab_cache WHERE resource=?". - ($type ? ' AND type=?' : ''), - $this->resource_uri, - $type + "DELETE FROM $this->cache_table WHERE folder_id=?". + $this->folder_id ); return $this->db->affected_rows($result); } + /** + * Update resource URI for existing cache entries + * + * @param string Target IMAP folder to move it to + */ + public function rename($new_folder) + { + $target = kolab_storage::get_folder($new_folder); + + // resolve new message UID in target folder + $this->db->query( + "UPDATE $this->folders_table SET resource=? ". + "WHERE resource=?", + $target->get_resource_uri(), + $this->resource_uri + ); + } /** * Select Kolab objects filtered by the given query @@ -331,10 +402,12 @@ // read from local cache DB (assume it to be synchronized) if ($this->ready) { + $this->_read_folder_data(); + $sql_result = $this->db->query( - "SELECT " . ($uids ? 'msguid, uid' : '*') . " FROM kolab_cache ". - "WHERE resource=? " . $this->_sql_where($query), - $this->resource_uri + "SELECT " . ($uids ? 'msguid, uid' : '*') . " FROM $this->cache_table ". + "WHERE folder_id=? " . $this->_sql_where($query), + $this->folder_id ); while ($sql_arr = $this->db->fetch_assoc($sql_result)) { @@ -391,10 +464,12 @@ // cache is in sync, we can count records in local DB if ($this->synched) { + $this->_read_folder_data(); + $sql_result = $this->db->query( - "SELECT COUNT(*) AS numrows FROM kolab_cache ". - "WHERE resource=? " . $this->_sql_where($query), - $this->resource_uri + "SELECT COUNT(*) AS numrows FROM $this->cache_table ". + "WHERE folder_id=? " . $this->_sql_where($query), + $this->folder_id ); $sql_arr = $this->db->fetch_assoc($sql_result); @@ -415,10 +490,10 @@ /** * Helper method to compose a valid SQL query from pseudo filter triplets */ - private function _sql_where($query) + protected function _sql_where($query) { $sql_where = ''; - foreach ($query as $param) { + foreach ((array) $query as $param) { if (is_array($param[0])) { $subq = array(); foreach ($param[0] as $q) { @@ -460,7 +535,7 @@ * Helper method to convert the given pseudo-query triplets into * an associative filter array with 'equals' values only */ - private function _query2assoc($query) + protected function _query2assoc($query) { // extract object type from query parameter $filter = array(); @@ -479,7 +554,7 @@ * @param string IMAP folder to read from * @return array List of parsed Kolab objects */ - private function _fetch($index, $type = null, $folder = null) + protected function _fetch($index, $type = null, $folder = null) { $results = array(); foreach ((array)$index as $msguid) { @@ -501,13 +576,19 @@ * @param string IMAP folder to read from * @return array List of parsed Kolab objects */ - private function _fetch_uids($index, $type = null) + protected function _fetch_uids($index, $type = null) { if (!$type) $type = $this->folder->type; + $this->bypass(true); + $results = array(); - foreach ((array)$this->imap->fetch_headers($this->folder->name, $index, false) as $msguid => $headers) { + $headers = $this->imap->fetch_headers($this->folder->name, $index, false); + + $this->bypass(false); + + foreach ((array)$headers as $msguid => $headers) { $object_type = kolab_format::mime2object_type($headers->others['x-kolab-type']); // check object type header and abort on mismatch @@ -526,35 +607,9 @@ /** * Helper method to convert the given Kolab object into a dataset to be written to cache */ - private function _serialize($object) + protected function _serialize($object) { - $sql_data = array('changed' => null, 'dtstart' => null, 'dtend' => null, 'xml' => '', 'tags' => '', 'words' => ''); - $objtype = $object['_type'] ? $object['_type'] : $this->folder->type; - - // set type specific values - if ($objtype == 'event') { - // database runs in server's timezone so using date() is what we want - $sql_data['dtstart'] = date('Y-m-d H:i:s', is_object($object['start']) ? $object['start']->format('U') : $object['start']); - $sql_data['dtend'] = date('Y-m-d H:i:s', is_object($object['end']) ? $object['end']->format('U') : $object['end']); - - // extend date range for recurring events - if ($object['recurrence'] && $object['_formatobj']) { - $recurrence = new kolab_date_recurrence($object['_formatobj']); - $sql_data['dtend'] = date('Y-m-d 23:59:59', $recurrence->end() ?: strtotime('now +1 year')); - } - } - else if ($objtype == 'task') { - if ($object['start']) - $sql_data['dtstart'] = date('Y-m-d H:i:s', is_object($object['start']) ? $object['start']->format('U') : $object['start']); - if ($object['due']) - $sql_data['dtend'] = date('Y-m-d H:i:s', is_object($object['due']) ? $object['due']->format('U') : $object['due']); - } - else if ($objtype == 'file') { - if (!empty($object['_attachments'])) { - reset($object['_attachments']); - $sql_data['filename'] = $object['_attachments'][key($object['_attachments'])]['name']; - } - } + $sql_data = array('changed' => null, 'xml' => '', 'tags' => '', 'words' => ''); if ($object['changed']) { $sql_data['changed'] = date('Y-m-d H:i:s', is_object($object['changed']) ? $object['changed']->format('U') : $object['changed']); @@ -596,7 +651,7 @@ /** * Helper method to turn stored cache data into a valid storage object */ - private function _unserialize($sql_arr) + protected function _unserialize($sql_arr) { $object = unserialize($sql_arr['data']); @@ -608,11 +663,11 @@ } // add meta data - $object['_type'] = $sql_arr['type']; + $object['_type'] = $sql_arr['type'] ?: $this->folder->type; $object['_msguid'] = $sql_arr['msguid']; $object['_mailbox'] = $this->folder->name; $object['_size'] = strlen($sql_arr['xml']); - $object['_formatobj'] = kolab_format::factory($sql_arr['type'], 3.0, $sql_arr['xml']); + $object['_formatobj'] = kolab_format::factory($object['_type'], 3.0, $sql_arr['xml']); return $object; } @@ -623,37 +678,35 @@ * @param int Message UID. Set 0 to commit buffered inserts * @param array Kolab object to cache */ - private function _extended_insert($msguid, $object) + protected function _extended_insert($msguid, $object) { static $buffer = ''; $line = ''; if ($object) { $sql_data = $this->_serialize($object); - $objtype = $object['_type'] ? $object['_type'] : $this->folder->type; - $values = array( - $this->db->quote($this->resource_uri), - $this->db->quote($objtype), + $this->db->quote($this->folder_id), $this->db->quote($msguid), $this->db->quote($object['uid']), $this->db->now(), $this->db->quote($sql_data['changed']), $this->db->quote($sql_data['data']), $this->db->quote($sql_data['xml']), - $this->db->quote($sql_data['dtstart']), - $this->db->quote($sql_data['dtend']), $this->db->quote($sql_data['tags']), $this->db->quote($sql_data['words']), - $this->db->quote($sql_data['filename']), ); + foreach ($this->extra_cols as $col) { + $values[] = $this->db->quote($sql_data[$col]); + } $line = '(' . join(',', $values) . ')'; } if ($buffer && (!$msguid || (strlen($buffer) + strlen($line) > $this->max_sql_packet()))) { + $extra_cols = $this->extra_cols ? ', ' . join(', ', $this->extra_cols) : ''; $result = $this->db->query( - "INSERT INTO kolab_cache ". - " (resource, type, msguid, uid, created, changed, data, xml, dtstart, dtend, tags, words, filename)". + "INSERT INTO $this->cache_table ". + " (folder_id, msguid, uid, created, changed, data, xml, tags, words $extra_cols)". " VALUES $buffer" ); if (!$this->db->affected_rows($result)) { @@ -672,7 +725,7 @@ /** * Returns max_allowed_packet from mysql config */ - private function max_sql_packet() + protected function max_sql_packet() { if (!$this->max_sql_packet) { // mysql limit or max 4 MB @@ -684,16 +737,36 @@ } /** + * Read this folder's ID and cache metadata + */ + protected function _read_folder_data() + { + // already done + if (!empty($this->folder_id)) + return; + + $sql_arr = $this->db->fetch_assoc($this->db->query("SELECT folder_id, synclock, ctag FROM $this->folders_table WHERE resource=?", $this->resource_uri)); + if ($sql_arr) { + $this->metadata = $sql_arr; + $this->folder_id = $sql_arr['folder_id']; + } + else { + $this->db->query("INSERT INTO $this->folders_table (resource, type) VALUES (?, ?)", $this->resource_uri, $this->folder->type); + $this->folder_id = $this->db->insert_id('kolab_folders'); + $this->metadata = array(); + } + } + + /** * Check lock record for this folder and wait if locked or set lock */ - private function _sync_lock() + protected function _sync_lock() { if (!$this->ready) return; - $sql_query = "SELECT msguid AS locked, ".$this->db->unixtimestamp('created')." AS created FROM kolab_cache ". - "WHERE resource=? AND type=?"; - $sql_arr = $this->db->fetch_assoc($this->db->query($sql_query, $this->resource_uri, 'lock')); + $this->_read_folder_data(); + $sql_query = "SELECT synclock, ctag FROM $this->folders_table WHERE folder_id=?"; // abort if database is not set-up if ($this->db->is_error()) { @@ -704,28 +777,13 @@ $this->synclock = true; // wait if locked (expire locks after 10 minutes) - while ($sql_arr && intval($sql_arr['locked']) > 0 && $sql_arr['created'] + $this->max_sync_lock_time > time()) { + while ($this->metadata && intval($this->metadata['synclock']) > 0 && $this->metadata['synclock'] + $this->max_sync_lock_time > time()) { usleep(500000); - $sql_arr = $this->db->fetch_assoc($this->db->query($sql_query, $this->resource_uri, 'lock')); + $this->metadata = $this->db->fetch_assoc($this->db->query($sql_query, $this->folder_id)); } - // create lock record if not exists - if (!$sql_arr) { - $this->db->query( - "INSERT INTO kolab_cache (resource, type, msguid, created, uid, data, xml, tags, words)". - " VALUES (?, ?, 1, " . $this->db->now() . ", '', '', '', '', '')", - $this->resource_uri, - 'lock' - ); - } - else { - $this->db->query( - "UPDATE kolab_cache SET msguid = 1, created = " . $this->db->now() . - " WHERE resource = ? AND type = ?", - $this->resource_uri, - 'lock' - ); - } + // set lock + $this->db->query("UPDATE $this->folders_table SET synclock = ? WHERE folder_id = ?", time(), $this->folder_id); } /** @@ -737,9 +795,9 @@ return; $this->db->query( - "UPDATE kolab_cache SET msguid = 0 WHERE resource = ? AND type = ?", - $this->resource_uri, - 'lock' + "UPDATE $this->folders_table SET synclock = 0, ctag = ? WHERE folder_id = ?", + $this->metadata['ctag'], + $this->folder_id ); $this->synclock = false; @@ -765,4 +823,72 @@ return $this->uid2msg[$uid]; } + /** + * Getter for protected member variables + */ + public function __get($name) + { + if ($name == 'folder_id') { + $this->_read_folder_data(); + } + + return $this->$name; + } + + /** + * Bypass Roundcube messages cache. + * Roundcube cache duplicates information already stored in kolab_cache. + * + * @param bool $disable True disables, False enables messages cache + */ + public function bypass($disable = false) + { + // if kolab cache is disabled do nothing + if (!$this->enabled) { + return; + } + + static $messages_cache, $cache_bypass; + + if ($messages_cache === null) { + $rcmail = rcube::get_instance(); + $messages_cache = (bool) $rcmail->config->get('messages_cache'); + $cache_bypass = (int) $rcmail->config->get('kolab_messages_cache_bypass'); + } + + if ($messages_cache) { + // handle recurrent (multilevel) bypass() calls + if ($disable) { + $this->cache_bypassed += 1; + if ($this->cache_bypassed > 1) { + return; + } + } + else { + $this->cache_bypassed -= 1; + if ($this->cache_bypassed > 0) { + return; + } + } + + switch ($cache_bypass) { + case 2: + // Disable messages cache completely + $this->imap->set_messages_caching(!$disable); + break; + + case 1: + // We'll disable messages cache, but keep index cache. + // Default mode is both (MODE_INDEX | MODE_MESSAGE) + $mode = rcube_imap_cache::MODE_INDEX; + + if (!$disable) { + $mode |= rcube_imap_cache::MODE_MESSAGE; + } + + $this->imap->set_messages_caching(true, $mode); + } + } + } + }
View file
kolab-syncroton-2.2.1.tar.gz/lib/plugins/libkolab/lib/kolab_storage_folder.php -> kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/lib/kolab_storage_folder.php
Changed
@@ -7,7 +7,7 @@ * @author Thomas Bruederli <bruederli@kolabsys.com> * @author Aleksander Machniak <machniak@kolabsys.com> * - * Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com> + * Copyright (C) 2012-2013, Kolab Systems AG <contact@kolabsys.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -43,8 +43,8 @@ public $default = false; /** - * Is this folder set to be default - * @var boolean + * The kolab_storage_cache instance for caching operations + * @var object */ public $cache; @@ -64,7 +64,6 @@ { $this->imap = rcube::get_instance()->get_storage(); $this->imap->set_options(array('skip_deleted' => true)); - $this->cache = new kolab_storage_cache($this); $this->set_folder($name, $type); } @@ -79,11 +78,16 @@ { $this->type_annotation = $ftype ? $ftype : kolab_storage::folder_type($name); + $oldtype = $this->type; list($this->type, $suffix) = explode('.', $this->type_annotation); $this->default = $suffix == 'default'; $this->name = $name; $this->resource_uri = null; + // get a new cache instance of folder type changed + if (!$this->cache || $type != $oldtype) + $this->cache = kolab_storage_cache::factory($this); + $this->imap->set_folder($this->name); $this->cache->set_folder($this); } @@ -92,7 +96,7 @@ /** * */ - private function get_folder_info() + public function get_folder_info() { if (!isset($this->info)) $this->info = $this->imap->folder_info($this->name); @@ -260,6 +264,52 @@ } /** + * Helper method to extract folder UID metadata + * + * @return string Folder's UID + */ + public function get_uid() + { + // UID is defined in folder METADATA + $metakeys = array(kolab_storage::UID_KEY_SHARED, kolab_storage::UID_KEY_PRIVATE, kolab_storage::UID_KEY_CYRUS); + $metadata = $this->get_metadata($metakeys); + foreach ($metakeys as $key) { + if (($uid = $metadata[$key])) { + return $uid; + } + } + + // generate a folder UID and set it to IMAP + $uid = rtrim(chunk_split(md5($this->name . $this->get_owner()), 12, '-'), '-'); + $this->set_uid($uid); + + return $uid; + } + + /** + * Helper method to set an UID value to the given IMAP folder instance + * + * @param string Folder's UID + * @return boolean True on succes, False on failure + */ + public function set_uid($uid) + { + if (!($success = $this->set_metadata(array(kolab_storage::UID_KEY_SHARED => $uid)))) { + $success = $this->set_metadata(array(kolab_storage::UID_KEY_PRIVATE => $uid)); + } + return $success; + } + + /** + * Compose a folder Etag identifier + */ + public function get_ctag() + { + $fdata = $this->get_imap_data(); + return sprintf('%d-%d-%d', $fdata['UIDVALIDITY'], $fdata['HIGHESTMODSEQ'], $fdata['UIDNEXT']); + } + + /** * Check activation status of this folder * * @return boolean True if enabled, false if not @@ -303,7 +353,6 @@ return $subscribed ? kolab_storage::folder_subscribe($this->name) : kolab_storage::folder_unsubscribe($this->name); } - /** * Get number of objects stored in this folder * @@ -312,19 +361,12 @@ * @return integer The number of objects of the given type * @see self::select() */ - public function count($type_or_query = null) + public function count($query = null) { - if (!$type_or_query) - $query = array(array('type','=',$this->type)); - else if (is_string($type_or_query)) - $query = array(array('type','=',$type_or_query)); - else - $query = $this->_prepare_query((array)$type_or_query); - // synchronize cache first $this->cache->synchronize(); - return $this->cache->count($query); + return $this->cache->count($this->_prepare_query($query)); } @@ -342,7 +384,7 @@ $this->cache->synchronize(); // fetch objects from cache - return $this->cache->select(array(array('type','=',$type))); + return $this->cache->select($this->_prepare_query($type)); } @@ -388,10 +430,15 @@ */ private function _prepare_query($query) { - $type = null; - foreach ($query as $i => $param) { - if ($param[0] == 'type') { - $type = $param[2]; + // string equals type query + // FIXME: should not be called this way! + if (is_string($query)) { + return $this->cache->has_type_col() && !empty($query) ? array(array('type','=',$query)) : array(); + } + + foreach ((array)$query as $i => $param) { + if ($param[0] == 'type' && !$this->cache->has_type_col()) { + unset($query[$i]); } else if (($param[0] == 'dtstart' || $param[0] == 'dtend' || $param[0] == 'changed')) { if (is_object($param[2]) && is_a($param[2], 'DateTime')) @@ -401,10 +448,6 @@ } } - // add type selector if not in $query - if (!$type) - $query[] = array('type','=',$this->type); - return $query; } @@ -465,6 +508,7 @@ * @param string The IMAP message UID to fetch * @param string The object type expected (use wildcard '*' to accept all types) * @param string The folder name where the message is stored + * * @return mixed Hash array representing the Kolab object, a kolab_format instance or false if not found */ public function read_object($msguid, $type = null, $folder = null) @@ -474,31 +518,31 @@ $this->imap->set_folder($folder); - $headers = $this->imap->get_message_headers($msguid); - $message = null; + $this->cache->bypass(true); + $message = new rcube_message($msguid); + $this->cache->bypass(false); // Message doesn't exist? - if (empty($headers)) { + if (empty($message->headers)) { return false; } // extract the X-Kolab-Type header from the XML attachment part if missing - if (empty($headers->others['x-kolab-type'])) { - $message = new rcube_message($msguid); + if (empty($message->headers->others['x-kolab-type'])) { foreach ((array)$message->attachments as $part) { if (strpos($part->mimetype, kolab_format::KTYPE_PREFIX) === 0) { - $headers->others['x-kolab-type'] = $part->mimetype; + $message->headers->others['x-kolab-type'] = $part->mimetype; break; } } } // fix buggy messages stating the X-Kolab-Type header twice - else if (is_array($headers->others['x-kolab-type'])) { - $headers->others['x-kolab-type'] = reset($headers->others['x-kolab-type']); + else if (is_array($message->headers->others['x-kolab-type'])) { + $message->headers->others['x-kolab-type'] = reset($message->headers->others['x-kolab-type']); } // no object type header found: abort - if (empty($headers->others['x-kolab-type'])) { + if (empty($message->headers->others['x-kolab-type'])) { rcube::raise_error(array( 'code' => 600, 'type' => 'php', @@ -509,14 +553,13 @@ return false; } - $object_type = kolab_format::mime2object_type($headers->others['x-kolab-type']); - $content_type = kolab_format::KTYPE_PREFIX . $object_type; + $object_type = kolab_format::mime2object_type($message->headers->others['x-kolab-type']); + $content_type = kolab_format::KTYPE_PREFIX . $object_type; // check object type header and abort on mismatch if ($type != '*' && $object_type != $type) return false; - if (!$message) $message = new rcube_message($msguid); $attachments = array(); // get XML part @@ -558,7 +601,7 @@ } // check kolab format version - $format_version = $headers->others['x-kolab-mime-version']; + $format_version = $message->headers->others['x-kolab-mime-version']; if (empty($format_version)) { list($xmltype, $subtype) = explode('.', $object_type); $xmlhead = substr($xml, 0, 512); @@ -651,8 +694,10 @@ $numatt = count($object['_attachments']); foreach ($object['_attachments'] as $key => $attachment) { // FIXME: kolab_storage and Roundcube attachment hooks use different fields! - if (empty($attachment['content']) && !empty($attachment['data'])) + if (empty($attachment['content']) && !empty($attachment['data'])) { $attachment['content'] = $attachment['data']; + unset($attachment['data'], $object['_attachments'][$key]['data']); + } // make sure size is set, so object saved in cache contains this info if (!isset($attachment['size'])) { @@ -710,7 +755,9 @@ // delete old message if ($result && !empty($object['_msguid']) && !empty($object['_mailbox'])) { + $this->cache->bypass(true); $this->imap->delete_message($object['_msguid'], $object['_mailbox']); + $this->cache->bypass(false); $this->cache->set($object['_msguid'], false, $object['_mailbox']); } @@ -805,6 +852,8 @@ $msguid = is_array($object) ? $object['_msguid'] : $this->cache->uid2msguid($object); $success = false; + $this->cache->bypass(true); + if ($msguid && $expunge) { $success = $this->imap->delete_message($msguid, $this->name); } @@ -812,6 +861,8 @@ $success = $this->imap->set_flag($msguid, 'DELETED', $this->name); } + $this->cache->bypass(false); + if ($success) { $this->cache->set($msguid, false); } @@ -826,7 +877,11 @@ public function delete_all() { $this->cache->purge(); - return $this->imap->clear_folder($this->name); + $this->cache->bypass(true); + $result = $this->imap->clear_folder($this->name); + $this->cache->bypass(false); + + return $result; } @@ -839,7 +894,11 @@ public function undelete($uid) { if ($msguid = $this->cache->uid2msguid($uid, true)) { - if ($this->imap->set_flag($msguid, 'UNDELETED', $this->name)) { + $this->cache->bypass(true); + $result = $this->imap->set_flag($msguid, 'UNDELETED', $this->name); + $this->cache->bypass(false); + + if ($result) { return $msguid; } } @@ -858,7 +917,11 @@ public function move($uid, $target_folder) { if ($msguid = $this->cache->uid2msguid($uid)) { - if ($this->imap->move_message($msguid, $target_folder, $this->name)) { + $this->cache->bypass(true); + $result = $this->imap->move_message($msguid, $target_folder, $this->name); + $this->cache->bypass(false); + + if ($result) { $this->cache->move($msguid, $uid, $target_folder); return true; } @@ -1108,9 +1171,7 @@ require_once('HTTP/Request2.php'); try { - $rcmail = rcube::get_instance(); - $request = new HTTP_Request2($url); - $request->setConfig(array('ssl_verify_peer' => $rcmail->config->get('kolab_ssl_verify_peer', true))); + $request = libkolab::http_request($url); // set authentication credentials if ($auth_user && $auth_passwd)
View file
kolab-syncroton-2.2.1.tar.gz/lib/plugins/libkolab/libkolab.php -> kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/libkolab.php
Changed
@@ -27,6 +27,8 @@ class libkolab extends rcube_plugin { + static $http_requests = array(); + /** * Required startup method of a Roundcube plugin */ @@ -59,4 +61,66 @@ $p['fetch_headers'] = trim($p['fetch_headers'] .' X-KOLAB-TYPE X-KOLAB-MIME-VERSION'); return $p; } + + /** + * Wrapper function to load and initalize the HTTP_Request2 Object + * + * @param string|Net_Url2 Request URL + * @param string Request method ('OPTIONS','GET','HEAD','POST','PUT','DELETE','TRACE','CONNECT') + * @param array Configuration for this Request instance, that will be merged + * with default configuration + * + * @return HTTP_Request2 Request object + */ + public static function http_request($url = '', $method = 'GET', $config = array()) + { + $rcube = rcube::get_instance(); + $http_config = (array) $rcube->config->get('kolab_http_request'); + + // deprecated configuration options + if (empty($http_config)) { + foreach (array('ssl_verify_peer', 'ssl_verify_host') as $option) { + $value = $rcube->config->get('kolab_' . $option, true); + if (is_bool($value)) { + $http_config[$option] = $value; + } + } + } + + if (!empty($config)) { + $http_config = array_merge($http_config, $config); + } + + $key = md5(serialize($http_config)); + + if (!($request = self::$http_requests[$key])) { + // load HTTP_Request2 + require_once 'HTTP/Request2.php'; + + try { + $request = new HTTP_Request2(); + $request->setConfig($http_config); + } + catch (Exception $e) { + rcube::raise_error($e, true, true); + } + + // proxy User-Agent string + $request->setHeader('user-agent', $_SERVER['HTTP_USER_AGENT']); + + self::$http_requests[$key] = $request; + } + + // cleanup + try { + $request->setBody(''); + $request->setUrl($url); + $request->setMethod($method); + } + catch (Exception $e) { + rcube::raise_error($e, true, true); + } + + return $request; + } }
View file
kolab-syncroton.dsc
Changed
@@ -2,7 +2,7 @@ Source: kolab-syncroton Binary: kolab-syncroton Architecture: all -Version: 2.2.1-0~kolab1 +Version: 2.2.2-0~kolab1 Maintainer: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Uploaders: Paul Klos <kolab@klos2day.nl> Homepage: http://www.kolab.org/ @@ -12,5 +12,5 @@ Package-List: kolab-syncroton deb utils extra Files: - 00000000000000000000000000000000 0 kolab-syncroton-2.2.1.tar.gz + 00000000000000000000000000000000 0 kolab-syncroton-2.2.2.tar.gz 00000000000000000000000000000000 0 debian.tar.gz
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.