3 commits - lib/ext lib/kolab_sync.php lib/plugins
Jeroen van Meeuwen
vanmeeuwen at kolabsys.com
Mon Aug 19 14:01:29 CEST 2013
lib/ext/Roundcube/README.md | 102 +++
lib/ext/Roundcube/html.php | 2
lib/ext/Roundcube/rcube.php | 14
lib/ext/Roundcube/rcube_base_replacer.php | 4
lib/ext/Roundcube/rcube_charset.php | 14
lib/ext/Roundcube/rcube_config.php | 73 ++
lib/ext/Roundcube/rcube_image.php | 91 ++-
lib/ext/Roundcube/rcube_imap.php | 42 -
lib/ext/Roundcube/rcube_imap_generic.php | 79 +--
lib/ext/Roundcube/rcube_ldap.php | 70 +-
lib/ext/Roundcube/rcube_message.php | 6
lib/ext/Roundcube/rcube_session.php | 12
lib/ext/Roundcube/rcube_shared.inc | 474 ------------------
lib/ext/Roundcube/rcube_spellchecker.php | 144 +++++
lib/ext/Roundcube/rcube_storage.php | 2
lib/ext/Roundcube/rcube_string_replacer.php | 4
lib/ext/Roundcube/rcube_utils.php | 15
lib/ext/Roundcube/rcube_washtml.php | 19
lib/kolab_sync.php | 2
lib/plugins/kolab_auth/kolab_auth.php | 22
lib/plugins/kolab_auth/kolab_auth_ldap.php | 18
lib/plugins/kolab_folders/kolab_folders.php | 1
lib/plugins/libkolab/SQL/postgres.initial.sql | 31 +
lib/plugins/libkolab/bin/Date_Recurrence_weekday.diff | 325 ------------
lib/plugins/libkolab/bin/Date_last_weekday.diff | 37 -
lib/plugins/libkolab/bin/get_horde_date.sh | 64 --
lib/plugins/libkolab/bin/modcache.sh | 4
lib/plugins/libkolab/config.inc.php.dist | 4
lib/plugins/libkolab/lib/kolab_format_contact.php | 2
lib/plugins/libkolab/lib/kolab_format_event.php | 40 -
lib/plugins/libkolab/lib/kolab_format_xcal.php | 43 +
lib/plugins/libkolab/lib/kolab_storage.php | 185 ++++++-
lib/plugins/libkolab/lib/kolab_storage_cache.php | 4
lib/plugins/libkolab/lib/kolab_storage_folder.php | 4
34 files changed, 810 insertions(+), 1143 deletions(-)
New commits:
commit 5877185dacd6738af323edb37585c8c5b784d870
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date: Mon Aug 19 14:01:15 2013 +0200
This branch now moves forward to 2.3
diff --git a/lib/kolab_sync.php b/lib/kolab_sync.php
index d27608a..544a388 100644
--- a/lib/kolab_sync.php
+++ b/lib/kolab_sync.php
@@ -43,7 +43,7 @@ class kolab_sync extends rcube
public $user;
const CHARSET = 'UTF-8';
- const VERSION = "2.2";
+ const VERSION = "2.3";
/**
commit 8c5809ea81503e1e988ecbf1ae0e579c30aec449
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date: Mon Aug 19 13:57:21 2013 +0200
Rebase lib/ext/Roundcube
diff --git a/lib/ext/Roundcube/README.md b/lib/ext/Roundcube/README.md
new file mode 100644
index 0000000..88f2d07
--- /dev/null
+++ b/lib/ext/Roundcube/README.md
@@ -0,0 +1,102 @@
+Roundcube Framework
+===================
+
+INTRODUCTION
+------------
+The Roundcube Framework is the basic library used for the Roundcube Webmail
+application. It is an extract of classes providing the core functionality for
+an email system. They can be used individually or as package for the following
+tasks:
+
+- IMAP mailbox access with optional caching
+- MIME message handling
+- Email message creation and sending through SMTP
+- General caching utilities using the local database
+- Database abstraction using PDO
+- VCard parsing and writing
+
+
+INSTALLATION
+------------
+Copy all files of this directory to your project or install it in the default
+include_path directory of your webserver. Some classes of the framework require
+one or multiple of the following [PEAR][pear] libraries:
+
+- Mail_Mime 1.8.1 or newer
+- Mail_mimeDecode 1.5.5 or newer
+- Net_SMTP (latest from https://github.com/pear/Net_SMTP/)
+- Net_IDNA2 0.1.1 or newer
+- Auth_SASL 1.0.6 or newer
+
+
+USAGE
+-----
+The Roundcube Framework provides a bootstrapping file which registers an
+autoloader and sets up the environment necessary for the Roundcube classes.
+In order to make use of the framework, simply include the bootstrap.php file
+from this directory in your application and start using the classes by simply
+instantiating them.
+
+If you wanna use more complex functionality like IMAP access with database
+caching or plugins, the rcube singleton helps you loading the necessary files:
+
+```php
+<?php
+
+define('RCUBE_CONFIG_DIR', '<path-to-config-directory>');
+define('RCUBE_PLUGINS_DIR', '<path-to-roundcube-plugins-directory');
+
+require_once '<path-to-roundcube-framework/bootstrap.php';
+
+$rcube = rcube::get_instance(rcube::INIT_WITH_DB | rcube::INIT_WITH_PLUGINS);
+$imap = $rcube->get_storage();
+
+// do cool stuff here...
+
+?>
+```
+
+LICENSE
+-------
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License (**with exceptions
+for plugins**) as published by the Free Software Foundation, either
+version 3 of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see [www.gnu.org/licenses/][gpl].
+
+This file forms part of the Roundcube Webmail Framework for which the
+following exception is added: Plugins which merely make function calls to the
+Roundcube Webmail Framework, and for that purpose include it by reference
+shall not be considered modifications of the software.
+
+If you wish to use this file in another project or create a modified
+version that will not be part of the Roundcube Webmail Framework, you
+may remove the exception above and use this source code under the
+original version of the license.
+
+For more details about licensing and the exceptions for skins and plugins
+see [roundcube.net/license][license]
+
+
+CONTACT
+-------
+For any bug reports or feature requests please refer to the tracking system
+at [trac.roundcube.net][tracreport] or subscribe to our mailing list.
+See [roundcube.net/support][support] for details.
+
+You're always welcome to send a message to the project admins:
+hello(at)roundcube(dot)net
+
+
+[pear]: http://pear.php.net
+[gpl]: http://www.gnu.org/licenses/
+[license]: http://roundcube.net/license
+[support]: http://roundcube.net/support
+[tracreport]: http://trac.roundcube.net/wiki/Howto_ReportIssues
\ No newline at end of file
diff --git a/lib/ext/Roundcube/html.php b/lib/ext/Roundcube/html.php
index 3e6e47a..a367112 100644
--- a/lib/ext/Roundcube/html.php
+++ b/lib/ext/Roundcube/html.php
@@ -358,7 +358,7 @@ class html_inputfield extends html
protected $tagname = 'input';
protected $type = 'text';
protected $allowed = array(
- 'type','name','value','size','tabindex','autocapitalize',
+ 'type','name','value','size','tabindex','autocapitalize','required',
'autocomplete','checked','onchange','onclick','disabled','readonly',
'spellcheck','results','maxlength','src','multiple','accept',
'placeholder','autofocus',
diff --git a/lib/ext/Roundcube/rcube.php b/lib/ext/Roundcube/rcube.php
index 6543a39..e0f889a 100644
--- a/lib/ext/Roundcube/rcube.php
+++ b/lib/ext/Roundcube/rcube.php
@@ -105,13 +105,14 @@ class rcube
* This implements the 'singleton' design pattern
*
* @param integer Options to initialize with this instance. See rcube::INIT_WITH_* constants
+ * @param string Environment name to run (e.g. live, dev, test)
*
* @return rcube The one and only instance
*/
- static function get_instance($mode = 0)
+ static function get_instance($mode = 0, $env = '')
{
if (!self::$instance) {
- self::$instance = new rcube();
+ self::$instance = new rcube($env);
self::$instance->init($mode);
}
@@ -122,10 +123,10 @@ class rcube
/**
* Private constructor
*/
- protected function __construct()
+ protected function __construct($env = '')
{
// load configuration
- $this->config = new rcube_config;
+ $this->config = new rcube_config($env);
$this->plugins = new rcube_dummy_plugin_api;
register_shutdown_function(array($this, 'shutdown'));
@@ -377,6 +378,7 @@ class rcube
'auth_pw' => $this->config->get("{$driver}_auth_pw"),
'debug' => (bool) $this->config->get("{$driver}_debug"),
'force_caps' => (bool) $this->config->get("{$driver}_force_caps"),
+ 'disabled_caps' => $this->config->get("{$driver}_disabled_caps"),
'timeout' => (int) $this->config->get("{$driver}_timeout"),
'skip_deleted' => (bool) $this->config->get('skip_deleted'),
'driver' => $driver,
@@ -496,11 +498,11 @@ class rcube
if ($tmp && ($dir = opendir($tmp))) {
while (($fname = readdir($dir)) !== false) {
- if ($fname{0} == '.') {
+ if ($fname[0] == '.') {
continue;
}
- if (filemtime($tmp.'/'.$fname) < $expire) {
+ if (@filemtime($tmp.'/'.$fname) < $expire) {
@unlink($tmp.'/'.$fname);
}
}
diff --git a/lib/ext/Roundcube/rcube_base_replacer.php b/lib/ext/Roundcube/rcube_base_replacer.php
index e41ccb1..a59bba9 100644
--- a/lib/ext/Roundcube/rcube_base_replacer.php
+++ b/lib/ext/Roundcube/rcube_base_replacer.php
@@ -44,8 +44,8 @@ class rcube_base_replacer
public function replace($body)
{
return preg_replace_callback(array(
- '/(src|background|href)=(["\']?)([^"\'\s]+)(\2|\s|>)/Ui',
- '/(url\s*\()(["\']?)([^"\'\)\s]+)(\2)\)/Ui',
+ '/(src|background|href)=(["\']?)([^"\'\s>]+)(\2|\s|>)/i',
+ '/(url\s*\()(["\']?)([^"\'\)\s]+)(\2)\)/i',
),
array($this, 'callback'), $body);
}
diff --git a/lib/ext/Roundcube/rcube_charset.php b/lib/ext/Roundcube/rcube_charset.php
index a7f26a3..19dbf6c 100644
--- a/lib/ext/Roundcube/rcube_charset.php
+++ b/lib/ext/Roundcube/rcube_charset.php
@@ -674,23 +674,27 @@ class rcube_charset
// Prioritize charsets according to current language (#1485669)
switch ($language) {
- case 'ja_JP': // for Japanese
+ case 'ja_JP':
$prio = array('ISO-2022-JP', 'JIS', 'UTF-8', 'EUC-JP', 'eucJP-win', 'SJIS', 'SJIS-win');
break;
- case 'zh_CN': // for Chinese (Simplified)
- case 'zh_TW': // for Chinese (Traditional)
+ case 'zh_CN':
+ case 'zh_TW':
$prio = array('UTF-8', 'BIG-5', 'GB2312', 'EUC-TW');
break;
- case 'ko_KR': // for Korean
+ case 'ko_KR':
$prio = array('UTF-8', 'EUC-KR', 'ISO-2022-KR');
break;
- case 'ru_RU': // for Russian
+ case 'ru_RU':
$prio = array('UTF-8', 'WINDOWS-1251', 'KOI8-R');
break;
+ case 'tr_TR':
+ $prio = array('UTF-8', 'ISO-8859-9', 'WINDOWS-1254');
+ break;
+
default:
$prio = array('UTF-8', 'SJIS', 'GB2312',
'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
diff --git a/lib/ext/Roundcube/rcube_config.php b/lib/ext/Roundcube/rcube_config.php
index 18055f7..3cf4da8 100644
--- a/lib/ext/Roundcube/rcube_config.php
+++ b/lib/ext/Roundcube/rcube_config.php
@@ -26,6 +26,8 @@ class rcube_config
{
const DEFAULT_SKIN = 'larry';
+ private $env = '';
+ private $basedir = 'config/';
private $prop = array();
private $errors = array();
private $userprefs = array();
@@ -50,9 +52,14 @@ class rcube_config
/**
* Object constructor
+ *
+ * @param string Environment suffix for config files to load
*/
- public function __construct()
+ public function __construct($env = '')
{
+ $this->env = $env;
+ $this->basedir = RCUBE_CONFIG_DIR;
+
$this->load();
// Defaults, that we do not require you to configure,
@@ -69,16 +76,26 @@ class rcube_config
*/
private function load()
{
- // load main config file
- if (!$this->load_from_file(RCUBE_CONFIG_DIR . 'main.inc.php'))
- $this->errors[] = 'main.inc.php was not found.';
+ // Load default settings
+ if (!$this->load_from_file('defaults.inc.php')) {
+ $this->errors[] = 'defaults.inc.php was not found.';
+ }
- // load database config
- if (!$this->load_from_file(RCUBE_CONFIG_DIR . 'db.inc.php'))
- $this->errors[] = 'db.inc.php was not found.';
+ // load main config file
+ if (!$this->load_from_file('config.inc.php')) {
+ // Old configuration files
+ if (!$this->load_from_file('main.inc.php') ||
+ !$this->load_from_file('db.inc.php')) {
+ $this->errors[] = 'config.inc.php was not found.';
+ }
+ else if (rand(1,100) == 10) { // log warning on every 100th request (average)
+ trigger_error("config.inc.php was not found. Please migrate your config by running bin/update.sh", E_USER_WARNING);
+ }
+ }
// load host-specific configuration
- $this->load_host_config();
+ if (!empty($_SERVER['HTTP_HOST']))
+ $this->load_host_config();
// set skin (with fallback to old 'skin_path' property)
if (empty($this->prop['skin'])) {
@@ -155,7 +172,7 @@ class rcube_config
}
if ($fname) {
- $this->load_from_file(RCUBE_CONFIG_DIR . $fname);
+ $this->load_from_file($fname);
}
}
@@ -164,18 +181,24 @@ class rcube_config
* Read configuration from a file
* and merge with the already stored config values
*
- * @param string $fpath Full path to the config file to be loaded
+ * @param string $file Name of the config file to be loaded
* @return booelan True on success, false on failure
*/
- public function load_from_file($fpath)
+ public function load_from_file($file)
{
- if (is_file($fpath) && is_readable($fpath)) {
+ $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($rcmail_config)) {
+ 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;
}
@@ -184,6 +207,30 @@ class rcube_config
return false;
}
+ /**
+ * Helper method to resolve the absolute path to the given config file.
+ * This also takes the 'env' property into account.
+ */
+ public function resolve_path($file, $use_env = true)
+ {
+ if (strpos($file, '/') === false) {
+ $file = rtrim($this->basedir, '/') . '/' . $file;
+
+ if (!realpath($file) === false) {
+ $file = realpath($file);
+ }
+ }
+
+ // 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;
+ }
+
+ return $file;
+ }
+
/**
* Getter for a specific config parameter
diff --git a/lib/ext/Roundcube/rcube_image.php b/lib/ext/Roundcube/rcube_image.php
index 09bb4e8..4e4caae 100644
--- a/lib/ext/Roundcube/rcube_image.php
+++ b/lib/ext/Roundcube/rcube_image.php
@@ -105,7 +105,6 @@ class rcube_image
if ($convert) {
$p['out'] = $filename;
$p['in'] = $this->image_file;
- $p['size'] = $size.'x'.$size;
$type = $props['type'];
if (!$type && ($data = $this->identify())) {
@@ -120,11 +119,37 @@ class rcube_image
$type = 'jpg';
}
- $p += array('type' => $type, 'types' => "bmp,eps,gif,jp2,jpg,png,svg,tif", 'quality' => 75);
- $p['-opts'] = array('-resize' => $p['size'].'>');
+ // If only one dimension is greater than the limit convert doesn't
+ // work as expected, we need to calculate new dimensions
+ $scale = $size / max($props['width'], $props['height']);
- if (in_array($type, explode(',', $p['types']))) { // Valid type?
- $result = rcube::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace RGB -quality {quality} {-opts} {intype}:{in} {type}:{out}', $p);
+ // if file is smaller than the limit, we do nothing
+ // but copy original file to destination file
+ if ($scale >= 1 && $p['intype'] == $type) {
+ $result = ($this->image_file == $filename || copy($this->image_file, $filename)) ? '' : false;
+ }
+ else {
+ if ($scale >= 1) {
+ $width = $props['width'];
+ $height = $props['height'];
+ }
+ else {
+ $width = intval($props['width'] * $scale);
+ $height = intval($props['height'] * $scale);
+ }
+
+ $valid_types = "bmp,eps,gif,jp2,jpg,png,svg,tif";
+
+ $p += array(
+ 'type' => $type,
+ 'quality' => 75,
+ 'size' => $width . 'x' . $height,
+ );
+
+ if (in_array($type, explode(',', $valid_types))) { // Valid type?
+ $result = rcube::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace sRGB -strip'
+ . ' -quality {quality} -resize {size} {intype}:{in} {type}:{out}', $p);
+ }
}
if ($result === '') {
@@ -161,34 +186,34 @@ class rcube_image
// Imagemagick resize is implemented in shrinking mode (see -resize argument above)
// we do the same here, if an image is smaller than specified size
// we do nothing but copy original file to destination file
- if ($scale > 1) {
- return $this->image_file == $filename || copy($this->image_file, $filename) ? $type : false;
- }
-
- $width = $props['width'] * $scale;
- $height = $props['height'] * $scale;
-
- $new_image = imagecreatetruecolor($width, $height);
-
- // Fix transparency of gif/png image
- if ($props['gd_type'] != IMAGETYPE_JPEG) {
- imagealphablending($new_image, false);
- imagesavealpha($new_image, true);
- $transparent = imagecolorallocatealpha($new_image, 255, 255, 255, 127);
- imagefilledrectangle($new_image, 0, 0, $width, $height, $transparent);
+ if ($scale >= 1) {
+ $result = $this->image_file == $filename || copy($this->image_file, $filename);
}
-
- imagecopyresampled($new_image, $image, 0, 0, 0, 0, $width, $height, $props['width'], $props['height']);
- $image = $new_image;
-
- if ($props['gd_type'] == IMAGETYPE_JPEG) {
- $result = imagejpeg($image, $filename, 75);
- }
- elseif($props['gd_type'] == IMAGETYPE_GIF) {
- $result = imagegif($image, $filename);
- }
- elseif($props['gd_type'] == IMAGETYPE_PNG) {
- $result = imagepng($image, $filename, 6, PNG_ALL_FILTERS);
+ else {
+ $width = intval($props['width'] * $scale);
+ $height = intval($props['height'] * $scale);
+ $new_image = imagecreatetruecolor($width, $height);
+
+ // Fix transparency of gif/png image
+ if ($props['gd_type'] != IMAGETYPE_JPEG) {
+ imagealphablending($new_image, false);
+ imagesavealpha($new_image, true);
+ $transparent = imagecolorallocatealpha($new_image, 255, 255, 255, 127);
+ imagefilledrectangle($new_image, 0, 0, $width, $height, $transparent);
+ }
+
+ imagecopyresampled($new_image, $image, 0, 0, 0, 0, $width, $height, $props['width'], $props['height']);
+ $image = $new_image;
+
+ if ($props['gd_type'] == IMAGETYPE_JPEG) {
+ $result = imagejpeg($image, $filename, 75);
+ }
+ elseif($props['gd_type'] == IMAGETYPE_GIF) {
+ $result = imagegif($image, $filename);
+ }
+ elseif($props['gd_type'] == IMAGETYPE_PNG) {
+ $result = imagepng($image, $filename, 6, PNG_ALL_FILTERS);
+ }
}
if ($result) {
@@ -230,7 +255,7 @@ class rcube_image
$p['out'] = $filename;
$p['type'] = self::$extensions[$type];
- $result = rcube::exec($convert . ' 2>&1 -colorspace RGB -quality 75 {in} {type}:{out}', $p);
+ $result = rcube::exec($convert . ' 2>&1 -colorspace sRGB -strip -quality 75 {in} {type}:{out}', $p);
if ($result === '') {
@chmod($filename, 0600);
diff --git a/lib/ext/Roundcube/rcube_imap.php b/lib/ext/Roundcube/rcube_imap.php
index 7ef8d62..c5346c8 100644
--- a/lib/ext/Roundcube/rcube_imap.php
+++ b/lib/ext/Roundcube/rcube_imap.php
@@ -2092,17 +2092,18 @@ class rcube_imap extends rcube_storage
/**
* Fetch message body of a specific message from the server
*
- * @param int $uid Message UID
- * @param string $part Part number
- * @param rcube_message_part $o_part Part object created by get_structure()
- * @param mixed $print True to print part, ressource to write part contents in
- * @param resource $fp File pointer to save the message part
- * @param boolean $skip_charset_conv Disables charset conversion
- * @param int $max_bytes Only read this number of bytes
+ * @param int Message UID
+ * @param string Part number
+ * @param rcube_message_part Part object created by get_structure()
+ * @param mixed True to print part, resource to write part contents in
+ * @param resource File pointer to save the message part
+ * @param boolean Disables charset conversion
+ * @param int Only read this number of bytes
+ * @param boolean Enables formatting of text/* parts bodies
*
* @return string Message/part body if not printed
*/
- public function get_message_part($uid, $part=1, $o_part=NULL, $print=NULL, $fp=NULL, $skip_charset_conv=false, $max_bytes=0)
+ public function get_message_part($uid, $part=1, $o_part=NULL, $print=NULL, $fp=NULL, $skip_charset_conv=false, $max_bytes=0, $formatted=true)
{
if (!$this->check_connection()) {
return null;
@@ -2121,8 +2122,9 @@ class rcube_imap extends rcube_storage
}
if ($o_part && $o_part->size) {
+ $formatted = $formatted && $o_part->ctype_primary == 'text';
$body = $this->conn->handlePartBody($this->folder, $uid, true,
- $part ? $part : 'TEXT', $o_part->encoding, $print, $fp, $o_part->ctype_primary == 'text', $max_bytes);
+ $part ? $part : 'TEXT', $o_part->encoding, $print, $fp, $formatted, $max_bytes);
}
if ($fp || $print) {
@@ -2667,7 +2669,6 @@ class rcube_imap extends rcube_storage
if ($list_extended) {
// unsubscribe non-existent folders, remove from the list
- // we can do this only when LIST response is available
if (is_array($a_folders) && $name == '*' && !empty($this->conn->data['LIST'])) {
foreach ($a_folders as $idx => $folder) {
if (($opts = $this->conn->data['LIST'][$folder])
@@ -2680,19 +2681,14 @@ class rcube_imap extends rcube_storage
}
}
else {
- // unsubscribe non-existent folders, remove them from the list,
- // we can do this only when LIST response is available
- if (is_array($a_folders) && $name == '*' && !empty($this->conn->data['LIST'])) {
- foreach ($a_folders as $idx => $folder) {
- if (!isset($this->conn->data['LIST'][$folder])
- || in_array('\\Noselect', $this->conn->data['LIST'][$folder])
- ) {
- // Some servers returns \Noselect for existing folders
- if (!$this->folder_exists($folder)) {
- $this->conn->unsubscribe($folder);
- unset($a_folders[$idx]);
- }
- }
+ // unsubscribe non-existent folders, remove them from the list
+ if (is_array($a_folders) && !empty($a_folders) && $name == '*') {
+ $existing = $this->list_folders($root, $name);
+ $nonexisting = array_diff($a_folders, $existing);
+ $a_folders = array_diff($a_folders, $nonexisting);
+
+ foreach ($nonexisting as $folder) {
+ $this->conn->unsubscribe($folder);
}
}
}
diff --git a/lib/ext/Roundcube/rcube_imap_generic.php b/lib/ext/Roundcube/rcube_imap_generic.php
index 1928c70..e119374 100644
--- a/lib/ext/Roundcube/rcube_imap_generic.php
+++ b/lib/ext/Roundcube/rcube_imap_generic.php
@@ -715,6 +715,10 @@ class rcube_imap_generic
$auth_method = 'CHECK';
}
+ if (!empty($this->prefs['disabled_caps'])) {
+ $this->prefs['disabled_caps'] = array_map('strtoupper', (array)$this->prefs['disabled_caps']);
+ }
+
$result = false;
// initialize connection
@@ -796,23 +800,21 @@ class rcube_imap_generic
// TLS connection
if ($this->prefs['ssl_mode'] == 'tls' && $this->getCapability('STARTTLS')) {
- if (version_compare(PHP_VERSION, '5.1.0', '>=')) {
- $res = $this->execute('STARTTLS');
-
- if ($res[0] != self::ERROR_OK) {
- $this->closeConnection();
- return false;
- }
+ $res = $this->execute('STARTTLS');
- if (!stream_socket_enable_crypto($this->fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
- $this->setError(self::ERROR_BAD, "Unable to negotiate TLS");
- $this->closeConnection();
- return false;
- }
+ if ($res[0] != self::ERROR_OK) {
+ $this->closeConnection();
+ return false;
+ }
- // Now we're secure, capabilities need to be reread
- $this->clearCapability();
+ if (!stream_socket_enable_crypto($this->fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
+ $this->setError(self::ERROR_BAD, "Unable to negotiate TLS");
+ $this->closeConnection();
+ return false;
}
+
+ // Now we're secure, capabilities need to be reread
+ $this->clearCapability();
}
// Send ID info
@@ -1329,9 +1331,8 @@ class rcube_imap_generic
$folders[$mailbox] = array();
}
- // store LSUB options only if not empty, this way
- // we can detect a situation when LIST doesn't return specified folder
- if (!empty($opts) || $cmd == 'LIST') {
+ // store folder options
+ if ($cmd == 'LIST') {
// Add to options array
if (empty($this->data['LIST'][$mailbox]))
$this->data['LIST'][$mailbox] = $opts;
@@ -2159,14 +2160,18 @@ class rcube_imap_generic
else if ($name == 'RFC822') {
$result[$id]->body = $value;
}
- else if ($name == 'BODY') {
- $body = $this->tokenizeResponse($line, 1);
- if ($value[0] == 'HEADER.FIELDS')
- $headers = $body;
- else if (!empty($value))
- $result[$id]->bodypart[$value[0]] = $body;
+ else if (stripos($name, 'BODY[') === 0) {
+ $name = str_replace(']', '', substr($name, 5));
+
+ if ($name == 'HEADER.FIELDS') {
+ // skip ']' after headers list
+ $this->tokenizeResponse($line, 1);
+ $headers = $this->tokenizeResponse($line, 1);
+ }
+ else if (strlen($name))
+ $result[$id]->bodypart[$name] = $value;
else
- $result[$id]->body = $body;
+ $result[$id]->body = $value;
}
}
@@ -2485,7 +2490,7 @@ class rcube_imap_generic
}
if ($binary) {
- // WARNING: Use $formatting argument with care, this may break binary data stream
+ // WARNING: Use $formatted argument with care, this may break binary data stream
$mode = -1;
}
@@ -2511,8 +2516,7 @@ class rcube_imap_generic
for ($i=0; $i<count($tokens); $i+=2) {
if (preg_match('/^(BODY|BINARY)/i', $tokens[$i])) {
- $i += 2; // skip BODY|BINARY and part number
- $result = $tokens[$i];
+ $result = $tokens[$i+1];
$found = true;
break;
}
@@ -2536,7 +2540,11 @@ class rcube_imap_generic
$prev = '';
$found = true;
- while ($bytes > 0) {
+ // empty body
+ if (!$bytes) {
+ $result = '';
+ }
+ else while ($bytes > 0) {
$line = $this->readLine(8192);
if ($line === NULL) {
@@ -2980,7 +2988,7 @@ class rcube_imap_generic
}
foreach ($entries as $name => $value) {
- $entries[$name] = $this->escape($name) . ' ' . $this->escape($value);
+ $entries[$name] = $this->escape($name) . ' ' . $this->escape($value, true);
}
$entries = implode(' ', $entries);
@@ -3477,25 +3485,24 @@ class rcube_imap_generic
// Parenthesized list
case '(':
- case '[':
$str = substr($str, 1);
$result[] = self::tokenizeResponse($str);
break;
case ')':
- case ']':
$str = substr($str, 1);
return $result;
break;
- // String atom, number, NIL, *, %
+ // String atom, number, astring, NIL, *, %
default:
// empty string
if ($str === '' || $str === null) {
break 2;
}
- // excluded chars: SP, CTL, ), [, ], DEL
- if (preg_match('/^([^\x00-\x20\x29\x5B\x5D\x7F]+)/', $str, $m)) {
+ // excluded chars: SP, CTL, ), DEL
+ // we do not exclude [ and ] (#1489223)
+ if (preg_match('/^([^\x00-\x20\x29\x7F]+)/', $str, $m)) {
$result[] = $m[1] == 'NIL' ? NULL : $m[1];
$str = substr($str, strlen($m[1]));
}
@@ -3686,6 +3693,10 @@ class rcube_imap_generic
$this->capability = explode(' ', strtoupper($str));
+ if (!empty($this->prefs['disabled_caps'])) {
+ $this->capability = array_diff($this->capability, $this->prefs['disabled_caps']);
+ }
+
if (!isset($this->prefs['literal+']) && in_array('LITERAL+', $this->capability)) {
$this->prefs['literal+'] = true;
}
diff --git a/lib/ext/Roundcube/rcube_ldap.php b/lib/ext/Roundcube/rcube_ldap.php
index 522c81d..cb7fa84 100644
--- a/lib/ext/Roundcube/rcube_ldap.php
+++ b/lib/ext/Roundcube/rcube_ldap.php
@@ -27,7 +27,7 @@
*/
class rcube_ldap extends rcube_addressbook
{
- /** public properties */
+ // public properties
public $primary_key = 'ID';
public $groups = false;
public $readonly = true;
@@ -35,7 +35,7 @@ class rcube_ldap extends rcube_addressbook
public $group_id = 0;
public $coltypes = array();
- /** private properties */
+ // private properties
protected $ldap;
protected $prop = array();
protected $fieldmap = array();
@@ -46,6 +46,21 @@ class rcube_ldap extends rcube_addressbook
protected $mail_domain = '';
protected $debug = false;
+ /**
+ * Group objectclass (lowercase) to member attribute mapping
+ *
+ * @var array
+ */
+ private static $group_types = array(
+ 'group' => 'member',
+ 'groupofnames' => 'member',
+ 'kolabgroupofnames' => 'member',
+ 'groupofuniquenames' => 'uniqueMember',
+ 'kolabgroupofuniquenames' => 'uniqueMember',
+ 'univentiongroup' => 'uniqueMember',
+ 'groupofurls' => null,
+ );
+
private $base_dn = '';
private $groups_base_dn = '';
private $group_url;
@@ -499,7 +514,8 @@ class rcube_ldap extends rcube_addressbook
$this->result = new rcube_result_set($entries['count'], ($this->list_page-1) * $this->page_size);
}
else {
- $prop = $this->group_id ? $this->group_data : $this->prop;
+ $prop = $this->group_id ? $this->group_data : $this->prop;
+ $base_dn = $this->group_id ? $this->group_base_dn : $this->base_dn;
// use global search filter
if (!empty($this->filter))
@@ -507,7 +523,7 @@ class rcube_ldap extends rcube_addressbook
// exec LDAP search if no result resource is stored
if ($this->ready && !$this->ldap_result)
- $this->ldap_result = $this->ldap->search($prop['base_dn'], $prop['filter'], $prop['scope'], $this->prop['attributes'], $prop);
+ $this->ldap_result = $this->ldap->search($base_dn, $prop['filter'], $prop['scope'], $this->prop['attributes'], $prop);
// count contacts for this user
$this->result = $this->count();
@@ -826,13 +842,13 @@ class rcube_ldap extends rcube_addressbook
}
// We have a connection but no result set, attempt to get one.
else if ($this->ready) {
- $prop = $this->group_id ? $this->group_data : $this->prop;
+ $prop = $this->group_id ? $this->group_data : $this->prop;
+ $base_dn = $this->group_id ? $this->group_base_dn : $this->base_dn;
if (!empty($this->filter)) { // Use global search filter
$prop['filter'] = $this->filter;
}
-
- $count = $this->ldap->search($prop['base_dn'], $prop['filter'], $prop['scope'], array('dn'), $prop, true);
+ $count = $this->ldap->search($base_dn, $prop['filter'], $prop['scope'], array('dn'), $prop, true);
}
return new rcube_result_set($count, ($this->list_page-1) * $this->page_size);
@@ -1191,8 +1207,7 @@ class rcube_ldap extends rcube_addressbook
// change the group membership of the contact
if ($this->groups) {
$group_ids = $this->get_record_groups($dn);
- foreach ($group_ids as $group_id => $group_prop)
- {
+ foreach (array_keys($group_ids) as $group_id) {
$this->remove_from_group($group_id, $dn);
$this->add_to_group($group_id, $newdn);
}
@@ -1257,7 +1272,7 @@ class rcube_ldap extends rcube_addressbook
if ($this->groups) {
$dn = self::dn_encode($dn);
$group_ids = $this->get_record_groups($dn);
- foreach ($group_ids as $group_id => $group_prop) {
+ foreach (array_keys($group_ids) as $group_id) {
$this->remove_from_group($group_id, $dn);
}
}
@@ -1445,11 +1460,11 @@ class rcube_ldap extends rcube_addressbook
{
// list of known attribute aliases
static $aliases = array(
- 'gn' => 'givenname',
+ 'gn' => 'givenname',
'rfc822mailbox' => 'email',
- 'userid' => 'uid',
- 'emailaddress' => 'email',
- 'pkcs9email' => 'email',
+ 'userid' => 'uid',
+ 'emailaddress' => 'email',
+ 'pkcs9email' => 'email',
);
list($name, $limit) = explode(':', $namev, 2);
@@ -1463,11 +1478,9 @@ class rcube_ldap extends rcube_addressbook
*/
private static function is_group_entry($entry)
{
- return array_intersect(
- array('group', 'groupofnames', 'kolabgroupofnames', 'groupofuniquenames',
- 'kolabgroupofuniquenames', 'groupofurls', 'univentiongroup'),
- array_map('strtolower', (array)$entry['objectclass'])
- );
+ $classes = array_map('strtolower', (array)$entry['objectclass']);
+
+ return count(array_intersect(array_keys(self::$group_types), $classes)) > 0;
}
/**
@@ -1573,6 +1586,7 @@ class rcube_ldap extends rcube_addressbook
$base_dn = $this->groups_base_dn;
$filter = $this->prop['groups']['filter'];
+ $scope = $this->prop['groups']['scope'];
$name_attr = $this->prop['groups']['name_attr'];
$email_attr = $this->prop['groups']['email_attr'] ? $this->prop['groups']['email_attr'] : 'mail';
$sort_attrs = $this->prop['groups']['sort'] ? (array)$this->prop['groups']['sort'] : array($name_attr);
@@ -1593,7 +1607,7 @@ class rcube_ldap extends rcube_addressbook
}
$attrs = array_unique(array('dn', 'objectClass', $name_attr, $email_attr, $sort_attr));
- $ldap_data = $ldap->search($base_dn, $filter, $this->prop['groups']['scope'], $attrs, $this->prop['groups']);
+ $ldap_data = $ldap->search($base_dn, $filter, $scope, $attrs, $this->prop['groups']);
if ($ldap_data === false) {
return array();
@@ -1864,6 +1878,7 @@ class rcube_ldap extends rcube_addressbook
$name_attr = $this->prop['groups']['name_attr'] ? $this->prop['groups']['name_attr'] : 'cn';
$member_attr = $this->get_group_member_attr();
$add_filter = '';
+
if ($member_attr != 'member' && $member_attr != 'uniqueMember')
$add_filter = "($member_attr=$contact_dn)";
$filter = strtr("(|(member=$contact_dn)(uniqueMember=$contact_dn)$add_filter)", array('\\' => '\\\\'));
@@ -1879,8 +1894,9 @@ class rcube_ldap extends rcube_addressbook
$entry['dn'] = $ldap_data->get_dn();
$group_name = $entry[$name_attr][0];
$group_id = self::dn_encode($entry['dn']);
- $groups[$group_id] = array('ID' => $group_id, 'name' => $group_name, 'dn' => $entry['dn']);
+ $groups[$group_id] = $group_name;
}
+
return $groups;
}
@@ -1895,16 +1911,8 @@ class rcube_ldap extends rcube_addressbook
if (!empty($object_classes)) {
foreach ((array)$object_classes as $oc) {
- switch (strtolower($oc)) {
- case 'group':
- case 'groupofnames':
- case 'kolabgroupofnames':
- return 'member';
-
- case 'groupofuniquenames':
- case 'kolabgroupofuniquenames':
- case 'univentiongroup':
- return 'uniqueMember';
+ if ($attr = self::$group_types[strtolower($oc)]) {
+ return $attr;
}
}
}
diff --git a/lib/ext/Roundcube/rcube_message.php b/lib/ext/Roundcube/rcube_message.php
index 797ca18..0d33ea4 100644
--- a/lib/ext/Roundcube/rcube_message.php
+++ b/lib/ext/Roundcube/rcube_message.php
@@ -168,10 +168,11 @@ class rcube_message
* @param resource $fp File pointer to save the message part
* @param boolean $skip_charset_conv Disables charset conversion
* @param int $max_bytes Only read this number of bytes
+ * @param boolean $formatted Enables formatting of text/* parts bodies
*
* @return string Part content
*/
- public function get_part_content($mime_id, $fp = null, $skip_charset_conv = false, $max_bytes = 0)
+ public function get_part_content($mime_id, $fp = null, $skip_charset_conv = false, $max_bytes = 0, $formatted = true)
{
if ($part = $this->mime_parts[$mime_id]) {
// stored in message structure (winmail/inline-uuencode)
@@ -185,7 +186,8 @@ class rcube_message
// get from IMAP
$this->storage->set_folder($this->folder);
- return $this->storage->get_message_part($this->uid, $mime_id, $part, NULL, $fp, $skip_charset_conv, $max_bytes);
+ return $this->storage->get_message_part($this->uid, $mime_id, $part,
+ NULL, $fp, $skip_charset_conv, $max_bytes, $formatted);
}
}
diff --git a/lib/ext/Roundcube/rcube_session.php b/lib/ext/Roundcube/rcube_session.php
index 615ec6f..67072df 100644
--- a/lib/ext/Roundcube/rcube_session.php
+++ b/lib/ext/Roundcube/rcube_session.php
@@ -54,7 +54,7 @@ class rcube_session
{
$this->db = $db;
$this->start = microtime(true);
- $this->ip = $_SERVER['REMOTE_ADDR'];
+ $this->ip = rcube_utils::remote_addr();
$this->logging = $config->get('log_session', false);
$lifetime = $config->get('session_lifetime', 1) * 60;
@@ -333,9 +333,9 @@ class rcube_session
$newvars = $oldvars !== null ? $this->_fixvars($vars, $oldvars) : $vars;
- if ($newvars !== $oldvars || $ts - $this->changed > $this->lifetime / 2) {
+ if ($newvars !== $oldvars || $ts - $this->changed > $this->lifetime / 3) {
return $this->memcache->set($key, serialize(array('changed' => time(), 'ip' => $this->ip, 'vars' => $newvars)),
- MEMCACHE_COMPRESSED, $this->lifetime);
+ MEMCACHE_COMPRESSED, $this->lifetime + 60);
}
return true;
@@ -480,7 +480,7 @@ class rcube_session
public function kill()
{
$this->vars = null;
- $this->ip = $_SERVER['REMOTE_ADDR']; // update IP (might have changed)
+ $this->ip = rcube_utils::remote_addr(); // update IP (might have changed)
$this->destroy(session_id());
rcube_utils::setcookie($this->cookiename, '-del-', time() - 60);
}
@@ -694,10 +694,10 @@ class rcube_session
function check_auth()
{
$this->cookie = $_COOKIE[$this->cookiename];
- $result = $this->ip_check ? $_SERVER['REMOTE_ADDR'] == $this->ip : true;
+ $result = $this->ip_check ? rcube_utils::remote_addr() == $this->ip : true;
if (!$result) {
- $this->log("IP check failed for " . $this->key . "; expected " . $this->ip . "; got " . $_SERVER['REMOTE_ADDR']);
+ $this->log("IP check failed for " . $this->key . "; expected " . $this->ip . "; got " . rcube_utils::remote_addr());
}
if ($result && $this->_mkcookie($this->now) != $this->cookie) {
diff --git a/lib/ext/Roundcube/rcube_shared.inc b/lib/ext/Roundcube/rcube_shared.inc
deleted file mode 100644
index 4577c6d..0000000
--- a/lib/ext/Roundcube/rcube_shared.inc
+++ /dev/null
@@ -1,474 +0,0 @@
-<?php
-
-/*
- +-----------------------------------------------------------------------+
- | program/include/rcube_shared.inc |
- | |
- | This file is part of the Roundcube PHP suite |
- | Copyright (C) 2005-2012, The Roundcube Dev Team |
- | |
- | Licensed under the GNU General Public License version 3 or |
- | any later version with exceptions for skins & plugins. |
- | See the README file for a full license statement. |
- | |
- | CONTENTS: |
- | Shared functions used by Roundcube Framework |
- | |
- +-----------------------------------------------------------------------+
- | Author: Thomas Bruederli <roundcube at gmail.com> |
- +-----------------------------------------------------------------------+
-*/
-
-
-/**
- * Roundcube shared functions
- *
- * @package Core
- */
-
-
-/**
- * Similar function as in_array() but case-insensitive
- *
- * @param string $needle Needle value
- * @param array $heystack Array to search in
- *
- * @return boolean True if found, False if not
- */
-function in_array_nocase($needle, $haystack)
-{
- $needle = mb_strtolower($needle);
- foreach ((array)$haystack as $value) {
- if ($needle === mb_strtolower($value)) {
- return true;
- }
- }
-
- return false;
-}
-
-
-/**
- * Find out if the string content means true or false
- *
- * @param string $str Input value
- *
- * @return boolean Boolean value
- */
-function get_boolean($str)
-{
- $str = strtolower($str);
-
- return !in_array($str, array('false', '0', 'no', 'off', 'nein', ''), true);
-}
-
-
-/**
- * Parse a human readable string for a number of bytes.
- *
- * @param string $str Input string
- *
- * @return float Number of bytes
- */
-function parse_bytes($str)
-{
- if (is_numeric($str)) {
- return floatval($str);
- }
-
- if (preg_match('/([0-9\.]+)\s*([a-z]*)/i', $str, $regs)) {
- $bytes = floatval($regs[1]);
- switch (strtolower($regs[2])) {
- case 'g':
- case 'gb':
- $bytes *= 1073741824;
- break;
- case 'm':
- case 'mb':
- $bytes *= 1048576;
- break;
- case 'k':
- case 'kb':
- $bytes *= 1024;
- break;
- }
- }
-
- return floatval($bytes);
-}
-
-
-/**
- * Make sure the string ends with a slash
- */
-function slashify($str)
-{
- return unslashify($str).'/';
-}
-
-
-/**
- * Remove slashes at the end of the string
- */
-function unslashify($str)
-{
- return preg_replace('/\/+$/', '', $str);
-}
-
-
-/**
- * Delete all files within a folder
- *
- * @param string Path to directory
- *
- * @return boolean True on success, False if directory was not found
- */
-function clear_directory($dir_path)
-{
- $dir = @opendir($dir_path);
- if (!$dir) {
- return false;
- }
-
- while ($file = readdir($dir)) {
- if (strlen($file) > 2) {
- unlink("$dir_path/$file");
- }
- }
-
- closedir($dir);
-
- return true;
-}
-
-
-/**
- * Returns number of seconds for a specified offset string.
- *
- * @param string $str String representation of the offset (e.g. 20min, 5h, 2days, 1week)
- *
- * @return int Number of seconds
- */
-function get_offset_sec($str)
-{
- if (preg_match('/^([0-9]+)\s*([smhdw])/i', $str, $regs)) {
- $amount = (int) $regs[1];
- $unit = strtolower($regs[2]);
- }
- else {
- $amount = (int) $str;
- $unit = 's';
- }
-
- switch ($unit) {
- case 'w':
- $amount *= 7;
- case 'd':
- $amount *= 24;
- case 'h':
- $amount *= 60;
- case 'm':
- $amount *= 60;
- }
-
- return $amount;
-}
-
-
-/**
- * Create a unix timestamp with a specified offset from now.
- *
- * @param string $offset_str String representation of the offset (e.g. 20min, 5h, 2days)
- * @param int $factor Factor to multiply with the offset
- *
- * @return int Unix timestamp
- */
-function get_offset_time($offset_str, $factor=1)
-{
- return time() + get_offset_sec($offset_str) * $factor;
-}
-
-
-/**
- * Truncate string if it is longer than the allowed length.
- * Replace the middle or the ending part of a string with a placeholder.
- *
- * @param string $str Input string
- * @param int $maxlength Max. length
- * @param string $placeholder Replace removed chars with this
- * @param bool $ending Set to True if string should be truncated from the end
- *
- * @return string Abbreviated string
- */
-function abbreviate_string($str, $maxlength, $placeholder='...', $ending=false)
-{
- $length = mb_strlen($str);
-
- if ($length > $maxlength) {
- if ($ending) {
- return mb_substr($str, 0, $maxlength) . $placeholder;
- }
-
- $placeholder_length = mb_strlen($placeholder);
- $first_part_length = floor(($maxlength - $placeholder_length)/2);
- $second_starting_location = $length - $maxlength + $first_part_length + $placeholder_length;
-
- $str = mb_substr($str, 0, $first_part_length) . $placeholder . mb_substr($str, $second_starting_location);
- }
-
- return $str;
-}
-
-
-/**
- * Get all keys from array (recursive).
- *
- * @param array $array Input array
- *
- * @return array List of array keys
- */
-function array_keys_recursive($array)
-{
- $keys = array();
-
- if (!empty($array) && is_array($array)) {
- foreach ($array as $key => $child) {
- $keys[] = $key;
- foreach (array_keys_recursive($child) as $val) {
- $keys[] = $val;
- }
- }
- }
-
- return $keys;
-}
-
-
-/**
- * Remove all non-ascii and non-word chars except ., -, _
- */
-function asciiwords($str, $css_id = false, $replace_with = '')
-{
- $allowed = 'a-z0-9\_\-' . (!$css_id ? '\.' : '');
- return preg_replace("/[^$allowed]/i", $replace_with, $str);
-}
-
-
-/**
- * Check if a string contains only ascii characters
- *
- * @param string $str String to check
- * @param bool $control_chars Includes control characters
- *
- * @return bool
- */
-function is_ascii($str, $control_chars = true)
-{
- $regexp = $control_chars ? '/[^\x00-\x7F]/' : '/[^\x20-\x7E]/';
- return preg_match($regexp, $str) ? false : true;
-}
-
-
-/**
- * Remove single and double quotes from a given string
- *
- * @param string Input value
- *
- * @return string Dequoted string
- */
-function strip_quotes($str)
-{
- return str_replace(array("'", '"'), '', $str);
-}
-
-
-/**
- * Remove new lines characters from given string
- *
- * @param string $str Input value
- *
- * @return string Stripped string
- */
-function strip_newlines($str)
-{
- return preg_replace('/[\r\n]/', '', $str);
-}
-
-
-/**
- * Compose a valid representation of name and e-mail address
- *
- * @param string $email E-mail address
- * @param string $name Person name
- *
- * @return string Formatted string
- */
-function format_email_recipient($email, $name = '')
-{
- $email = trim($email);
-
- if ($name && $name != $email) {
- // Special chars as defined by RFC 822 need to in quoted string (or escaped).
- if (preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $name)) {
- $name = '"'.addcslashes($name, '"').'"';
- }
-
- return "$name <$email>";
- }
-
- return $email;
-}
-
-
-/**
- * Format e-mail address
- *
- * @param string $email E-mail address
- *
- * @return string Formatted e-mail address
- */
-function format_email($email)
-{
- $email = trim($email);
- $parts = explode('@', $email);
- $count = count($parts);
-
- if ($count > 1) {
- $parts[$count-1] = mb_strtolower($parts[$count-1]);
-
- $email = implode('@', $parts);
- }
-
- return $email;
-}
-
-
-/**
- * mbstring replacement functions
- */
-if (!extension_loaded('mbstring'))
-{
- function mb_strlen($str)
- {
- return strlen($str);
- }
-
- function mb_strtolower($str)
- {
- return strtolower($str);
- }
-
- function mb_strtoupper($str)
- {
- return strtoupper($str);
- }
-
- function mb_substr($str, $start, $len=null)
- {
- return substr($str, $start, $len);
- }
-
- function mb_strpos($haystack, $needle, $offset=0)
- {
- return strpos($haystack, $needle, $offset);
- }
-
- function mb_strrpos($haystack, $needle, $offset=0)
- {
- return strrpos($haystack, $needle, $offset);
- }
-}
-
-/**
- * intl replacement functions
- */
-
-if (!function_exists('idn_to_utf8'))
-{
- function idn_to_utf8($domain, $flags=null)
- {
- static $idn, $loaded;
-
- if (!$loaded) {
- $idn = new Net_IDNA2();
- $loaded = true;
- }
-
- if ($idn && $domain && preg_match('/(^|\.)xn--/i', $domain)) {
- try {
- $domain = $idn->decode($domain);
- }
- catch (Exception $e) {
- }
- }
- return $domain;
- }
-}
-
-if (!function_exists('idn_to_ascii'))
-{
- function idn_to_ascii($domain, $flags=null)
- {
- static $idn, $loaded;
-
- if (!$loaded) {
- $idn = new Net_IDNA2();
- $loaded = true;
- }
-
- if ($idn && $domain && preg_match('/[^\x20-\x7E]/', $domain)) {
- try {
- $domain = $idn->encode($domain);
- }
- catch (Exception $e) {
- }
- }
- return $domain;
- }
-}
-
-/**
- * Use PHP5 autoload for dynamic class loading
- *
- * @todo Make Zend, PEAR etc play with this
- * @todo Make our classes conform to a more straight forward CS.
- */
-function rcube_autoload($classname)
-{
- $filename = preg_replace(
- array(
- '/Mail_(.+)/',
- '/Net_(.+)/',
- '/Auth_(.+)/',
- '/^html_.+/',
- '/^utf8$/',
- ),
- array(
- 'Mail/\\1',
- 'Net/\\1',
- 'Auth/\\1',
- 'html',
- 'utf8.class',
- ),
- $classname
- );
-
- if ($fp = @fopen("$filename.php", 'r', true)) {
- fclose($fp);
- include_once "$filename.php";
- return true;
- }
-
- return false;
-}
-
-/**
- * Local callback function for PEAR errors
- */
-function rcube_pear_error($err)
-{
- error_log(sprintf("%s (%s): %s",
- $err->getMessage(),
- $err->getCode(),
- $err->getUserinfo()), 0);
-}
diff --git a/lib/ext/Roundcube/rcube_spellchecker.php b/lib/ext/Roundcube/rcube_spellchecker.php
index 166de9b..df43652 100644
--- a/lib/ext/Roundcube/rcube_spellchecker.php
+++ b/lib/ext/Roundcube/rcube_spellchecker.php
@@ -84,6 +84,9 @@ class rcube_spellchecker
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);
}
@@ -115,6 +118,9 @@ class rcube_spellchecker
if ($this->engine == 'pspell') {
return $this->_pspell_suggestions($word);
}
+ else if ($this->engine == 'enchant') {
+ return $this->_enchant_suggestions($word);
+ }
return $this->_googie_suggestions($word);
}
@@ -133,6 +139,9 @@ class rcube_spellchecker
if ($this->engine == 'pspell') {
return $this->_pspell_words($text, $is_html);
}
+ else if ($this->engine == 'enchant') {
+ return $this->_enchant_words($text, $is_html);
+ }
return $this->_googie_words($text, $is_html);
}
@@ -326,6 +335,141 @@ class rcube_spellchecker
}
+ /**
+ * 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
diff --git a/lib/ext/Roundcube/rcube_storage.php b/lib/ext/Roundcube/rcube_storage.php
index 4b336f2..de83345 100644
--- a/lib/ext/Roundcube/rcube_storage.php
+++ b/lib/ext/Roundcube/rcube_storage.php
@@ -61,8 +61,6 @@ abstract class rcube_storage
'MAIL-FOLLOWUP-TO',
'MAIL-REPLY-TO',
'RETURN-PATH',
- 'DELIVERED-TO',
- 'ENVELOPE-TO',
);
const UNKNOWN = 0;
diff --git a/lib/ext/Roundcube/rcube_string_replacer.php b/lib/ext/Roundcube/rcube_string_replacer.php
index 0fc90a5..354b459 100644
--- a/lib/ext/Roundcube/rcube_string_replacer.php
+++ b/lib/ext/Roundcube/rcube_string_replacer.php
@@ -37,9 +37,9 @@ class rcube_string_replacer
// Support unicode/punycode in top-level domain part
$utf_domain = '[^?&@"\'\\/()<>\s\r\t\n]+\\.?([^\\x00-\\x2f\\x3b-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-zA-Z0-9]{2,})';
$url1 = '.:;,';
- $url2 = 'a-zA-Z0-9%=#$@+?!&\\/_~\\[\\]\\(\\){}\*-';
+ $url2 = 'a-zA-Z0-9%=#$@+?|!&\\/_~\\[\\]\\(\\){}\*-';
- $this->link_pattern = "/([\w]+:\/\/|\W[Ww][Ww][Ww]\.|^[Ww][Ww][Ww]\.)($utf_domain([$url1]?[$url2]+)*)/";
+ $this->link_pattern = "/([\w]+:\/\/|\W[Ww][Ww][Ww]\.|^[Ww][Ww][Ww]\.)($utf_domain([$url1]*[$url2]+)*)/";
$this->mailto_pattern = "/("
."[-\w!\#\$%&\'*+~\/^`|{}=]+(?:\.[-\w!\#\$%&\'*+~\/^`|{}=]+)*" // local-part
."@$utf_domain" // domain-part
diff --git a/lib/ext/Roundcube/rcube_utils.php b/lib/ext/Roundcube/rcube_utils.php
index 6c3bd21..cf87ded 100644
--- a/lib/ext/Roundcube/rcube_utils.php
+++ b/lib/ext/Roundcube/rcube_utils.php
@@ -666,6 +666,21 @@ class rcube_utils
/**
+ * Returns the real remote IP address
+ *
+ * @return string Remote IP address
+ */
+ public static function remote_addr()
+ {
+ foreach (array('HTTP_X_FORWARDED_FOR','HTTP_X_REAL_IP','REMOTE_ADDR') as $prop) {
+ if (!empty($_SERVER[$prop]))
+ return $_SERVER[$prop];
+ }
+
+ return '';
+ }
+
+ /**
* Read a specific HTTP request header.
*
* @param string $name Header name
diff --git a/lib/ext/Roundcube/rcube_washtml.php b/lib/ext/Roundcube/rcube_washtml.php
index 6b2efcc..8f7fe97 100644
--- a/lib/ext/Roundcube/rcube_washtml.php
+++ b/lib/ext/Roundcube/rcube_washtml.php
@@ -410,6 +410,25 @@ class rcube_washtml
);
$html = preg_replace($html_search, $html_replace, trim($html));
+ //-> Replace all of those weird MS Word quotes and other high characters
+ $badwordchars=array(
+ "\xe2\x80\x98", // left single quote
+ "\xe2\x80\x99", // right single quote
+ "\xe2\x80\x9c", // left double quote
+ "\xe2\x80\x9d", // right double quote
+ "\xe2\x80\x94", // em dash
+ "\xe2\x80\xa6" // elipses
+ );
+ $fixedwordchars=array(
+ "'",
+ "'",
+ '"',
+ '"',
+ '—',
+ '...'
+ );
+ $html = str_replace($badwordchars,$fixedwordchars, $html);
+
// PCRE errors handling (#1486856), should we use something like for every preg_* use?
if ($html === null && ($preg_error = preg_last_error()) != PREG_NO_ERROR) {
$errstr = "Could not clean up HTML message! PCRE Error: $preg_error.";
commit 38e9e41c862e6d4b0c185fb06ba13248c166e2c0
Author: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen at kolabsys.com>
Date: Mon Aug 19 13:55:41 2013 +0200
Rebase lib/plugins/*
diff --git a/lib/plugins/kolab_auth/kolab_auth.php b/lib/plugins/kolab_auth/kolab_auth.php
index 5579743..b139e32 100644
--- a/lib/plugins/kolab_auth/kolab_auth.php
+++ b/lib/plugins/kolab_auth/kolab_auth.php
@@ -87,6 +87,8 @@ class kolab_auth extends rcube_plugin
// Array(
// '<role_dn>' => Array('plugin1', 'plugin2'),
// );
+ //
+ // NOTE that <role_dn> may in fact be something like: 'cn=role,%dc'
$role_plugins = $rcmail->config->get('kolab_auth_role_plugins');
@@ -101,9 +103,29 @@ class kolab_auth extends rcube_plugin
// ),
// ),
// );
+ //
+ // NOTE that <role_dn> may in fact be something like: 'cn=role,%dc'
$role_settings = $rcmail->config->get('kolab_auth_role_settings');
+ $ldap = self::ldap();
+ if (!$ldap || !$ldap->ready) {
+ $args['abort'] = true;
+ return $args;
+ }
+
+ if (!empty($role_plugins)) {
+ foreach ($role_plugins as $role_dn => $plugins) {
+ $role_plugins[$ldap->parse_vars($role_dn)] = $plugins;
+ }
+ }
+
+ if (!empty($role_settings)) {
+ foreach ($role_settings as $role_dn => $settings) {
+ $role_settings[$ldap->parse_vars($role_dn)] = $settings;
+ }
+ }
+
foreach ($role_dns as $role_dn) {
if (isset($role_plugins[$role_dn]) && is_array($role_plugins[$role_dn])) {
foreach ($role_plugins[$role_dn] as $plugin) {
diff --git a/lib/plugins/kolab_auth/kolab_auth_ldap.php b/lib/plugins/kolab_auth/kolab_auth_ldap.php
index c8ebcd1..4584c60 100644
--- a/lib/plugins/kolab_auth/kolab_auth_ldap.php
+++ b/lib/plugins/kolab_auth/kolab_auth_ldap.php
@@ -391,17 +391,11 @@ class kolab_auth_ldap extends rcube_ldap_generic
list($usr, $dom) = explode('@', $user);
// unrealm domain, user login can contain a domain alias
- if ($dom != $domain && ($r_domain = $this->find_domain($dom))) {
- // $dom is a domain DN string?
- if (strpos($r_domain, '=')) {
- $dc = $r_domain;
- }
- else {
- $user = $usr . '@' . $r_domain;
- }
+ if ($dom != $domain && ($dc = $this->find_domain($dom))) {
+ // @FIXME: we should replace domain in $user, I suppose
}
}
- else if ($domain && !strpos($user, '@')) {
+ else if ($domain) {
$user .= '@' . $domain;
}
@@ -426,7 +420,7 @@ class kolab_auth_ldap extends rcube_ldap_generic
*
* @param string $domain Domain name
*
- * @return string Domain name or domain DN string
+ * @return string Domain DN string
*/
function find_domain($domain)
{
@@ -458,7 +452,9 @@ class kolab_auth_ldap extends rcube_ldap_generic
return $entry['inetdomainbasedn'];
}
- return is_array($entry[$name_attr]) ? $entry[$name_attr][0] : $entry[$name_attr];
+ $domain = is_array($entry[$name_attr]) ? $entry[$name_attr][0] : $entry[$name_attr];
+
+ return $domain ? 'dc=' . implode(',dc=', explode('.', $domain)) : null;
}
}
diff --git a/lib/plugins/kolab_folders/kolab_folders.php b/lib/plugins/kolab_folders/kolab_folders.php
index 5b1d1d3..cc43390 100644
--- a/lib/plugins/kolab_folders/kolab_folders.php
+++ b/lib/plugins/kolab_folders/kolab_folders.php
@@ -535,6 +535,7 @@ class kolab_folders extends rcube_plugin
// create folder
if (!$exists && !$storage->folder_exists($foldername)) {
$storage->create_folder($foldername, $type1 == 'mail');
+ $storage->subscribe($foldername);
}
// set type
diff --git a/lib/plugins/libkolab/SQL/postgres.initial.sql b/lib/plugins/libkolab/SQL/postgres.initial.sql
new file mode 100644
index 0000000..e06346c
--- /dev/null
+++ b/lib/plugins/libkolab/SQL/postgres.initial.sql
@@ -0,0 +1,31 @@
+/**
+ * libkolab database schema
+ *
+ * @version @package_version@
+ * @author Sidlyarenko Sergey
+ * @licence GNU AGPL
+ **/
+
+DROP TABLE IF EXISTS kolab_cache;
+
+CREATE TABLE kolab_cache (
+ resource character varying(255) NOT NULL,
+ type character varying(32) NOT NULL,
+ msguid NUMERIC(20) NOT NULL,
+ uid character varying(128) NOT NULL,
+ created timestamp without time zone DEFAULT NULL,
+ changed timestamp without time zone DEFAULT NULL,
+ data text NOT NULL,
+ xml text NOT NULL,
+ dtstart timestamp without time zone,
+ dtend timestamp without time zone,
+ tags character varying(255) NOT NULL,
+ words text NOT NULL,
+ filename character varying(255) DEFAULT NULL,
+ PRIMARY KEY(resource, type, msguid)
+);
+
+CREATE INDEX kolab_cache_resource_filename_idx ON kolab_cache (resource, filename);
+
+
+INSERT INTO system (name, value) VALUES ('libkolab-version', '2013041900');
diff --git a/lib/plugins/libkolab/bin/Date_Recurrence_weekday.diff b/lib/plugins/libkolab/bin/Date_Recurrence_weekday.diff
deleted file mode 100644
index e8b767d..0000000
--- a/lib/plugins/libkolab/bin/Date_Recurrence_weekday.diff
+++ /dev/null
@@ -1,325 +0,0 @@
---- Date/Recurrence.php.orig 2012-07-10 19:54:48.000000000 +0200
-+++ Date/Recurrence.php 2012-07-10 19:55:38.000000000 +0200
-@@ -95,6 +95,20 @@
- public $recurData = null;
-
- /**
-+ * BYDAY recurrence number
-+ *
-+ * @var integer
-+ */
-+ public $recurNthDay = null;
-+
-+ /**
-+ * BYMONTH recurrence data
-+ *
-+ * @var array
-+ */
-+ public $recurMonths = array();
-+
-+ /**
- * All the exceptions from recurrence for this event.
- *
- * @var array
-@@ -157,6 +171,44 @@
- }
-
- /**
-+ *
-+ * @param integer $nthDay The nth weekday of month to repeat events on
-+ */
-+ public function setRecurNthWeekday($nth)
-+ {
-+ $this->recurNthDay = (int)$nth;
-+ }
-+
-+ /**
-+ *
-+ * @return integer The nth weekday of month to repeat events.
-+ */
-+ public function getRecurNthWeekday()
-+ {
-+ return isset($this->recurNthDay) ? $this->recurNthDay : ceil($this->start->mday / 7);
-+ }
-+
-+ /**
-+ * Specifies the months for yearly (weekday) recurrence
-+ *
-+ * @param array $months List of months (integers) this event recurs on.
-+ */
-+ function setRecurByMonth($months)
-+ {
-+ $this->recurMonths = (array)$months;
-+ }
-+
-+ /**
-+ * Returns a list of months this yearly event recurs on
-+ *
-+ * @return array List of months (integers) this event recurs on.
-+ */
-+ function getRecurByMonth()
-+ {
-+ return $this->recurMonths;
-+ }
-+
-+ /**
- * Returns the days this event recurs on.
- *
- * @return integer A mask consisting of Horde_Date::MASK_* constants
-@@ -546,8 +598,13 @@
- $estart = clone $this->start;
-
- // What day of the week, and week of the month, do we recur on?
-- $nth = ceil($this->start->mday / 7);
-- $weekday = $estart->dayOfWeek();
-+ if (isset($this->recurNthDay)) {
-+ $nth = $this->recurNthDay;
-+ $weekday = log($this->recurData, 2);
-+ } else {
-+ $nth = ceil($this->start->mday / 7);
-+ $weekday = $estart->dayOfWeek();
-+ }
-
- // Adjust $estart to be the first candidate.
- $offset = ($after->month - $estart->month) + ($after->year - $estart->year) * 12;
-@@ -660,8 +717,13 @@
- $estart = clone $this->start;
-
- // What day of the week, and week of the month, do we recur on?
-- $nth = ceil($this->start->mday / 7);
-- $weekday = $estart->dayOfWeek();
-+ if (isset($this->recurNthDay)) {
-+ $nth = $this->recurNthDay;
-+ $weekday = log($this->recurData, 2);
-+ } else {
-+ $nth = ceil($this->start->mday / 7);
-+ $weekday = $estart->dayOfWeek();
-+ }
-
- // Adjust $estart to be the first candidate.
- $offset = floor(($after->year - $estart->year + $this->recurInterval - 1) / $this->recurInterval) * $this->recurInterval;
-@@ -894,15 +956,6 @@
- case 'W':
- $this->setRecurType(self::RECUR_WEEKLY);
- if (!empty($remainder)) {
-- $maskdays = array(
-- 'SU' => Horde_Date::MASK_SUNDAY,
-- 'MO' => Horde_Date::MASK_MONDAY,
-- 'TU' => Horde_Date::MASK_TUESDAY,
-- 'WE' => Horde_Date::MASK_WEDNESDAY,
-- 'TH' => Horde_Date::MASK_THURSDAY,
-- 'FR' => Horde_Date::MASK_FRIDAY,
-- 'SA' => Horde_Date::MASK_SATURDAY,
-- );
- $mask = 0;
- while (preg_match('/^ ?[A-Z]{2} ?/', $remainder, $matches)) {
- $day = trim($matches[0]);
-@@ -953,7 +1006,10 @@
- list($year, $month, $mday) = sscanf($remainder, '%04d%02d%02d');
- $this->setRecurEnd(new Horde_Date(array('year' => $year,
- 'month' => $month,
-- 'mday' => $mday)));
-+ 'mday' => $mday,
-+ 'hour' => 23,
-+ 'min' => 59,
-+ 'sec' => 59)));
- }
- }
- }
-@@ -1049,6 +1105,16 @@
- // Always default the recurInterval to 1.
- $this->setRecurInterval(isset($rdata['INTERVAL']) ? $rdata['INTERVAL'] : 1);
-
-+ $maskdays = array(
-+ 'SU' => Horde_Date::MASK_SUNDAY,
-+ 'MO' => Horde_Date::MASK_MONDAY,
-+ 'TU' => Horde_Date::MASK_TUESDAY,
-+ 'WE' => Horde_Date::MASK_WEDNESDAY,
-+ 'TH' => Horde_Date::MASK_THURSDAY,
-+ 'FR' => Horde_Date::MASK_FRIDAY,
-+ 'SA' => Horde_Date::MASK_SATURDAY,
-+ );
-+
- switch (Horde_String::upper($rdata['FREQ'])) {
- case 'DAILY':
- $this->setRecurType(self::RECUR_DAILY);
-@@ -1057,15 +1123,6 @@
- case 'WEEKLY':
- $this->setRecurType(self::RECUR_WEEKLY);
- if (isset($rdata['BYDAY'])) {
-- $maskdays = array(
-- 'SU' => Horde_Date::MASK_SUNDAY,
-- 'MO' => Horde_Date::MASK_MONDAY,
-- 'TU' => Horde_Date::MASK_TUESDAY,
-- 'WE' => Horde_Date::MASK_WEDNESDAY,
-- 'TH' => Horde_Date::MASK_THURSDAY,
-- 'FR' => Horde_Date::MASK_FRIDAY,
-- 'SA' => Horde_Date::MASK_SATURDAY,
-- );
- $days = explode(',', $rdata['BYDAY']);
- $mask = 0;
- foreach ($days as $day) {
-@@ -1090,6 +1147,10 @@
- case 'MONTHLY':
- if (isset($rdata['BYDAY'])) {
- $this->setRecurType(self::RECUR_MONTHLY_WEEKDAY);
-+ if (preg_match('/(-?[1-4])([A-Z]+)/', $rdata['BYDAY'], $m)) {
-+ $this->setRecurOnDay($maskdays[$m[2]]);
-+ $this->setRecurNthWeekday($m[1]);
-+ }
- } else {
- $this->setRecurType(self::RECUR_MONTHLY_DATE);
- }
-@@ -1100,6 +1161,14 @@
- $this->setRecurType(self::RECUR_YEARLY_DAY);
- } elseif (isset($rdata['BYDAY'])) {
- $this->setRecurType(self::RECUR_YEARLY_WEEKDAY);
-+ if (preg_match('/(-?[1-4])([A-Z]+)/', $rdata['BYDAY'], $m)) {
-+ $this->setRecurOnDay($maskdays[$m[2]]);
-+ $this->setRecurNthWeekday($m[1]);
-+ }
-+ if ($rdata['BYMONTH']) {
-+ $months = explode(',', $rdata['BYMONTH']);
-+ $this->setRecurByMonth($months);
-+ }
- } else {
- $this->setRecurType(self::RECUR_YEARLY_DATE);
- }
-@@ -1163,13 +1232,19 @@
- break;
-
- case self::RECUR_MONTHLY_WEEKDAY:
-- $nth_weekday = (int)($this->start->mday / 7);
-- if (($this->start->mday % 7) > 0) {
-- $nth_weekday++;
-+ if (isset($this->recurNthDay)) {
-+ $nth_weekday = $this->recurNthDay;
-+ $day_of_week = log($this->recurData, 2);
-+ } else {
-+ $day_of_week = $this->start->dayOfWeek();
-+ $nth_weekday = (int)($this->start->mday / 7);
-+ if (($this->start->mday % 7) > 0) {
-+ $nth_weekday++;
-+ }
- }
- $vcaldays = array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA');
- $rrule = 'FREQ=MONTHLY;INTERVAL=' . $this->recurInterval
-- . ';BYDAY=' . $nth_weekday . $vcaldays[$this->start->dayOfWeek()];
-+ . ';BYDAY=' . $nth_weekday . $vcaldays[$day_of_week];
- break;
-
- case self::RECUR_YEARLY_DATE:
-@@ -1182,15 +1257,22 @@
- break;
-
- case self::RECUR_YEARLY_WEEKDAY:
-- $nth_weekday = (int)($this->start->mday / 7);
-- if (($this->start->mday % 7) > 0) {
-- $nth_weekday++;
-- }
-+ if (isset($this->recurNthDay)) {
-+ $nth_weekday = $this->recurNthDay;
-+ $day_of_week = log($this->recurData, 2);
-+ } else {
-+ $day_of_week = $this->start->dayOfWeek();
-+ $nth_weekday = (int)($this->start->mday / 7);
-+ if (($this->start->mday % 7) > 0) {
-+ $nth_weekday++;
-+ }
-+ }
-+ $months = !empty($this->recurMonths) ? join(',', $this->recurMonths) : $this->start->month;
- $vcaldays = array('SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA');
- $rrule = 'FREQ=YEARLY;INTERVAL=' . $this->recurInterval
- . ';BYDAY='
- . $nth_weekday
-- . $vcaldays[$this->start->dayOfWeek()]
-+ . $vcaldays[$day_of_week]
- . ';BYMONTH=' . $this->start->month;
- break;
- }
-@@ -1223,6 +1305,21 @@
-
- $this->setRecurInterval((int)$hash['interval']);
-
-+ $month2number = array(
-+ 'january' => 1,
-+ 'february' => 2,
-+ 'march' => 3,
-+ 'april' => 4,
-+ 'may' => 5,
-+ 'june' => 6,
-+ 'july' => 7,
-+ 'august' => 8,
-+ 'september' => 9,
-+ 'october' => 10,
-+ 'november' => 11,
-+ 'december' => 12,
-+ );
-+
- $parse_day = false;
- $set_daymask = false;
- $update_month = false;
-@@ -1255,11 +1352,9 @@
-
- case 'weekday':
- $this->setRecurType(self::RECUR_MONTHLY_WEEKDAY);
-- $nth_weekday = (int)$hash['daynumber'];
-- $hash['daynumber'] = 1;
-+ $this->setRecurNthWeekday($hash['daynumber']);
- $parse_day = true;
-- $update_daynumber = true;
-- $update_weekday = true;
-+ $set_daymask = true;
- break;
- }
- break;
-@@ -1297,12 +1392,13 @@
- }
-
- $this->setRecurType(self::RECUR_YEARLY_WEEKDAY);
-- $nth_weekday = (int)$hash['daynumber'];
-- $hash['daynumber'] = 1;
-+ $this->setRecurNthWeekday($hash['daynumber']);
- $parse_day = true;
-- $update_month = true;
-- $update_daynumber = true;
-- $update_weekday = true;
-+ $set_daymask = true;
-+
-+ if ($hash['month'] && isset($month2number[$hash['month']])) {
-+ $this->setRecurByMonth($month2number[$hash['month']]);
-+ }
- break;
- }
- }
-@@ -1368,21 +1464,6 @@
-
- if ($update_month || $update_daynumber || $update_weekday) {
- if ($update_month) {
-- $month2number = array(
-- 'january' => 1,
-- 'february' => 2,
-- 'march' => 3,
-- 'april' => 4,
-- 'may' => 5,
-- 'june' => 6,
-- 'july' => 7,
-- 'august' => 8,
-- 'september' => 9,
-- 'october' => 10,
-- 'november' => 11,
-- 'december' => 12,
-- );
--
- if (isset($month2number[$hash['month']])) {
- $this->start->month = $month2number[$hash['month']];
- }
-@@ -1398,7 +1479,7 @@
- }
-
- if ($update_weekday) {
-- $this->start->setNthWeekday($last_found_day, $nth_weekday);
-+ $this->setNthWeekday($nth_weekday);
- }
- }
-
diff --git a/lib/plugins/libkolab/bin/Date_last_weekday.diff b/lib/plugins/libkolab/bin/Date_last_weekday.diff
deleted file mode 100644
index d260360..0000000
--- a/lib/plugins/libkolab/bin/Date_last_weekday.diff
+++ /dev/null
@@ -1,37 +0,0 @@
---- Date.php.orig 2012-07-10 19:14:26.000000000 +0200
-+++ Date.php 2012-07-10 19:16:22.000000000 +0200
-@@ -627,16 +627,25 @@
- return;
- }
-
-- $this->_mday = 1;
-- $first = $this->dayOfWeek();
-- if ($weekday < $first) {
-- $this->_mday = 8 + $weekday - $first;
-- } else {
-- $this->_mday = $weekday - $first + 1;
-+ if ($nth < 0) { // last $weekday of month
-+ $this->_mday = $lastday = Horde_Date_Utils::daysInMonth($this->_month, $this->_year);
-+ $last = $this->dayOfWeek();
-+ $this->_mday += ($weekday - $last);
-+ if ($this->_mday > $lastday)
-+ $this->_mday -= 7;
-+ }
-+ else {
-+ $this->_mday = 1;
-+ $first = $this->dayOfWeek();
-+ if ($weekday < $first) {
-+ $this->_mday = 8 + $weekday - $first;
-+ } else {
-+ $this->_mday = $weekday - $first + 1;
-+ }
-+ $diff = 7 * $nth - 7;
-+ $this->_mday += $diff;
-+ $this->_correct(self::MASK_DAY, $diff < 0);
- }
-- $diff = 7 * $nth - 7;
-- $this->_mday += $diff;
-- $this->_correct(self::MASK_DAY, $diff < 0);
- }
-
- /**
diff --git a/lib/plugins/libkolab/bin/get_horde_date.sh b/lib/plugins/libkolab/bin/get_horde_date.sh
deleted file mode 100755
index b8e663d..0000000
--- a/lib/plugins/libkolab/bin/get_horde_date.sh
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/bin/sh
-
-# Copy Horde_Date_Recurrence classes and dependencies to the given target directory.
-# This will create a standalone copy of the classes requried for date recurrence computation.
-
-SRCDIR=$1
-DESTDIR=$2
-BINDIR=`dirname $0`
-
-if [ ! -d "$SRCDIR" -o ! -d "$DESTDIR" ]; then
- echo "Usage: get_horde_date.sh SRCDIR DESTDIR"
- echo "Please enter valid source and destination directories for the Horde libs"
- exit 1
-fi
-
-
-# concat Date.php and Date/Utils.php
-HORDE_DATE="$DESTDIR/Horde_Date.php"
-
-echo "<?php
-
-/**
- * This is a concatenated copy of the following files:
- * Horde/Date.php, Horde/Date/Utils.php
- * Pull the latest version of these files from the PEAR channel of the Horde
- * project at http://pear.horde.org by installing the Horde_Date package.
- */
-" > $HORDE_DATE
-
-patch $SRCDIR/Date.php $BINDIR/Date_last_weekday.diff --output=$HORDE_DATE.patched
-sed 's/<?php//; s/?>//' $HORDE_DATE.patched >> $HORDE_DATE
-sed 's/<?php//; s/?>//' $SRCDIR/Date/Utils.php >> $HORDE_DATE
-
-# copy and patch Date/Recurrence.php
-HORDE_DATE_RECURRENCE="$DESTDIR/Horde_Date_Recurrence.php"
-
-echo "<?php
-
-/**
- * This is a modified copy of Horde/Date/Recurrence.php
- * Pull the latest version of this file from the PEAR channel of the Horde
- * project at http://pear.horde.org by installing the Horde_Date package.
- */
-
-if (!class_exists('Horde_Date'))
- require_once(dirname(__FILE__) . '/Horde_Date.php');
-
-// minimal required implementation of Horde_Date_Translation to avoid a huge dependency nightmare
-class Horde_Date_Translation
-{
- function t(\$arg) { return \$arg; }
- function ngettext(\$sing, \$plur, \$num) { return (\$num > 1 ? \$plur : \$sing); }
-}
-" > $HORDE_DATE_RECURRENCE
-
-patch $SRCDIR/Date/Recurrence.php $BINDIR/Date_Recurrence_weekday.diff --output=$HORDE_DATE_RECURRENCE.patched
-sed 's/<?php//; s/?>//' $HORDE_DATE_RECURRENCE.patched >> $HORDE_DATE_RECURRENCE
-
-# remove dependency to Horde_String
-sed -i '' "s/Horde_String::/strto/" $HORDE_DATE_RECURRENCE
-
-rm $DESTDIR/Horde_Date*.patched
-
-
diff --git a/lib/plugins/libkolab/bin/modcache.sh b/lib/plugins/libkolab/bin/modcache.sh
index 04d36a5..5ac9a21 100755
--- a/lib/plugins/libkolab/bin/modcache.sh
+++ b/lib/plugins/libkolab/bin/modcache.sh
@@ -81,7 +81,7 @@ case 'clear':
if (!$db->is_connected() || $db->is_error())
die("No DB connection\n");
- $folder_types = $opts['type'] ? explode(',', $opts['type']) : array('contact','distribution-list','event','task','configuration');
+ $folder_types = $opts['type'] ? explode(',', $opts['type']) : array('contact','distribution-list','event','task','configuration','file');
$folder_types_db = array_map(array($db, 'quote'), $folder_types);
if ($opts['all']) {
@@ -106,7 +106,7 @@ case 'prewarm':
$rcmail->plugins->load_plugin('libkolab');
if (authenticate($opts)) {
- $folder_types = $opts['type'] ? explode(',', $opts['type']) : array('contact','event','task','configuration');
+ $folder_types = $opts['type'] ? explode(',', $opts['type']) : array('contact','event','task','configuration','file');
foreach ($folder_types as $type) {
// sync every folder of the given type
foreach (kolab_storage::get_folders($type) as $folder) {
diff --git a/lib/plugins/libkolab/config.inc.php.dist b/lib/plugins/libkolab/config.inc.php.dist
index 01e1334..aa0c8d0 100644
--- a/lib/plugins/libkolab/config.inc.php.dist
+++ b/lib/plugins/libkolab/config.inc.php.dist
@@ -19,4 +19,8 @@ $rcmail_config['kolab_ssl_verify_peer'] = false;
// folders in calendar view or available addressbooks
$rcmail_config['kolab_use_subscriptions'] = false;
+// Enables the use of displayname folder annotations as introduced in KEP:?
+// for displaying resource folder names (experimental!)
+$rcmail_config['kolab_custom_display_names'] = false;
+
?>
diff --git a/lib/plugins/libkolab/lib/kolab_format_contact.php b/lib/plugins/libkolab/lib/kolab_format_contact.php
index 0c571f2..72867fc 100644
--- a/lib/plugins/libkolab/lib/kolab_format_contact.php
+++ b/lib/plugins/libkolab/lib/kolab_format_contact.php
@@ -386,7 +386,7 @@ class kolab_format_contact extends kolab_format
public function get_words()
{
$data = '';
- foreach (self::$fulltext_cols as $col) {
+ foreach (self::$fulltext_cols as $colname) {
list($col, $field) = explode(':', $colname);
if ($field) {
diff --git a/lib/plugins/libkolab/lib/kolab_format_event.php b/lib/plugins/libkolab/lib/kolab_format_event.php
index e050774..f3d0470 100644
--- a/lib/plugins/libkolab/lib/kolab_format_event.php
+++ b/lib/plugins/libkolab/lib/kolab_format_event.php
@@ -89,25 +89,6 @@ class kolab_format_event extends kolab_format_xcal
$status = kolabformat::StatusCancelled;
$this->obj->setStatus($status);
- // save attachments
- $vattach = new vectorattachment;
- foreach ((array)$object['_attachments'] as $cid => $attr) {
- if (empty($attr))
- continue;
- $attach = new Attachment;
- $attach->setLabel((string)$attr['name']);
- $attach->setUri('cid:' . $cid, $attr['mimetype']);
- $vattach->push($attach);
- }
-
- foreach ((array)$object['links'] as $link) {
- $attach = new Attachment;
- $attach->setUri($link, null);
- $vattach->push($attach);
- }
-
- $this->obj->setAttachments($vattach);
-
// save recurrence exceptions
if ($object['recurrence']['EXCEPTIONS']) {
$vexceptions = new vectorevent;
@@ -170,27 +151,6 @@ class kolab_format_event extends kolab_format_xcal
else if ($status == kolabformat::StatusCancelled)
$object['cancelled'] = true;
- // handle attachments
- $vattach = $this->obj->attachments();
- for ($i=0; $i < $vattach->size(); $i++) {
- $attach = $vattach->get($i);
-
- // skip cid: attachments which are mime message parts handled by kolab_storage_folder
- if (substr($attach->uri(), 0, 4) != 'cid:' && $attach->label()) {
- $name = $attach->label();
- $content = $attach->data();
- $object['_attachments'][$name] = array(
- 'name' => $name,
- 'mimetype' => $attach->mimetype(),
- 'size' => strlen($content),
- 'content' => $content,
- );
- }
- else if (substr($attach->uri(), 0, 4) == 'http') {
- $object['links'][] = $attach->uri();
- }
- }
-
// read exception event objects
if (($exceptions = $this->obj->exceptions()) && is_object($exceptions) && $exceptions->size()) {
for ($i=0; $i < $exceptions->size(); $i++) {
diff --git a/lib/plugins/libkolab/lib/kolab_format_xcal.php b/lib/plugins/libkolab/lib/kolab_format_xcal.php
index ff10a10..085e577 100644
--- a/lib/plugins/libkolab/lib/kolab_format_xcal.php
+++ b/lib/plugins/libkolab/lib/kolab_format_xcal.php
@@ -218,6 +218,27 @@ abstract class kolab_format_xcal extends kolab_format
}
}
+ // handle attachments
+ $vattach = $this->obj->attachments();
+ for ($i=0; $i < $vattach->size(); $i++) {
+ $attach = $vattach->get($i);
+
+ // skip cid: attachments which are mime message parts handled by kolab_storage_folder
+ if (substr($attach->uri(), 0, 4) != 'cid:' && $attach->label()) {
+ $name = $attach->label();
+ $content = $attach->data();
+ $object['_attachments'][$name] = array(
+ 'name' => $name,
+ 'mimetype' => $attach->mimetype(),
+ 'size' => strlen($content),
+ 'content' => $content,
+ );
+ }
+ else if (substr($attach->uri(), 0, 4) == 'http') {
+ $object['links'][] = $attach->uri();
+ }
+ }
+
return $object;
}
@@ -237,7 +258,8 @@ abstract class kolab_format_xcal extends kolab_format
parent::set($object);
// increment sequence on updates
- $object['sequence'] = !$is_new ? $this->obj->sequence()+1 : 0;
+ if (empty($object['sequence']))
+ $object['sequence'] = !$is_new ? $this->obj->sequence()+1 : 0;
$this->obj->setSequence($object['sequence']);
$this->obj->setSummary($object['title']);
@@ -378,6 +400,25 @@ abstract class kolab_format_xcal extends kolab_format
$valarms->push($alarm);
}
$this->obj->setAlarms($valarms);
+
+ // save attachments
+ $vattach = new vectorattachment;
+ foreach ((array)$object['_attachments'] as $cid => $attr) {
+ if (empty($attr))
+ continue;
+ $attach = new Attachment;
+ $attach->setLabel((string)$attr['name']);
+ $attach->setUri('cid:' . $cid, $attr['mimetype']);
+ $vattach->push($attach);
+ }
+
+ foreach ((array)$object['links'] as $link) {
+ $attach = new Attachment;
+ $attach->setUri($link, 'unknown');
+ $vattach->push($attach);
+ }
+
+ $this->obj->setAttachments($vattach);
}
/**
diff --git a/lib/plugins/libkolab/lib/kolab_storage.php b/lib/plugins/libkolab/lib/kolab_storage.php
index 2f3cdc5..ee6ede0 100644
--- a/lib/plugins/libkolab/lib/kolab_storage.php
+++ b/lib/plugins/libkolab/lib/kolab_storage.php
@@ -41,6 +41,23 @@ class kolab_storage
private static $config;
private static $imap;
+ // Default folder names
+ private static $default_folders = array(
+ 'event' => 'Calendar',
+ 'contact' => 'Contacts',
+ 'task' => 'Tasks',
+ 'note' => 'Notes',
+ 'file' => 'Files',
+ 'configuration' => 'Configuration',
+ 'journal' => 'Journal',
+ 'mail.inbox' => 'INBOX',
+ 'mail.drafts' => 'Drafts',
+ 'mail.sentitems' => 'Sent',
+ 'mail.wastebasket' => 'Trash',
+ 'mail.outbox' => 'Outbox',
+ 'mail.junkemail' => 'Junk',
+ );
+
/**
* Setup the environment needed by the libs
@@ -349,25 +366,8 @@ class kolab_storage
$result = self::folder_create($folder, $prop['type'], $prop['subscribed'], $prop['active']);
}
- // save displayname and color in METADATA
- // TODO: also save 'showalarams' and other properties here
if ($result) {
- $ns = null;
- foreach (array('color' => array(self::COLOR_KEY_SHARED,self::COLOR_KEY_PRIVATE),
- 'displayname' => array(self::NAME_KEY_SHARED,self::NAME_KEY_PRIVATE)) as $key => $metakeys) {
- if (!empty($prop[$key])) {
- if (!isset($ns))
- $ns = self::$imap->folder_namespace($folder);
-
- $meta_saved = false;
- if ($ns == 'personal') // save in shared namespace for personal folders
- $meta_saved = self::$imap->set_metadata($folder, array($metakeys[0] => $prop[$key]));
- if (!$meta_saved) // try in private namespace
- $meta_saved = self::$imap->set_metadata($folder, array($metakeys[1] => $prop[$key]));
- if ($meta_saved)
- unset($prop[$key]); // unsetting will prevent fallback to local user prefs
- }
- }
+ self::set_folder_props($folder, $prop);
}
return $result ? $folder : false;
@@ -388,9 +388,11 @@ class kolab_storage
self::setup();
// find custom display name in folder METADATA
- $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 (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;
+ }
}
$found = false;
@@ -645,13 +647,43 @@ class kolab_storage
/**
+ * Sort the given list of kolab folders by namespace/name
+ *
+ * @param array List of kolab_storage_folder objects
+ * @return array Sorted list of folders
+ */
+ public static function sort_folders($folders)
+ {
+ $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 »
+ }
+
+ $names = array();
+ foreach ($nsnames as $ns => $dummy) {
+ asort($nsnames[$ns], SORT_LOCALE_STRING);
+ $names += $nsnames[$ns];
+ }
+
+ $out = array();
+ foreach ($names as $utf7name => $name) {
+ $out[] = $folders[$utf7name];
+ }
+
+ return $out;
+ }
+
+
+ /**
* Returns folder types indexed by folder name
*
* @param string $prefix Folder prefix (Default '*' for all folders)
*
* @return array|bool List of folders, False on failure
*/
- static function folders_typedata($prefix = '*')
+ public static function folders_typedata($prefix = '*')
{
if (!self::setup()) {
return false;
@@ -670,7 +702,7 @@ class kolab_storage
/**
* Callback for array_map to select the correct annotation value
*/
- static function folder_select_metadata($types)
+ public static function folder_select_metadata($types)
{
if (!empty($types[self::CTYPE_KEY_PRIVATE])) {
return $types[self::CTYPE_KEY_PRIVATE];
@@ -690,7 +722,7 @@ class kolab_storage
*
* @return string Folder type
*/
- static function folder_type($folder)
+ public static function folder_type($folder)
{
self::setup();
@@ -716,7 +748,7 @@ class kolab_storage
*
* @return boolean True on success
*/
- static function set_folder_type($folder, $type='mail')
+ public static function set_folder_type($folder, $type='mail')
{
self::setup();
@@ -881,4 +913,107 @@ class kolab_storage
$rcube = rcube::get_instance();
return $rcube->user->save_prefs(array('kolab_active_folders' => $folders));
}
+
+ /**
+ * Creates default folder of specified type
+ * To be run when none of subscribed folders (of specified type) is found
+ *
+ * @param string $type Folder type
+ * @param string $props Folder properties (color, etc)
+ *
+ * @return string Folder name
+ */
+ public static function create_default_folder($type, $props = array())
+ {
+ if (!self::setup()) {
+ return;
+ }
+
+ $folders = self::$imap->get_metadata('*', array(kolab_storage::CTYPE_KEY_PRIVATE));
+
+ // from kolab_folders config
+ $folder_type = strpos($type, '.') ? str_replace('.', '_', $type) : $type . '_default';
+ $default_name = self::$config->get('kolab_folders_' . $folder_type);
+ $folder_type = str_replace('_', '.', $folder_type);
+
+ // check if we have any folder in personal namespace
+ // folder(s) may exist but not subscribed
+ foreach ($folders as $f => $data) {
+ if (strpos($data[self::CTYPE_KEY_PRIVATE], $type) === 0) {
+ $folder = $f;
+ break;
+ }
+ }
+
+ if (!$folder) {
+ if (!$default_name) {
+ $default_name = self::$default_folders[$type];
+ }
+
+ if (!$default_name) {
+ return;
+ }
+
+ $folder = rcube_charset::convert($default_name, RCUBE_CHARSET, 'UTF7-IMAP');
+ $prefix = self::$imap->get_namespace('prefix');
+
+ // add personal namespace prefix if needed
+ if ($prefix && strpos($folder, $prefix) !== 0 && $folder != 'INBOX') {
+ $folder = $prefix . $folder;
+ }
+
+ if (!self::$imap->folder_exists($folder)) {
+ if (!self::$imap->folder_create($folder)) {
+ return;
+ }
+ }
+
+ self::set_folder_type($folder, $folder_type);
+ }
+
+ self::folder_subscribe($folder);
+
+ if ($props['active']) {
+ self::set_state($folder, true);
+ }
+
+ if (!empty($props)) {
+ self::set_folder_props($folder, $props);
+ }
+
+ return $folder;
+ }
+
+ /**
+ * Sets folder metadata properties
+ *
+ * @param string $folder Folder name
+ * @param array $prop Folder properties
+ */
+ public static function set_folder_props($folder, &$prop)
+ {
+ if (!self::setup()) {
+ return;
+ }
+
+ // TODO: also save 'showalarams' and other properties here
+ $ns = self::$imap->folder_namespace($folder);
+ $supported = array(
+ 'color' => array(self::COLOR_KEY_SHARED, self::COLOR_KEY_PRIVATE),
+ 'displayname' => array(self::NAME_KEY_SHARED, self::NAME_KEY_PRIVATE),
+ );
+
+ foreach ($supported as $key => $metakeys) {
+ if (array_key_exists($key, $prop)) {
+ $meta_saved = false;
+ if ($ns == 'personal') // save in shared namespace for personal folders
+ $meta_saved = self::$imap->set_metadata($folder, array($metakeys[0] => $prop[$key]));
+ if (!$meta_saved) // try in private namespace
+ $meta_saved = self::$imap->set_metadata($folder, array($metakeys[1] => $prop[$key]));
+ if ($meta_saved)
+ unset($prop[$key]); // unsetting will prevent fallback to local user prefs
+ }
+ }
+ }
+
}
diff --git a/lib/plugins/libkolab/lib/kolab_storage_cache.php b/lib/plugins/libkolab/lib/kolab_storage_cache.php
index a3b6626..ba6c106 100644
--- a/lib/plugins/libkolab/lib/kolab_storage_cache.php
+++ b/lib/plugins/libkolab/lib/kolab_storage_cache.php
@@ -712,8 +712,8 @@ class kolab_storage_cache
// create lock record if not exists
if (!$sql_arr) {
$this->db->query(
- "INSERT INTO kolab_cache (resource, type, msguid, created, uid, data, xml)".
- " VALUES (?, ?, 1, " . $this->db->now() . ", '', '', '')",
+ "INSERT INTO kolab_cache (resource, type, msguid, created, uid, data, xml, tags, words)".
+ " VALUES (?, ?, 1, " . $this->db->now() . ", '', '', '', '', '')",
$this->resource_uri,
'lock'
);
diff --git a/lib/plugins/libkolab/lib/kolab_storage_folder.php b/lib/plugins/libkolab/lib/kolab_storage_folder.php
index f046bbf..303ed99 100644
--- a/lib/plugins/libkolab/lib/kolab_storage_folder.php
+++ b/lib/plugins/libkolab/lib/kolab_storage_folder.php
@@ -650,6 +650,10 @@ class kolab_storage_folder
if (is_array($object['_attachments'])) {
$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']))
+ $attachment['content'] = $attachment['data'];
+
// make sure size is set, so object saved in cache contains this info
if (!isset($attachment['size'])) {
if (!empty($attachment['content'])) {
More information about the commits
mailing list