Projects
Kolab:3.4:Updates
kolab-syncroton
Log In
Username
Password
We truncated the diff of some files because they were too big. If you want to see the full diff for every file,
click here
.
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 8
View file
kolab-syncroton.spec
Changed
@@ -1,12 +1,19 @@ +%{!?php_inidir: %global php_inidir %{_sysconfdir}/php.d} -%if 0%{?suse_version} -%global _ap_sysconfdir %{_sysconfdir}/apache2 +f 0%{?suse_version} +%global httpd_group www +%global httpd_name apache2 +%global httpd_user wwwrun %else -%global _ap_sysconfdir %{_sysconfdir}/httpd +%global httpd_group apache +%global httpd_name httpd +%global httpd_user apache %endif +%global _ap_sysconfdir %{_sysconfdir}/%{httpd_name} + Name: kolab-syncroton -Version: 2.1.0 +Version: 2.2.0 Release: 1%{?dist} Summary: ActiveSync for Kolab Groupware @@ -102,9 +109,9 @@ if [ -f "%{php_inidir}/apc.ini" ]; then if [ ! -z "`grep ^apc.enabled=1 %{php_inidir}/apc.ini`" ]; then %if 0%{?fedora} > 15 - /sbin/systemctl condrestart httpd.service + /sbin/systemctl condrestart %{httpd_name}.service %else - /sbin/service httpd condrestart + /sbin/service %{httpd_name} condrestart %endif fi fi @@ -121,9 +128,12 @@ %config(noreplace) %{_ap_sysconfdir}/conf.d/ %config(noreplace) %{_sysconfdir}/logrotate.d/%{name} %{_datadir}/%{name} -%attr(0770,apache,apache) %{_var}/log/%{name} +%attr(0770,%{httpd_user},%{httpd_group}) %{_var}/log/%{name} %changelog +* Sun Sep 8 2013 Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> - 2.2.0-1 +- Release version 2.2.0 + * Wed Sep 3 2013 Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> - 2.1.0-1 - Bug fixes for #1658, and attachment sending
View file
debian.changelog
Changed
@@ -1,3 +1,9 @@ +kolab-syncroton (2.2.0-1) unstable; urgency=low + + * Check in version 2.2.0 + + -- Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Sun, 8 Sep 2013 21:07:13 +0100 + kolab-syncroton (2.1.0-1) unstable; urgency=low * Imported Upstream version 2.1~rc2
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_shared.inc
Deleted
@@ -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@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
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/SQL/mysql.sql
Deleted
@@ -1,25 +0,0 @@ -/** - * libkolab database schema - * - * @version @package_version@ - * @author Thomas Bruederli - * @licence GNU AGPL - **/ - -DROP TABLE IF EXISTS `kolab_cache`; - -CREATE TABLE `kolab_cache` ( - `resource` VARCHAR(255) CHARACTER SET ascii NOT NULL, - `type` VARCHAR(32) CHARACTER SET ascii 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, - `dtstart` DATETIME, - `dtend` DATETIME, - `tags` VARCHAR(255) NOT NULL, - `words` TEXT NOT NULL, - PRIMARY KEY(`resource`,`type`,`msguid`) -) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */;
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/bin/Date_Recurrence_weekday.diff
Deleted
@@ -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();
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/bin/Date_last_weekday.diff
Deleted
@@ -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); - } - - /**
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/bin/get_horde_date.sh
Deleted
@@ -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 - -
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/lib/Horde_Date.php
Deleted
@@ -1,1304 +0,0 @@ -<?php - -/** - * This is a concatenated copy of the following files: - * Horde/Date/Utils.php, Horde/Date/Recurrence.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 wrapper/logic class, including some calculation - * functions. - * - * @category Horde - * @package Date - * - * @TODO in format(): - * http://php.net/intldateformatter - * - * @TODO on timezones: - * http://trac.agavi.org/ticket/1008 - * http://trac.agavi.org/changeset/3659 - * - * @TODO on switching to PHP::DateTime: - * The only thing ever stored in the database *IS* Unix timestamps. Doing - * anything other than that is unmanageable, yet some frameworks use 'server - * based' times in their systems, simply because they do not bother with - * daylight saving and only 'serve' one timezone! - * - * The second you have to manage 'real' time across timezones then daylight - * saving becomes essential, BUT only on the display side! Since the browser - * only provides a time offset, this is useless and to be honest should simply - * be ignored ( until it is upgraded to provide the correct information ;) - * ). So we need a 'display' function that takes a simple numeric epoch, and a - * separate timezone id into which the epoch is to be 'converted'. My W3C - * mapping works simply because ADOdb then converts that to it's own simple - * offset abbreviation - in my case GMT or BST. As long as DateTime passes the - * full 64 bit number the date range from 100AD is also preserved ( and - * further back if 2 digit years are disabled ). If I want to display the - * 'real' timezone with this 'time' then I just add it in place of ADOdb's - * 'timezone'. I am tempted to simply adjust the ADOdb class to take a - * timezone in place of the simple GMT switch it currently uses. - * - * The return path is just the reverse and simply needs to take the client - * display offset off prior to storage of the UTC epoch. SO we use - * DateTimeZone to get an offset value for the clients timezone and simply add - * or subtract this from a timezone agnostic display on the client end when - * entering new times. - * - * - * It's not really feasible to store dates in specific timezone, as most - * national/local timezones support DST - and that is a pain to support, as - * eg. sorting breaks when some timestamps get repeated. That's why it's - * usually better to store datetimes as either UTC datetime or plain unix - * timestamp. I usually go with the former - using database datetime type. - */ - -/** - * @category Horde - * @package Date - */ -class Horde_Date -{ - const DATE_SUNDAY = 0; - const DATE_MONDAY = 1; - const DATE_TUESDAY = 2; - const DATE_WEDNESDAY = 3; - const DATE_THURSDAY = 4; - const DATE_FRIDAY = 5; - const DATE_SATURDAY = 6; - - const MASK_SUNDAY = 1; - const MASK_MONDAY = 2; - const MASK_TUESDAY = 4; - const MASK_WEDNESDAY = 8; - const MASK_THURSDAY = 16; - const MASK_FRIDAY = 32; - const MASK_SATURDAY = 64; - const MASK_WEEKDAYS = 62; - const MASK_WEEKEND = 65; - const MASK_ALLDAYS = 127; - - const MASK_SECOND = 1; - const MASK_MINUTE = 2; - const MASK_HOUR = 4; - const MASK_DAY = 8; - const MASK_MONTH = 16; - const MASK_YEAR = 32; - const MASK_ALLPARTS = 63; - - const DATE_DEFAULT = 'Y-m-d H:i:s'; - const DATE_JSON = 'Y-m-d\TH:i:s'; - - /** - * Year - * - * @var integer - */ - protected $_year; - - /** - * Month - * - * @var integer - */ - protected $_month; - - /** - * Day - * - * @var integer - */ - protected $_mday; - - /** - * Hour - * - * @var integer - */ - protected $_hour = 0; - - /** - * Minute - * - * @var integer - */ - protected $_min = 0; - - /** - * Second - * - * @var integer - */ - protected $_sec = 0; - - /** - * String representation of the date's timezone. - * - * @var string - */ - protected $_timezone; - - /** - * Default format for __toString() - * - * @var string - */ - protected $_defaultFormat = self::DATE_DEFAULT; - - /** - * Default specs that are always supported. - * @var string - */ - protected static $_defaultSpecs = '%CdDeHImMnRStTyY'; - - /** - * Internally supported strftime() specifiers. - * @var string - */ - protected static $_supportedSpecs = ''; - - /** - * Map of required correction masks. - * - * @see __set() - * - * @var array - */ - protected static $_corrections = array( - 'year' => self::MASK_YEAR, - 'month' => self::MASK_MONTH, - 'mday' => self::MASK_DAY, - 'hour' => self::MASK_HOUR, - 'min' => self::MASK_MINUTE, - 'sec' => self::MASK_SECOND, - ); - - protected $_formatCache = array(); - - /** - * Builds a new date object. If $date contains date parts, use them to - * initialize the object. - * - * Recognized formats: - * - arrays with keys 'year', 'month', 'mday', 'day' - * 'hour', 'min', 'minute', 'sec' - * - objects with properties 'year', 'month', 'mday', 'hour', 'min', 'sec' - * - yyyy-mm-dd hh:mm:ss - * - yyyymmddhhmmss - * - yyyymmddThhmmssZ - * - yyyymmdd (might conflict with unix timestamps between 31 Oct 1966 and - * 03 Mar 1973) - * - unix timestamps - * - anything parsed by strtotime()/DateTime. - * - * @throws Horde_Date_Exception - */ - public function __construct($date = null, $timezone = null)
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/lib/Horde_Date_Recurrence.php
Deleted
@@ -1,1673 +0,0 @@ -<?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); } -} - - -/** - * This file contains the Horde_Date_Recurrence class and according constants. - * - * Copyright 2007-2012 Horde LLC (http://www.horde.org/) - * - * See the enclosed file COPYING for license information (LGPL). If you - * did not receive this file, see http://www.horde.org/licenses/lgpl21. - * - * @category Horde - * @package Date - */ - -/** - * The Horde_Date_Recurrence class implements algorithms for calculating - * recurrences of events, including several recurrence types, intervals, - * exceptions, and conversion from and to vCalendar and iCalendar recurrence - * rules. - * - * All methods expecting dates as parameters accept all values that the - * Horde_Date constructor accepts, i.e. a timestamp, another Horde_Date - * object, an ISO time string or a hash. - * - * @author Jan Schneider <jan@horde.org> - * @category Horde - * @package Date - */ -class Horde_Date_Recurrence -{ - /** No Recurrence **/ - const RECUR_NONE = 0; - - /** Recurs daily. */ - const RECUR_DAILY = 1; - - /** Recurs weekly. */ - const RECUR_WEEKLY = 2; - - /** Recurs monthly on the same date. */ - const RECUR_MONTHLY_DATE = 3; - - /** Recurs monthly on the same week day. */ - const RECUR_MONTHLY_WEEKDAY = 4; - - /** Recurs yearly on the same date. */ - const RECUR_YEARLY_DATE = 5; - - /** Recurs yearly on the same day of the year. */ - const RECUR_YEARLY_DAY = 6; - - /** Recurs yearly on the same week day. */ - const RECUR_YEARLY_WEEKDAY = 7; - - /** - * The start time of the event. - * - * @var Horde_Date - */ - public $start; - - /** - * The end date of the recurrence interval. - * - * @var Horde_Date - */ - public $recurEnd = null; - - /** - * The number of recurrences. - * - * @var integer - */ - public $recurCount = null; - - /** - * The type of recurrence this event follows. RECUR_* constant. - * - * @var integer - */ - public $recurType = self::RECUR_NONE; - - /** - * The length of time between recurrences. The time unit depends on the - * recurrence type. - * - * @var integer - */ - public $recurInterval = 1; - - /** - * Any additional recurrence data. - * - * @var integer - */ - 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 - */ - public $exceptions = array(); - - /** - * All the dates this recurrence has been marked as completed. - * - * @var array - */ - public $completions = array(); - - /** - * Constructor. - * - * @param Horde_Date $start Start of the recurring event. - */ - public function __construct($start) - { - $this->start = new Horde_Date($start); - } - - /** - * Resets the class properties. - */ - public function reset() - { - $this->recurEnd = null; - $this->recurCount = null; - $this->recurType = self::RECUR_NONE; - $this->recurInterval = 1; - $this->recurData = null; - $this->exceptions = array(); - $this->completions = array(); - } - - /** - * Checks if this event recurs on a given day of the week. - * - * @param integer $dayMask A mask consisting of Horde_Date::MASK_* - * constants specifying the day(s) to check. - * - * @return boolean True if this event recurs on the given day(s). - */ - public function recurOnDay($dayMask) - { - return ($this->recurData & $dayMask); - } - - /** - * Specifies the days this event recurs on. - * - * @param integer $dayMask A mask consisting of Horde_Date::MASK_* - * constants specifying the day(s) to recur on. - */ - public function setRecurOnDay($dayMask) - { - $this->recurData = $dayMask; - } - - /** - * - * @param integer $nthDay The nth weekday of month to repeat events on - */ - public function setRecurNthWeekday($nth) - { - $this->recurNthDay = (int)$nth; - } -
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/lib/Horde_Kolab_Format_XML_configuration.php
Deleted
@@ -1,76 +0,0 @@ -<?php - -/** - * Kolab XML handler for configuration (KEP:9). - * - * @author Aleksander Machniak <machniak@kolabsys.com> - * - * Copyright (C) 2011, 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 - * 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -class Horde_Kolab_Format_XML_configuration extends Horde_Kolab_Format_XML { - /** - * Specific data fields for the configuration object - * - * @var Kolab - */ - var $_fields_specific; - - var $_root_version = 2.1; - - /** - * Constructor - */ - function Horde_Kolab_Format_XML_configuration($params = array()) - { - $this->_root_name = 'configuration'; - - // Specific configuration fields, in kolab format specification order - $this->_fields_specific = array( - 'application' => array ( - 'type' => HORDE_KOLAB_XML_TYPE_STRING, - 'value' => HORDE_KOLAB_XML_VALUE_MAYBE_MISSING, - ), - 'type' => array( - 'type' => HORDE_KOLAB_XML_TYPE_STRING, - 'value' => HORDE_KOLAB_XML_VALUE_NOT_EMPTY, - ), - ); - - // Dictionary fields - if (!empty($params['subtype']) && preg_match('/^dictionary.*/', $params['subtype'])) { - $this->_fields_specific = array_merge($this->_fields_specific, array( - 'language' => array ( - 'type' => HORDE_KOLAB_XML_TYPE_STRING, - 'value' => HORDE_KOLAB_XML_VALUE_NOT_EMPTY, - ), - 'e' => array( - 'type' => HORDE_KOLAB_XML_TYPE_MULTIPLE, - 'value' => HORDE_KOLAB_XML_VALUE_NOT_EMPTY, - 'array' => array( - 'type' => HORDE_KOLAB_XML_TYPE_STRING, - 'value' => HORDE_KOLAB_XML_VALUE_NOT_EMPTY, - ), - ), - )); - } - - parent::Horde_Kolab_Format_XML($params); - - unset($this->_fields_basic['body']); - unset($this->_fields_basic['categories']); - unset($this->_fields_basic['sensitivity']); - } -}
View file
kolab-syncroton-2.1.0.tar.gz/config/main.inc.php.dist -> kolab-syncroton-2.2.0.tar.gz/config/main.inc.php.dist
Changed
@@ -14,12 +14,19 @@ // Type of ActiveSync cache. Supported values: 'db', 'apc' and 'memcache'. // Note: This is only for some additional data like timezones mapping. -// Main ActiveSync cache uses Roundcube SQL database -$rcmail_config['activesync_cache'] = null; +$rcmail_config['activesync_cache'] = 'db'; // lifetime of ActiveSync cache // possible units: s, m, h, d, w -$rcmail_config['activesync_cache_lifetime'] = '10d'; +$rcmail_config['activesync_cache_ttl'] = '1d'; + +// Type of ActiveSync Auth cache. Supported values: 'db', 'apc' and 'memcache'. +// Note: This is only for username canonification map. +$rcmail_config['activesync_auth_cache'] = 'db'; + +// lifetime of ActiveSync Auth cache +// possible units: s, m, h, d, w +$rcmail_config['activesync_auth_cache_ttl'] = '1d'; // List of global addressbooks (GAL) // Note: If empty 'autocomplete_addressbooks' setting will be used
View file
kolab-syncroton-2.1.0.tar.gz/docs/SQL/mysql.initial.sql -> kolab-syncroton-2.2.0.tar.gz/docs/SQL/mysql.initial.sql
Changed
@@ -3,50 +3,9 @@ `name` varchar(255) NOT NULL, `description` varchar(255) DEFAULT NULL, `policy_key` varchar(64) NOT NULL, - `allow_bluetooth` int(11) DEFAULT NULL, - `allow_browser` int(11) DEFAULT NULL, - `allow_camera` int(11) DEFAULT NULL, - `allow_consumer_email` int(11) DEFAULT NULL, - `allow_desktop_sync` int(11) DEFAULT NULL, - `allow_h_t_m_l_email` int(11) DEFAULT NULL, - `allow_internet_sharing` int(11) DEFAULT NULL, - `allow_ir_d_a` int(11) DEFAULT NULL, - `allow_p_o_p_i_m_a_p_email` int(11) DEFAULT NULL, - `allow_remote_desktop` int(11) DEFAULT NULL, - `allow_simple_device_password` int(11) DEFAULT NULL, - `allow_s_m_i_m_e_encryption_algorithm_negotiation` int(11) DEFAULT NULL, - `allow_s_m_i_m_e_soft_certs` int(11) DEFAULT NULL, - `allow_storage_card` int(11) DEFAULT NULL, - `allow_text_messaging` int(11) DEFAULT NULL, - `allow_unsigned_applications` int(11) DEFAULT NULL, - `allow_unsigned_installation_packages` int(11) DEFAULT NULL, - `allow_wifi` int(11) DEFAULT NULL, - `alphanumeric_device_password_required` int(11) DEFAULT NULL, - `approved_application_list` varchar(255) DEFAULT NULL, - `attachments_enabled` int(11) DEFAULT NULL, - `device_password_enabled` int(11) DEFAULT NULL, - `device_password_expiration` int(11) DEFAULT NULL, - `device_password_history` int(11) DEFAULT NULL, - `max_attachment_size` int(11) DEFAULT NULL, - `max_calendar_age_filter` int(11) DEFAULT NULL, - `max_device_password_failed_attempts` int(11) DEFAULT NULL, - `max_email_age_filter` int(11) DEFAULT NULL, - `max_email_body_truncation_size` int(11) DEFAULT NULL, - `max_email_h_t_m_l_body_truncation_size` int(11) DEFAULT NULL, - `max_inactivity_time_device_lock` int(11) DEFAULT NULL, - `min_device_password_complex_characters` int(11) DEFAULT NULL, - `min_device_password_length` int(11) DEFAULT NULL, - `password_recovery_enabled` int(11) DEFAULT NULL, - `require_device_encryption` int(11) DEFAULT NULL, - `require_encrypted_s_m_i_m_e_messages` int(11) DEFAULT NULL, - `require_encryption_s_m_i_m_e_algorithm` int(11) DEFAULT NULL, - `require_manual_sync_when_roaming` int(11) DEFAULT NULL, - `require_signed_s_m_i_m_e_algorithm` int(11) DEFAULT NULL, - `require_signed_s_m_i_m_e_messages` int(11) DEFAULT NULL, - `require_storage_card_encryption` int(11) DEFAULT NULL, - `unapproved_in_r_o_m_application_list` varchar(255) DEFAULT NULL, + `json_policy` blob NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB; +); CREATE TABLE IF NOT EXISTS `syncroton_device` ( `id` varchar(40) NOT NULL, @@ -106,9 +65,9 @@ CREATE TABLE IF NOT EXISTS `syncroton_content` ( `id` varchar(40) NOT NULL, - `device_id` varchar(40) DEFAULT NULL, - `folder_id` varchar(40) DEFAULT NULL, - `contentid` varchar(128) DEFAULT NULL, + `device_id` varchar(40) NOT NULL, + `folder_id` varchar(40) NOT NULL, + `contentid` varchar(128) NOT NULL, `creation_time` datetime DEFAULT NULL, `creation_synckey` int(11) NOT NULL, `is_deleted` tinyint(1) DEFAULT '0', @@ -153,4 +112,4 @@ PRIMARY KEY(`name`) ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; -INSERT INTO `system` (`name`, `value`) VALUES ('syncroton-version', '2013011600'); +INSERT INTO `system` (`name`, `value`) VALUES ('syncroton-version', '2013040900');
View file
kolab-syncroton-2.2.0.tar.gz/docs/SQL/mysql/2013040700.sql
Added
@@ -0,0 +1,9 @@ +DROP TABLE IF EXISTS `syncroton_policy`; +CREATE TABLE IF NOT EXISTS `syncroton_policy` ( + `id` varchar(40) NOT NULL, + `name` varchar(255) NOT NULL, + `description` varchar(255) DEFAULT NULL, + `policy_key` varchar(64) NOT NULL, + `json_policy` blob NOT NULL, + PRIMARY KEY (`id`) +);
View file
kolab-syncroton-2.2.0.tar.gz/docs/SQL/mysql/2013040900.sql
Added
@@ -0,0 +1,7 @@ +DELETE FROM `syncroton_content` WHERE `device_id` IS NULL; +DELETE FROM `syncroton_content` WHERE `folder_id` IS NULL; +DELETE FROM `syncroton_content` WHERE `contentid` IS NULL; +ALTER TABLE `syncroton_content` + MODIFY `device_id` varchar(40) NOT NULL, + MODIFY `folder_id` varchar(40) NOT NULL, + MODIFY `contentid` varchar(128) NOT NULL;
View file
kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/README.md
Added
@@ -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
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/bootstrap.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/bootstrap.php
Changed
@@ -31,21 +31,30 @@ // critical PHP settings here. Only these, which doesn't provide // an error/warning in the logs later. See (#1486307). 'mbstring.func_overload' => 0, - 'suhosin.session.encrypt' => 0, - 'session.auto_start' => 0, - 'file_uploads' => 1, 'magic_quotes_runtime' => 0, 'magic_quotes_sybase' => 0, // #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, + ); +} + foreach ($config as $optname => $optval) { - if ($optval != ini_get($optname) && @ini_set($optname, $optval) === false) { - die("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)."); + $ini_optval = filter_var(ini_get($optname), FILTER_VALIDATE_BOOLEAN); + 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)."; + if (defined('STDERR')) fwrite(STDERR, $error); else echo $error; + exit(1); } } // framework constants -define('RCUBE_VERSION', '0.9-git'); +define('RCUBE_VERSION', '1.0-git'); define('RCUBE_CHARSET', 'UTF-8'); if (!defined('RCUBE_LIB_DIR')) { @@ -284,32 +293,6 @@ /** - * 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
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/html.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/html.php
Changed
@@ -21,7 +21,7 @@ * Class for HTML code creation * * @package Framework - * @subpackage HTML + * @subpackage View */ class html { @@ -35,6 +35,7 @@ public static $common_attrib = array('id','class','style','title','align'); public static $containers = array('iframe','div','span','p','h1','h2','h3','form','textarea','table','thead','tbody','tr','th','td','style','script'); + /** * Constructor * @@ -217,7 +218,7 @@ $attr = array('src' => $attr); } return self::tag('iframe', $attr, $cont, array_merge(self::$common_attrib, - array('src','name','width','height','border','frameborder'))); + array('src','name','width','height','border','frameborder','onload'))); } /** @@ -287,7 +288,7 @@ } // attributes with no value - if (in_array($key, array('checked', 'multiple', 'disabled', 'selected'))) { + if (in_array($key, array('checked', 'multiple', 'disabled', 'selected', 'autofocus'))) { if ($value) { $attrib_arr[] = $key . '="' . $key . '"'; } @@ -332,7 +333,16 @@ */ public static function quote($str) { - return @htmlspecialchars($str, ENT_COMPAT, RCUBE_CHARSET); + static $flags; + + if (!$flags) { + $flags = ENT_COMPAT; + if (defined('ENT_SUBSTITUTE')) { + $flags |= ENT_SUBSTITUTE; + } + } + + return @htmlspecialchars($str, $flags, RCUBE_CHARSET); } } @@ -340,16 +350,18 @@ /** * Class to create an HTML input field * - * @package HTML + * @package Framework + * @subpackage View */ 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','placeholder', + 'spellcheck','results','maxlength','src','multiple','accept', + 'placeholder','autofocus', ); /** @@ -395,7 +407,8 @@ /** * Class to create an HTML password field * - * @package HTML + * @package Framework + * @subpackage View */ class html_passwordfield extends html_inputfield { @@ -405,9 +418,9 @@ /** * Class to create an hidden HTML input field * - * @package HTML + * @package Framework + * @subpackage View */ - class html_hiddenfield extends html { protected $tagname = 'input'; @@ -455,7 +468,8 @@ /** * Class to create HTML radio buttons * - * @package HTML + * @package Framework + * @subpackage View */ class html_radiobutton extends html_inputfield { @@ -485,7 +499,8 @@ /** * Class to create HTML checkboxes * - * @package HTML + * @package Framework + * @subpackage View */ class html_checkbox extends html_inputfield { @@ -515,7 +530,8 @@ /** * Class to create an HTML textarea * - * @package HTML + * @package Framework + * @subpackage View */ class html_textarea extends html { @@ -573,7 +589,8 @@ * print $select->show('CH'); * </pre> * - * @package HTML + * @package Framework + * @subpackage View */ class html_select extends html { @@ -638,7 +655,8 @@ /** * Class to build an HTML table * - * @package HTML + * @package Framework + * @subpackage View */ class html_table extends html { @@ -660,6 +678,11 @@ { $default_attrib = self::$doctype == 'xhtml' ? array('summary' => '', 'border' => 0) : array(); $this->attrib = array_merge($attrib, $default_attrib); + + if (!empty($attrib['tagname']) && $attrib['tagname'] != 'table') { + $this->tagname = $attrib['tagname']; + $this->allowed = self::$common_attrib; + } } /** @@ -761,6 +784,11 @@ $index = $this->rowindex; } + // make sure row object exists (#1489094) + if (!$this->rows[$index]) { + $this->rows[$index] = new stdClass; + } + $this->rows[$index]->attrib = $attr; } @@ -798,19 +826,20 @@ if (!empty($this->header)) { $rowcontent = ''; foreach ($this->header as $c => $col) { - $rowcontent .= self::tag('td', $col->attrib, $col->content); + $rowcontent .= self::tag($this->_col_tagname(), $col->attrib, $col->content); } - $thead = self::tag('thead', null, self::tag('tr', null, $rowcontent, parent::$common_attrib)); + $thead = $this->tagname == 'table' ? self::tag('thead', null, self::tag('tr', null, $rowcontent, parent::$common_attrib)) : + self::tag($this->_row_tagname(), array('class' => 'thead'), $rowcontent, parent::$common_attrib); } foreach ($this->rows as $r => $row) { $rowcontent = ''; foreach ($row->cells as $c => $col) { - $rowcontent .= self::tag('td', $col->attrib, $col->content); + $rowcontent .= self::tag($this->_col_tagname(), $col->attrib, $col->content); } if ($r < $this->rowindex || count($row->cells)) { - $tbody .= self::tag('tr', $row->attrib, $rowcontent, parent::$common_attrib); + $tbody .= self::tag($this->_row_tagname(), $row->attrib, $rowcontent, parent::$common_attrib); } } @@ -819,7 +848,7 @@ } // add <tbody>
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube.php
Changed
@@ -99,20 +99,20 @@ protected $texts; protected $caches = array(); protected $shutdown_functions = array(); - protected $expunge_cache = false; /** * This implements the 'singleton' design pattern * * @param integer Options to initialize with this instance. See rcube::INIT_WITH_* constants + * @param string Environment name to run (e.g. live, dev, test) * * @return rcube The one and only instance */ - static function get_instance($mode = 0) + static function get_instance($mode = 0, $env = '') { if (!self::$instance) { - self::$instance = new rcube(); + self::$instance = new rcube($env); self::$instance->init($mode); } @@ -123,10 +123,10 @@ /** * 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')); @@ -258,6 +258,39 @@ /** + * Initialize and get shared cache object + * + * @param string $name Cache identifier + * @param bool $packed Enables/disables data serialization + * + * @return rcube_cache_shared Cache object + */ + public function get_cache_shared($name, $packed=true) + { + $shared_name = "shared_$name"; + + if (!array_key_exists($shared_name, $this->caches)) { + $opt = strtolower($name) . '_cache'; + $type = $this->config->get($opt); + $ttl = $this->config->get($opt . '_ttl'); + + if (!$type) { + // cache is disabled + return $this->caches[$shared_name] = null; + } + + if ($ttl === null) { + $ttl = $this->config->get('shared_cache_ttl', '10d'); + } + + $this->caches[$shared_name] = new rcube_cache_shared($type, $name, $ttl, $packed); + } + + return $this->caches[$shared_name]; + } + + + /** * Create SMTP object and connect to server * * @param boolean True if connection should be established @@ -345,6 +378,7 @@ '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, @@ -405,6 +439,7 @@ $sess_domain = $this->config->get('session_domain'); $sess_path = $this->config->get('session_path'); $lifetime = $this->config->get('session_lifetime', 0) * 60; + $is_secure = $this->config->get('use_https') || rcube_utils::https_check(); // set session domain if ($sess_domain) { @@ -419,45 +454,55 @@ ini_set('session.gc_maxlifetime', $lifetime * 2); } - ini_set('session.cookie_secure', rcube_utils::https_check()); + ini_set('session.cookie_secure', $is_secure); ini_set('session.name', $sess_name ? $sess_name : 'roundcube_sessid'); ini_set('session.use_cookies', 1); ini_set('session.use_only_cookies', 1); - ini_set('session.serialize_handler', 'php'); ini_set('session.cookie_httponly', 1); // use database for storing session data $this->session = new rcube_session($this->get_dbh(), $this->config); - $this->session->register_gc_handler(array($this, 'temp_gc')); - $this->session->register_gc_handler(array($this, 'cache_gc')); - + $this->session->register_gc_handler(array($this, 'gc')); $this->session->set_secret($this->config->get('des_key') . dirname($_SERVER['SCRIPT_NAME'])); $this->session->set_ip_check($this->config->get('ip_check')); // start PHP session (if not in CLI mode) if ($_SERVER['REMOTE_ADDR']) { - session_start(); + $this->session->start(); } } /** + * Garbage collector - cache/temp cleaner + */ + public function gc() + { + rcube_cache::gc(); + rcube_cache_shared::gc(); + $this->get_storage()->cache_gc(); + + $this->gc_temp(); + } + + + /** * Garbage collector function for temp files. * Remove temp files older than two days */ - public function temp_gc() + public function gc_temp() { $tmp = unslashify($this->config->get('temp_dir')); $expire = time() - 172800; // expire in 48 hours 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); } } @@ -468,14 +513,21 @@ /** - * Garbage collector for cache entries. - * Set flag to expunge caches on shutdown + * Runs garbage collector with probability based on + * session settings. This is intended for environments + * without a session. */ - public function cache_gc() + public function gc_run() { - // because this gc function is called before storage is initialized, - // we just set a flag to expunge storage cache on shutdown. - $this->expunge_cache = true; + $probability = (int) ini_get('session.gc_probability'); + $divisor = (int) ini_get('session.gc_divisor'); + + if ($divisor > 0 && $probability > 0) { + $random = mt_rand(1, $divisor); + if ($random <= $probability) { + $this->gc(); + } + } } @@ -859,6 +911,14 @@ call_user_func($function); } + // write session data as soon as possible and before + // closing database connection, don't do this before + // registered shutdown functions, they may need the session + // Note: this will run registered gc handlers (ie. cache gc) + if ($_SERVER['REMOTE_ADDR'] && is_object($this->session)) { + $this->session->write_close(); + } + if (is_object($this->smtp)) { $this->smtp->disconnect();
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_addressbook.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_addressbook.php
Changed
@@ -309,9 +309,14 @@ * List all active contact groups of this source * * @param string Optional search string to match group name + * @param int Matching mode: + * 0 - partial (*abc*), + * 1 - strict (=), + * 2 - prefix (abc*) + * * @return array Indexed list of contact groups, each a hash array */ - function list_groups($search = null) + function list_groups($search = null, $mode = 0) { /* empty for address books don't supporting groups */ return array(); @@ -370,9 +375,10 @@ /** * Add the given contact records the a certain group * - * @param string Group identifier - * @param array List of contact identifiers to be added - * @return int Number of contacts added + * @param string Group identifier + * @param array|string List of contact identifiers to be added + * + * @return int Number of contacts added */ function add_to_group($group_id, $ids) { @@ -383,9 +389,10 @@ /** * Remove the given contact records from a certain group * - * @param string Group identifier - * @param array List of contact identifiers to be removed - * @return int Number of deleted group members + * @param string Group identifier + * @param array|string List of contact identifiers to be removed + * + * @return int Number of deleted group members */ function remove_from_group($group_id, $ids) { @@ -425,7 +432,7 @@ $out = array_merge($out, (array)$values); } else { - list($f, $type) = explode(':', $c); + list(, $type) = explode(':', $c); $out[$type] = array_merge((array)$out[$type], (array)$values); } } @@ -524,6 +531,21 @@ } /** + * Create a unique key for sorting contacts + */ + public static function compose_contact_key($contact, $sort_col) + { + $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']); + } + + return $key; + } + + /** * Compare search value with contact data * * @param string $colname Data name
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_base_replacer.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_base_replacer.php
Changed
@@ -21,7 +21,7 @@ * using a predefined base * * @package Framework - * @subpackage Core + * @subpackage Utils * @author Thomas Bruederli <roundcube@gmail.com> */ class rcube_base_replacer @@ -44,8 +44,8 @@ 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); }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_browser.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_browser.php
Changed
@@ -20,7 +20,7 @@ * Provide details about the client's browser based on the User-Agent header * * @package Framework - * @subpackage Core + * @subpackage Utils */ class rcube_browser {
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_cache.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_cache.php
Changed
@@ -38,6 +38,7 @@ private $type; private $userid; private $prefix; + private $table; private $ttl; private $packed; private $index; @@ -71,8 +72,9 @@ $this->db = function_exists('apc_exists'); // APC 3.1.4 required } else { - $this->type = 'db'; - $this->db = $rcube->get_dbh(); + $this->type = 'db'; + $this->db = $rcube->get_dbh(); + $this->table = $this->db->table_name('cache'); } // convert ttl string to seconds @@ -145,7 +147,7 @@ */ function write($key, $data) { - return $this->write_record($key, $this->packed ? serialize($data) : $data); + return $this->write_record($key, $this->serialize($data)); } @@ -192,20 +194,31 @@ */ function expunge() { - if ($this->type == 'db' && $this->db) { + if ($this->type == 'db' && $this->db && $this->ttl) { $this->db->query( - "DELETE FROM ".$this->db->table_name('cache'). + "DELETE FROM ".$this->table. " WHERE user_id = ?". " AND cache_key LIKE ?". - " AND " . $this->db->unixtimestamp('created')." < ?", + " AND expires < " . $this->db->now(), $this->userid, - $this->prefix.'.%', - time() - $this->ttl); + $this->prefix.'.%'); } } /** + * Remove expired records of all caches + */ + static function gc() + { + $rcube = rcube::get_instance(); + $db = $rcube->get_dbh(); + + $db->query("DELETE FROM " . $db->table_name('cache') . " WHERE expires < " . $db->now()); + } + + + /** * Writes the cache back to the DB. */ function close() @@ -219,7 +232,7 @@ if ($this->cache_changes[$key]) { // Make sure we're not going to write unchanged data // by comparing current md5 sum with the sum calculated on DB read - $data = $this->packed ? serialize($data) : $data; + $data = $this->serialize($data); if (!$this->cache_sums[$key] || $this->cache_sums[$key] != md5($data)) { $this->write_record($key, $data); @@ -255,7 +268,7 @@ if ($data) { $md5sum = md5($data); - $data = $this->packed ? unserialize($data) : $data; + $data = $this->unserialize($data); if ($nostore) { return $data; @@ -271,7 +284,7 @@ else { $sql_result = $this->db->limitquery( "SELECT data, cache_key". - " FROM ".$this->db->table_name('cache'). + " FROM " . $this->table. " WHERE user_id = ?". " AND cache_key = ?". // for better performance we allow more records for one key @@ -283,7 +296,7 @@ $key = substr($sql_arr['cache_key'], strlen($this->prefix)+1); $md5sum = $sql_arr['data'] ? md5($sql_arr['data']) : null; if ($sql_arr['data']) { - $data = $this->packed ? unserialize($sql_arr['data']) : $sql_arr['data']; + $data = $this->unserialize($sql_arr['data']); } if ($nostore) { @@ -326,7 +339,7 @@ // Remove NULL rows (here we don't need to check if the record exist) if ($data == 'N;') { $this->db->query( - "DELETE FROM ".$this->db->table_name('cache'). + "DELETE FROM " . $this->table. " WHERE user_id = ?". " AND cache_key = ?", $this->userid, $key); @@ -337,8 +350,10 @@ // update existing cache record if ($key_exists) { $result = $this->db->query( - "UPDATE ".$this->db->table_name('cache'). - " SET created = ". $this->db->now().", data = ?". + "UPDATE " . $this->table. + " SET created = " . $this->db->now(). + ", expires = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL'). + ", data = ?". " WHERE user_id = ?". " AND cache_key = ?", $data, $this->userid, $key); @@ -348,9 +363,9 @@ // for better performance we allow more records for one key // so, no need to check if record exist (see rcube_cache::read_record()) $result = $this->db->query( - "INSERT INTO ".$this->db->table_name('cache'). - " (created, user_id, cache_key, data)". - " VALUES (".$this->db->now().", ?, ?, ?)", + "INSERT INTO " . $this->table. + " (created, expires, user_id, cache_key, data)". + " VALUES (" . $this->db->now() . ", " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL') . ", ?, ?, ?)", $this->userid, $key, $data); } @@ -364,7 +379,6 @@ * @param string $key Cache key name or pattern * @param boolean $prefix_mode Enable it to clear all keys starting * with prefix specified in $key - * */ private function remove_record($key=null, $prefix_mode=false) { @@ -412,7 +426,7 @@ } $this->db->query( - "DELETE FROM ".$this->db->table_name('cache'). + "DELETE FROM " . $this->table. " WHERE user_id = ?" . $where, $this->userid); } @@ -553,4 +567,28 @@ // This way each cache will have its own index return sprintf('%d:%s%s', $this->userid, $this->prefix, 'INDEX'); } + + /** + * Serializes data for storing + */ + private function serialize($data) + { + if ($this->type == 'db') { + return $this->db->encode($data, $this->packed); + } + + return $this->packed ? serialize($data) : $data; + } + + /** + * Unserializes serialized data + */ + private function unserialize($data) + { + if ($this->type == 'db') { + return $this->db->decode($data, $this->packed); + } + + return $this->packed ? @unserialize($data) : $data; + } }
View file
kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_cache_shared.php
Added
@@ -0,0 +1,581 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | This file is part of the Roundcube Webmail client | + | Copyright (C) 2011-2013, The Roundcube Dev Team | + | Copyright (C) 2011-2013, Kolab Systems AG | + | | + | Licensed under the GNU General Public License version 3 or | + | any later version with exceptions for skins & plugins. | + | See the README file for a full license statement. | + | | + | PURPOSE: | + | Shared (cross-user) caching engine | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + | Author: Aleksander Machniak <alec@alec.pl> | + +-----------------------------------------------------------------------+ +*/ + + +/** + * Interface class for accessing Roundcube shared cache + * + * @package Framework + * @subpackage Cache + * @author Thomas Bruederli <roundcube@gmail.com> + * @author Aleksander Machniak <alec@alec.pl> + */ +class rcube_cache_shared +{ + /** + * Instance of database handler + * + * @var rcube_db|Memcache|bool + */ + private $db; + private $type; + private $prefix; + private $ttl; + private $packed; + private $index; + private $table; + private $cache = array(); + private $cache_changes = array(); + private $cache_sums = array(); + + + /** + * Object constructor. + * + * @param string $type Engine type ('db' or 'memcache' or 'apc') + * @param string $prefix Key name prefix + * @param string $ttl Expiration time of memcache/apc items + * @param bool $packed Enables/disabled data serialization. + * It's possible to disable data serialization if you're sure + * stored data will be always a safe string + */ + function __construct($type, $prefix='', $ttl=0, $packed=true) + { + $rcube = rcube::get_instance(); + $type = strtolower($type); + + if ($type == 'memcache') { + $this->type = 'memcache'; + $this->db = $rcube->get_memcache(); + } + else if ($type == 'apc') { + $this->type = 'apc'; + $this->db = function_exists('apc_exists'); // APC 3.1.4 required + } + else { + $this->type = 'db'; + $this->db = $rcube->get_dbh(); + $this->table = $this->db->table_name('cache_shared'); + } + + // convert ttl string to seconds + $ttl = get_offset_sec($ttl); + if ($ttl > 2592000) $ttl = 2592000; + + $this->ttl = $ttl; + $this->packed = $packed; + $this->prefix = $prefix; + } + + + /** + * Returns cached value. + * + * @param string $key Cache key name + * + * @return mixed Cached value + */ + function get($key) + { + if (!array_key_exists($key, $this->cache)) { + return $this->read_record($key); + } + + return $this->cache[$key]; + } + + + /** + * Sets (add/update) value in cache. + * + * @param string $key Cache key name + * @param mixed $data Cache data + */ + function set($key, $data) + { + $this->cache[$key] = $data; + $this->cache_changed = true; + $this->cache_changes[$key] = true; + } + + + /** + * Returns cached value without storing it in internal memory. + * + * @param string $key Cache key name + * + * @return mixed Cached value + */ + function read($key) + { + if (array_key_exists($key, $this->cache)) { + return $this->cache[$key]; + } + + return $this->read_record($key, true); + } + + + /** + * Sets (add/update) value in cache and immediately saves + * it in the backend, no internal memory will be used. + * + * @param string $key Cache key name + * @param mixed $data Cache data + * + * @param boolean True on success, False on failure + */ + function write($key, $data) + { + return $this->write_record($key, $this->serialize($data)); + } + + + /** + * Clears the cache. + * + * @param string $key Cache key name or pattern + * @param boolean $prefix_mode Enable it to clear all keys starting + * with prefix specified in $key + */ + function remove($key=null, $prefix_mode=false) + { + // Remove all keys + if ($key === null) { + $this->cache = array(); + $this->cache_changed = false; + $this->cache_changes = array(); + $this->cache_sums = array(); + } + // Remove keys by name prefix + else if ($prefix_mode) { + foreach (array_keys($this->cache) as $k) { + if (strpos($k, $key) === 0) { + $this->cache[$k] = null; + $this->cache_changes[$k] = false; + unset($this->cache_sums[$k]); + } + } + } + // Remove one key by name + else { + $this->cache[$key] = null; + $this->cache_changes[$key] = false; + unset($this->cache_sums[$key]); + } + + // Remove record(s) from the backend + $this->remove_record($key, $prefix_mode); + } + + + /** + * Remove cache records older than ttl + */ + function expunge() + { + if ($this->type == 'db' && $this->db && $this->ttl) { + $this->db->query( + "DELETE FROM " . $this->table + . " WHERE cache_key LIKE ?" + . " AND expires < " . $this->db->now(), + $this->prefix . '.%');
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_charset.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_charset.php
Changed
@@ -674,23 +674,27 @@ // 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',
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_config.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_config.php
Changed
@@ -26,6 +26,8 @@ { const DEFAULT_SKIN = 'larry'; + private $env = ''; + private $basedir = 'config/'; private $prop = array(); private $errors = array(); private $userprefs = array(); @@ -43,14 +45,21 @@ 'reply_mode' => 'top_posting', 'refresh_interval' => 'keep_alive', 'min_refresh_interval' => 'min_keep_alive', + 'messages_cache_ttl' => 'message_cache_lifetime', + 'redundant_attachments_cache_ttl' => 'redundant_attachments_memcache_ttl', ); /** * 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, @@ -67,16 +76,26 @@ */ 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'])) { @@ -153,7 +172,7 @@ } if ($fname) { - $this->load_from_file(RCUBE_CONFIG_DIR . $fname); + $this->load_from_file($fname); } } @@ -162,19 +181,25 @@ * 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)) { - $this->prop = array_merge($this->prop, $rcmail_config, $this->userprefs); + 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; } } @@ -182,6 +207,30 @@ 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 @@ -195,9 +244,6 @@ if (isset($this->prop[$name])) { $result = $this->prop[$name]; } - else if (isset($this->legacy_props[$name])) { - return $this->get($this->legacy_props[$name], $def); - } else { $result = $def; } @@ -241,6 +287,7 @@ public function merge($prefs) { $this->prop = array_merge($this->prop, $prefs, $this->userprefs); + $this->fix_legacy_props(); } @@ -273,6 +320,8 @@ $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']; @@ -435,4 +484,18 @@ return date_default_timezone_get(); } + /** + * Convert legacy options into new ones + */ + private function fix_legacy_props() + { + foreach ($this->legacy_props as $new => $old) { + if (isset($this->prop[$old])) { + if (!isset($this->prop[$new])) { + $this->prop[$new] = $this->prop[$old]; + } + unset($this->prop[$old]); + } + } + } }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_contacts.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_contacts.php
Changed
@@ -137,16 +137,34 @@ * List all active contact groups of this source * * @param string Search string to match group name + * @param int Matching mode: + * 0 - partial (*abc*), + * 1 - strict (=), + * 2 - prefix (abc*) + * * @return array Indexed list of contact groups, each a hash array */ - function list_groups($search = null) + function list_groups($search = null, $mode = 0) { $results = array(); if (!$this->groups) return $results; - $sql_filter = $search ? " AND " . $this->db->ilike('name', '%'.$search.'%') : ''; + if ($search) { + switch (intval($mode)) { + case 1: + $sql_filter = $this->db->ilike('name', $search); + break; + case 2: + $sql_filter = $this->db->ilike('name', $search . '%'); + break; + default: + $sql_filter = $this->db->ilike('name', '%' . $search . '%'); + } + + $sql_filter = " AND $sql_filter"; + } $sql_result = $this->db->query( "SELECT * FROM ".$this->db->table_name($this->db_groups). @@ -626,10 +644,6 @@ $insert_id = $this->db->insert_id($this->db_name); } - // also add the newly created contact to the active group - if ($insert_id && $this->group_id) - $this->add_to_group($this->group_id, $insert_id); - $this->cache = null; return $insert_id; @@ -883,9 +897,10 @@ /** * Add the given contact records the a certain group * - * @param string Group identifier - * @param array List of contact identifiers to be added - * @return int Number of contacts added + * @param string Group identifier + * @param array|string List of contact identifiers to be added + * + * @return int Number of contacts added */ function add_to_group($group_id, $ids) { @@ -930,9 +945,10 @@ /** * Remove the given contact records from a certain group * - * @param string Group identifier - * @param array List of contact identifiers to be removed - * @return int Number of deleted group members + * @param string Group identifier + * @param array|string List of contact identifiers to be removed + * + * @return int Number of deleted group members */ function remove_from_group($group_id, $ids) {
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_content_filter.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_content_filter.php
Changed
@@ -20,7 +20,7 @@ * PHP stream filter to detect html/javascript code in attachments * * @package Framework - * @subpackage Core + * @subpackage Utils */ class rcube_content_filter extends php_user_filter {
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_csv2vcard.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_csv2vcard.php
Changed
@@ -130,6 +130,21 @@ 'work_state' => 'region:work', 'home_city_short' => 'locality:home', 'home_state_short' => 'region:home', + + // Atmail + 'date_of_birth' => 'birthday', + 'email' => 'email:pref', + 'home_mobile' => 'phone:cell', + 'home_zip' => 'zipcode:home', + 'info' => 'notes', + 'user_photo' => 'photo', + 'url' => 'website:homepage', + 'work_company' => 'organization', + 'work_dept' => 'departament', + 'work_fax' => 'phone:work,fax', + 'work_mobile' => 'phone:work,cell', + 'work_title' => 'jobtitle', + 'work_zip' => 'zipcode:work', ); /** @@ -230,8 +245,29 @@ 'work_phone' => "Work Phone", 'work_address' => "Work Address", //'work_address_2' => "Work Address 2", + 'work_city' => "Work City", 'work_country' => "Work Country", + 'work_state' => "Work State", 'work_zipcode' => "Work ZipCode", + + // Atmail + 'date_of_birth' => "Date of Birth", + 'email' => "Email", + //'email_2' => "Email2", + //'email_3' => "Email3", + //'email_4' => "Email4", + //'email_5' => "Email5", + 'home_mobile' => "Home Mobile", + 'home_zip' => "Home Zip", + 'info' => "Info", + 'user_photo' => "User Photo", + 'url' => "URL", + 'work_company' => "Work Company", + 'work_dept' => "Work Dept", + 'work_fax' => "Work Fax", + 'work_mobile' => "Work Mobile", + 'work_title' => "Work Title", + 'work_zip' => "Work Zip", ); protected $local_label_map = array(); @@ -268,7 +304,6 @@ { // convert to UTF-8 $head = substr($csv, 0, 4096); - $fallback = rcube::get_instance()->config->get('default_charset', 'ISO-8859-1'); // fallback to Latin-1? $charset = rcube_charset::detect($head, RCUBE_CHARSET); $csv = rcube_charset::convert($csv, $charset); $head = ''; @@ -276,7 +311,7 @@ $this->map = array(); // Parse file - foreach (preg_split("/[\r\n]+/", $csv) as $i => $line) { + foreach (preg_split("/[\r\n]+/", $csv) as $line) { $elements = $this->parse_line($line); if (empty($elements)) { continue; @@ -353,6 +388,12 @@ if (!empty($this->local_label_map)) { for ($i = 0; $i < $size; $i++) { $label = $this->local_label_map[$elements[$i]]; + + // special localization label + if ($label && $label[0] == '_') { + $label = substr($label, 1); + } + if ($label && !empty($this->csv2vcard_map[$label])) { $map2[$i] = $this->csv2vcard_map[$label]; } @@ -384,9 +425,13 @@ $contact['birthday'] = $contact['birthday-y'] .'-' .$contact['birthday-m'] . '-' . $contact['birthday-d']; } + // Empty dates, e.g. "0/0/00", "0000-00-00 00:00:00" foreach (array('birthday', 'anniversary') as $key) { - if (!empty($contact[$key]) && $contact[$key] == '0/0/00') { // @TODO: localization? - unset($contact[$key]); + if (!empty($contact[$key])) { + $date = preg_replace('/[0[:^word:]]/', '', $contact[$key]); + if (empty($date)) { + unset($contact[$key]); + } } }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_db.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_db.php
Changed
@@ -47,6 +47,7 @@ 'identifier_end' => '"', ); + const DEBUG_LINE_LENGTH = 4096; /** * Factory, returns driver-specific instance of the class @@ -70,7 +71,7 @@ $driver = isset($driver_map[$driver]) ? $driver_map[$driver] : $driver; $class = "rcube_db_$driver"; - if (!class_exists($class)) { + if (!$driver || !class_exists($class)) { rcube::raise_error(array('code' => 600, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, 'message' => "Configuration error. Unsupported database driver: $driver"), @@ -99,27 +100,15 @@ $this->db_dsnw_array = self::parse_dsn($db_dsnw); $this->db_dsnr_array = self::parse_dsn($db_dsnr); - - // Initialize driver class - $this->init(); - } - - /** - * Initialization of the object with driver specific code - */ - protected function init() - { - // To be used by driver classes } /** * Connect to specific database * - * @param array $dsn DSN for DB connections - * - * @return PDO database handle + * @param array $dsn DSN for DB connections + * @param string $mode Connection mode (r|w) */ - protected function dsn_connect($dsn) + protected function dsn_connect($dsn, $mode) { $this->db_error = false; $this->db_error_msg = null; @@ -128,7 +117,7 @@ $dsn_string = $this->dsn_string($dsn); $dsn_options = $this->dsn_options($dsn); - if ($db_pconn) { + if ($this->db_pconn) { $dsn_options[PDO::ATTR_PERSISTENT] = true; } @@ -157,9 +146,10 @@ return null; } + $this->dbh = $dbh; + $this->db_mode = $mode; + $this->db_connected = true; $this->conn_configure($dsn, $dbh); - - return $dbh; } /** @@ -182,16 +172,6 @@ } /** - * Driver-specific database character set setting - * - * @param string $charset Character set name - */ - protected function set_charset($charset) - { - $this->query("SET NAMES 'utf8'"); - } - - /** * Connect to appropriate database depending on the operation * * @param string $mode Connection mode (r|w) @@ -218,23 +198,14 @@ $dsn = ($mode == 'r') ? $this->db_dsnr_array : $this->db_dsnw_array; - $this->dbh = $this->dsn_connect($dsn); - $this->db_connected = is_object($this->dbh); + $this->dsn_connect($dsn, $mode); // use write-master when read-only fails - if (!$this->db_connected && $mode == 'r') { - $mode = 'w'; - $this->dbh = $this->dsn_connect($this->db_dsnw_array); - $this->db_connected = is_object($this->dbh); + if (!$this->db_connected && $mode == 'r' && $this->is_replicated()) { + $this->dsn_connect($this->db_dsnw_array, 'w'); } - if ($this->db_connected) { - $this->db_mode = $mode; - $this->set_charset('utf8'); - } - else { - $this->conn_failure = true; - } + $this->conn_failure = !$this->db_connected; } /** @@ -255,6 +226,11 @@ protected function debug($query) { if ($this->options['debug_mode']) { + if (($len = strlen($query)) > self::DEBUG_LINE_LENGTH) { + $diff = $len - self::DEBUG_LINE_LENGTH; + $query = substr($query, 0, self::DEBUG_LINE_LENGTH) + . "... [truncated $diff bytes]"; + } rcube::write_log('sql', '[' . (++$this->db_index) . '] ' . $query . ';'); } } @@ -362,8 +338,10 @@ */ protected function _query($query, $offset, $numrows, $params) { + $query = trim($query); + // Read or write ? - $mode = preg_match('/^(select|show)/i', ltrim($query)) ? 'r' : 'w'; + $mode = preg_match('/^(select|show|set)/i', $query) ? 'r' : 'w'; $this->db_connect($mode); @@ -405,21 +383,25 @@ $this->db_error_msg = null; // send query - $query = $this->dbh->query($query); + $result = $this->dbh->query($query); - if ($query === false) { + if ($result === false) { $error = $this->dbh->errorInfo(); - $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), true, false); + 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); + } } - $this->last_result = $query; + $this->last_result = $result; - return $query; + return $result; } /** @@ -439,6 +421,32 @@ } /** + * Get number of rows for a SQL query + * If no query handle is specified, the last query will be taken as reference + * + * @param mixed $result Optional query handle + * @return mixed Number of rows or false on failure + * @deprecated This method shows very poor performance and should be avoided. + */ + public function num_rows($result = null) + { + if ($result || ($result === null && ($result = $this->last_result))) { + // repeat query with SELECT COUNT(*) ... + if (preg_match('/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/ims', $result->queryString, $m)) { + $query = $this->dbh->query('SELECT COUNT(*) FROM ' . $m[1], PDO::FETCH_NUM); + return $query ? intval($query->fetchColumn(0)) : false; + } + else { + $num = count($result->fetchAll()); + $result->execute(); // re-execute query because there's no seek(0) + return $num; + } + } +
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_db_mssql.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_db_mssql.php
Changed
@@ -29,38 +29,52 @@ public $db_provider = 'mssql'; /** - * Driver initialization + * Object constructor + * + * @param string $db_dsnw DSN for read/write operations + * @param string $db_dsnr Optional DSN for read only operations + * @param bool $pconn Enables persistent connections */ - protected function init() + public function __construct($db_dsnw, $db_dsnr = '', $pconn = false) { + parent::__construct($db_dsnw, $db_dsnr, $pconn); + $this->options['identifier_start'] = '['; $this->options['identifier_end'] = ']'; } /** - * Character setting + * Driver-specific configuration of database connection + * + * @param array $dsn DSN for DB connections + * @param PDO $dbh Connection handler */ - protected function set_charset($charset) + protected function conn_configure($dsn, $dbh) { - // UTF-8 is default + // Set date format in case of non-default language (#1488918) + $this->query("SET DATEFORMAT ymd"); } /** * Return SQL function for current time and date * + * @param int $interval Optional interval (in seconds) to add/subtract + * * @return string SQL function to use in query */ - public function now() + public function now($interval = 0) { + if ($interval) { + $interval = intval($interval); + return "dateadd(second, $interval, getdate())"; + } + return "getdate()"; } /** * Return SQL statement to convert a field value into a unix timestamp * - * This method is deprecated and should not be used anymore due to limitations - * of timestamp functions in Mysql (year 2038 problem) - * * @param string $field Field name * * @return string SQL statement to use in query @@ -100,26 +114,30 @@ { $limit = intval($limit); $offset = intval($offset); + $end = $offset + $limit; - $orderby = stristr($query, 'ORDER BY'); - if ($orderby !== false) { - $sort = (stripos($orderby, ' desc') !== false) ? 'desc' : 'asc'; - $order = str_ireplace('ORDER BY', '', $orderby); - $order = trim(preg_replace('/\bASC\b|\bDESC\b/i', '', $order)); + // query without OFFSET + if (!$offset) { + $query = preg_replace('/^SELECT\s/i', "SELECT TOP $limit ", $query); + return $query; } - $query = preg_replace('/^SELECT\s/i', 'SELECT TOP ' . ($limit + $offset) . ' ', $query); + $orderby = stristr($query, 'ORDER BY'); + $offset += 1; - $query = 'SELECT * FROM (SELECT TOP ' . $limit . ' * FROM (' . $query . ') AS inner_tbl'; if ($orderby !== false) { - $query .= ' ORDER BY ' . $order . ' '; - $query .= (stripos($sort, 'asc') !== false) ? 'DESC' : 'ASC'; + $query = trim(substr($query, 0, -1 * strlen($orderby))); } - $query .= ') AS outer_tbl'; - if ($orderby !== false) { - $query .= ' ORDER BY ' . $order . ' ' . $sort; + else { + // it shouldn't happen, paging without sorting has not much sense + // @FIXME: I don't know how to build paging query without ORDER BY + $orderby = "ORDER BY 1"; } + $query = preg_replace('/^SELECT\s/i', '', $query); + $query = "WITH paging AS (SELECT ROW_NUMBER() OVER ($orderby) AS [RowNumber], $query)" + . " SELECT * FROM paging WHERE [RowNumber] BETWEEN $offset AND $end ORDER BY [RowNumber]"; + return $query; }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_db_mysql.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_db_mysql.php
Changed
@@ -30,16 +30,40 @@ public $db_provider = 'mysql'; /** - * Driver initialization/configuration + * Object constructor + * + * @param string $db_dsnw DSN for read/write operations + * @param string $db_dsnr Optional DSN for read only operations + * @param bool $pconn Enables persistent connections */ - protected function init() + public function __construct($db_dsnw, $db_dsnr = '', $pconn = false) { + if (version_compare(PHP_VERSION, '5.3.0', '<')) { + rcube::raise_error(array('code' => 600, 'type' => 'db', + 'line' => __LINE__, 'file' => __FILE__, + 'message' => "MySQL driver requires PHP >= 5.3, current version is " . PHP_VERSION), + true, true); + } + + parent::__construct($db_dsnw, $db_dsnr, $pconn); + // SQL identifiers quoting $this->options['identifier_start'] = '`'; $this->options['identifier_end'] = '`'; } /** + * Driver-specific configuration of database connection + * + * @param array $dsn DSN for DB connections + * @param PDO $dbh Connection handler + */ + protected function conn_configure($dsn, $dbh) + { + $this->query("SET NAMES 'utf8'"); + } + + /** * Abstract SQL statement for value concatenation * * @return string SQL statement to be used in query @@ -127,7 +151,7 @@ $result[PDO::MYSQL_ATTR_FOUND_ROWS] = true; // Enable AUTOCOMMIT mode (#1488902) - $dsn_options[PDO::ATTR_AUTOCOMMIT] = true; + $result[PDO::ATTR_AUTOCOMMIT] = true; return $result; } @@ -147,7 +171,7 @@ $result = $this->query('SHOW VARIABLES'); - while ($sql_arr = $this->fetch_array($result)) { + while ($row = $this->fetch_array($result)) { $this->variables[$row[0]] = $row[1]; } }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_db_pgsql.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_db_pgsql.php
Changed
@@ -29,6 +29,17 @@ public $db_provider = 'postgres'; /** + * Driver-specific configuration of database connection + * + * @param array $dsn DSN for DB connections + * @param PDO $dbh Connection handler + */ + protected function conn_configure($dsn, $dbh) + { + $this->query("SET NAMES 'utf8'"); + } + + /** * Get last inserted record ID * * @param string $table Table name (to find the incremented sequence) @@ -53,19 +64,20 @@ /** * Return correct name for a specific database sequence * - * @param string $sequence Secuence name + * @param string $table Table name * * @return string Translated sequence name */ - protected function sequence_name($sequence) + protected function sequence_name($table) { - $rcube = rcube::get_instance(); + // Note: we support only one sequence per table + // Note: The sequence name must be <table_name>_seq + $sequence = $table . '_seq'; + $rcube = rcube::get_instance(); // return sequence name if configured - $config_key = 'db_sequence_'.$sequence; - - if ($name = $rcube->config->get($config_key)) { - return $name; + if ($prefix = $rcube->config->get('db_prefix')) { + return $prefix . $sequence; } return $sequence; @@ -74,9 +86,6 @@ /** * Return SQL statement to convert a field value into a unix timestamp * - * This method is deprecated and should not be used anymore due to limitations - * of timestamp functions in Mysql (year 2038 problem) - * * @param string $field Field name * * @return string SQL statement to use in query @@ -88,6 +97,24 @@ } /** + * Return SQL function for current time and date + * + * @param int $interval Optional interval (in seconds) to add/subtract + * + * @return string SQL function to use in query + */ + public function now($interval = 0) + { + if ($interval) { + $add = ' ' . ($interval > 0 ? '+' : '-') . " interval '"; + $add .= $interval > 0 ? intval($interval) : intval($interval) * -1; + $add .= " seconds'"; + } + + return "now()" . $add; + } + + /** * Return SQL statement for case insensitive LIKE * * @param string $column Field name @@ -129,4 +156,38 @@ return isset($this->variables[$varname]) ? $this->variables[$varname] : $default; } + /** + * Returns PDO DSN string from DSN array + * + * @param array $dsn DSN parameters + * + * @return string DSN string + */ + protected function dsn_string($dsn) + { + $params = array(); + $result = 'pgsql:'; + + if ($dsn['hostspec']) { + $params[] = 'host=' . $dsn['hostspec']; + } + else if ($dsn['socket']) { + $params[] = 'host=' . $dsn['socket']; + } + + if ($dsn['port']) { + $params[] = 'port=' . $dsn['port']; + } + + if ($dsn['database']) { + $params[] = 'dbname=' . $dsn['database']; + } + + if (!empty($params)) { + $result .= implode(';', $params); + } + + return $result; + } + }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_db_sqlite.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_db_sqlite.php
Changed
@@ -29,13 +29,6 @@ public $db_provider = 'sqlite'; /** - * Database character set - */ - protected function set_charset($charset) - { - } - - /** * Prepare connection */ protected function conn_prepare($dsn) @@ -56,10 +49,6 @@ */ protected function conn_configure($dsn, $dbh) { - // we emulate via callback some missing functions - $dbh->sqliteCreateFunction('unix_timestamp', array('rcube_db_sqlite', 'sqlite_unix_timestamp'), 1); - $dbh->sqliteCreateFunction('now', array('rcube_db_sqlite', 'sqlite_now'), 0); - // Initialize database structure in file is empty if (!empty($dsn['database']) && !filesize($dsn['database'])) { $data = file_get_contents(RCUBE_INSTALL_PATH . 'SQL/sqlite.initial.sql'); @@ -83,30 +72,32 @@ } /** - * Callback for sqlite: unix_timestamp() + * Return SQL statement to convert a field value into a unix timestamp + * + * @param string $field Field name + * + * @return string SQL statement to use in query + * @deprecated */ - public static function sqlite_unix_timestamp($timestamp = '') + public function unixtimestamp($field) { - $timestamp = trim($timestamp); - if (!$timestamp) { - $ret = time(); - } - else if (!preg_match('/^[0-9]+$/s', $timestamp)) { - $ret = strtotime($timestamp); - } - else { - $ret = $timestamp; - } - - return $ret; + return "strftime('%s', $field)"; } /** - * Callback for sqlite: now() + * Return SQL function for current time and date + * + * @param int $interval Optional interval (in seconds) to add/subtract + * + * @return string SQL function to use in query */ - public static function sqlite_now() + public function now($interval = 0) { - return date("Y-m-d H:i:s"); + if ($interval) { + $add = ($interval > 0 ? '+' : '') . intval($interval) . ' seconds'; + } + + return "datetime('now'" . ($add ? ",'$add'" : "") . ")"; } /**
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_db_sqlsrv.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_db_sqlsrv.php
Changed
@@ -29,29 +29,46 @@ public $db_provider = 'mssql'; /** - * Driver initialization + * Object constructor + * + * @param string $db_dsnw DSN for read/write operations + * @param string $db_dsnr Optional DSN for read only operations + * @param bool $pconn Enables persistent connections */ - protected function init() + public function __construct($db_dsnw, $db_dsnr = '', $pconn = false) { + parent::__construct($db_dsnw, $db_dsnr, $pconn); + $this->options['identifier_start'] = '['; $this->options['identifier_end'] = ']'; } /** - * Database character set setting + * Driver-specific configuration of database connection + * + * @param array $dsn DSN for DB connections + * @param PDO $dbh Connection handler */ - protected function set_charset($charset) + protected function conn_configure($dsn, $dbh) { - // UTF-8 is default + // Set date format in case of non-default language (#1488918) + $this->query("SET DATEFORMAT ymd"); } /** * Return SQL function for current time and date * + * @param int $interval Optional interval (in seconds) to add/subtract + * * @return string SQL function to use in query */ - public function now() + public function now($interval = 0) { + if ($interval) { + $interval = intval($interval); + return "dateadd(second, $interval, getdate())"; + } + return "getdate()"; } @@ -100,26 +117,30 @@ { $limit = intval($limit); $offset = intval($offset); + $end = $offset + $limit; - $orderby = stristr($query, 'ORDER BY'); - if ($orderby !== false) { - $sort = (stripos($orderby, ' desc') !== false) ? 'desc' : 'asc'; - $order = str_ireplace('ORDER BY', '', $orderby); - $order = trim(preg_replace('/\bASC\b|\bDESC\b/i', '', $order)); + // query without OFFSET + if (!$offset) { + $query = preg_replace('/^SELECT\s/i', "SELECT TOP $limit ", $query); + return $query; } - $query = preg_replace('/^SELECT\s/i', 'SELECT TOP ' . ($limit + $offset) . ' ', $query); + $orderby = stristr($query, 'ORDER BY'); + $offset += 1; - $query = 'SELECT * FROM (SELECT TOP ' . $limit . ' * FROM (' . $query . ') AS inner_tbl'; if ($orderby !== false) { - $query .= ' ORDER BY ' . $order . ' '; - $query .= (stripos($sort, 'asc') !== false) ? 'DESC' : 'ASC'; + $query = trim(substr($query, 0, -1 * strlen($orderby))); } - $query .= ') AS outer_tbl'; - if ($orderby !== false) { - $query .= ' ORDER BY ' . $order . ' ' . $sort; + else { + // it shouldn't happen, paging without sorting has not much sense + // @FIXME: I don't know how to build paging query without ORDER BY + $orderby = "ORDER BY 1"; } + $query = preg_replace('/^SELECT\s/i', '', $query); + $query = "WITH paging AS (SELECT ROW_NUMBER() OVER ($orderby) AS [RowNumber], $query)" + . " SELECT * FROM paging WHERE [RowNumber] BETWEEN $offset AND $end ORDER BY [RowNumber]"; + return $query; }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_enriched.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_enriched.php
Changed
@@ -118,7 +118,7 @@ $quoted = ''; $lines = explode('<br>', $a[2]); - foreach ($lines as $n => $line) + foreach ($lines as $line) $quoted .= '>'.$line.'<br>'; $body = $a[1].'<span class="quotes">'.$quoted.'</span>'.$a[3];
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_html2text.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_html2text.php
Changed
@@ -571,55 +571,65 @@ */ protected function _convert_blockquotes(&$text) { - if (preg_match_all('/<\/*blockquote[^>]*>/i', $text, $matches, PREG_OFFSET_CAPTURE)) { - $level = 0; - $diff = 0; - foreach ($matches[0] as $m) { - if ($m[0][0] == '<' && $m[0][1] == '/') { + $level = 0; + $offset = 0; + while (($start = strpos($text, '<blockquote', $offset)) !== false) { + $offset = $start + 12; + do { + $end = strpos($text, '</blockquote>', $offset); + $next = strpos($text, '<blockquote', $offset); + + // nested <blockquote>, skip + if ($next !== false && $next < $end) { + $offset = $next + 12; + $level++; + } + // nested </blockquote> tag + if ($end !== false && $level > 0) { + $offset = $end + 12; $level--; - if ($level < 0) { - $level = 0; // malformed HTML: go to next blockquote - } - else if ($level > 0) { - // skip inner blockquote - } - else { - $end = $m[1]; - $len = $end - $taglen - $start; - // Get blockquote content - $body = substr($text, $start + $taglen - $diff, $len); - - // Set text width - $p_width = $this->width; - if ($this->width > 0) $this->width -= 2; - // Convert blockquote content - $body = trim($body); - $this->_converter($body); - // Add citation markers and create PRE block - $body = preg_replace('/((^|\n)>*)/', '\\1> ', trim($body)); - $body = '<pre>' . htmlspecialchars($body) . '</pre>'; - // Re-set text width - $this->width = $p_width; - // Replace content - $text = substr($text, 0, $start - $diff) - . $body . substr($text, $end + strlen($m[0]) - $diff); - - $diff = $len + $taglen + strlen($m[0]) - strlen($body); - unset($body); - } } - else { - if ($level == 0) { - $start = $m[1]; - $taglen = strlen($m[0]); - } - $level ++; + // found matching end tag + else if ($end !== false && $level == 0) { + $taglen = strpos($text, '>', $start) - $start; + $startpos = $start + $taglen + 1; + + // get blockquote content + $body = trim(substr($text, $startpos, $end - $startpos)); + + // adjust text wrapping width + $p_width = $this->width; + if ($this->width > 0) $this->width -= 2; + + // replace content with inner blockquotes + $this->_converter($body); + + // resore text width + $this->width = $p_width; + + // Add citation markers and create <pre> block + $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); + $offset = 0; + break; } - } + } while ($end || $next); } } /** + * Callback function to correctly add citation markers for blockquote contents + */ + public function blockquote_citation_ballback($m) + { + $line = ltrim($m[2]); + $space = $line[0] == '>' ? '' : ' '; + return $m[1] . '>' . $space . $line; + } + + /** * Callback function for preg_replace_callback use. * * @param array PREG matches
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_image.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_image.php
Changed
@@ -77,7 +77,8 @@ } /** - * Resize image to a given size + * Resize image to a given size. Use only to shrink an image. + * If an image is smaller than specified size it will be not resized. * * @param int $size Max width/height size * @param string $filename Output filename @@ -92,6 +93,10 @@ $convert = $rcube->config->get('im_convert_path', false); $props = $this->props(); + if (empty($props)) { + return false; + } + if (!$filename) { $filename = $this->image_file; } @@ -100,7 +105,6 @@ if ($convert) { $p['out'] = $filename; $p['in'] = $this->image_file; - $p['size'] = $size.'x'.$size; $type = $props['type']; if (!$type && ($data = $this->identify())) { @@ -115,14 +119,41 @@ $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 === '') { + @chmod($filename, 0600); return $type; } } @@ -131,49 +162,62 @@ if ($props['gd_type']) { if ($props['gd_type'] == IMAGETYPE_JPEG && function_exists('imagecreatefromjpeg')) { $image = imagecreatefromjpeg($this->image_file); + $type = 'jpg'; } else if($props['gd_type'] == IMAGETYPE_GIF && function_exists('imagecreatefromgif')) { $image = imagecreatefromgif($this->image_file); + $type = 'gid'; } else if($props['gd_type'] == IMAGETYPE_PNG && function_exists('imagecreatefrompng')) { $image = imagecreatefrompng($this->image_file); + $type = 'png'; } else { // @TODO: print error to the log? return false; } - $scale = $size / max($props['width'], $props['height']); - $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 ($image === false) { + return false; } - imagecopyresampled($new_image, $image, 0, 0, 0, 0, $width, $height, $props['width'], $props['height']); - $image = $new_image; + $scale = $size / max($props['width'], $props['height']); - if ($props['gd_type'] == IMAGETYPE_JPEG) { - $result = imagejpeg($image, $filename, 75); - $type = 'jpg'; + // 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) { + $result = $this->image_file == $filename || copy($this->image_file, $filename); } - elseif($props['gd_type'] == IMAGETYPE_GIF) { - $result = imagegif($image, $filename); - $type = 'gid'; - } - elseif($props['gd_type'] == IMAGETYPE_PNG) { - $result = imagepng($image, $filename, 6, PNG_ALL_FILTERS); - $type = 'png'; + 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) { + @chmod($filename, 0600); return $type; } } @@ -211,9 +255,10 @@ $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); return true; } } @@ -245,6 +290,11 @@ else if ($type == self::TYPE_PNG) { $result = imagepng($image, $filename, 6, PNG_ALL_FILTERS); } + + if ($result) { + @chmod($filename, 0600); + return true; + } } // @TODO: print error to the log?
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_imap.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_imap.php
Changed
@@ -308,14 +308,7 @@ */ public function set_folder($folder) { - if ($this->folder == $folder) { - return; - } - $this->folder = $folder; - - // clear messagecount cache for this folder - $this->clear_messagecount($folder); } @@ -626,7 +619,7 @@ } if ($mode == 'THREADS') { - $res = $this->fetch_threads($folder, $force); + $res = $this->threads($folder); $count = $res->count(); if ($status) { @@ -656,11 +649,11 @@ $keys[] = 'ALL'; } if ($status) { - $keys[] = 'MAX'; + $keys[] = 'MAX'; } } - // @TODO: if $force==false && $mode == 'ALL' we could try to use cache index here + // @TODO: if $mode == 'ALL' we could try to use cache index here // get message count using (E)SEARCH // not very performant but more precise (using UNDELETED) @@ -791,7 +784,7 @@ $threads = $mcache->get_thread($folder); } else { - $threads = $this->fetch_threads($folder); + $threads = $this->threads($folder); } return $this->fetch_thread_headers($folder, $threads, $page, $slice); @@ -800,32 +793,47 @@ /** * Method for fetching threads data * - * @param string $folder Folder name - * @param bool $force Use IMAP server, no cache + * @param string $folder Folder name * * @return rcube_imap_thread Thread data object */ - function fetch_threads($folder, $force = false) + function threads($folder) { - if (!$force && ($mcache = $this->get_mcache_engine())) { + if ($mcache = $this->get_mcache_engine()) { // don't store in self's internal cache, cache has it's own internal cache return $mcache->get_thread($folder); } - if (empty($this->icache['threads'])) { - if (!$this->check_connection()) { - return new rcube_result_thread(); + if (!empty($this->icache['threads'])) { + if ($this->icache['threads']->get_parameters('MAILBOX') == $folder) { + return $this->icache['threads']; } + } - // get all threads - $result = $this->conn->thread($folder, $this->threading, - $this->options['skip_deleted'] ? 'UNDELETED' : '', true); + // get all threads + $result = $this->threads_direct($folder); - // add to internal (fast) cache - $this->icache['threads'] = $result; + // add to internal (fast) cache + return $this->icache['threads'] = $result; + } + + + /** + * Method for direct fetching of threads data + * + * @param string $folder Folder name + * + * @return rcube_imap_thread Thread data object + */ + function threads_direct($folder) + { + if (!$this->check_connection()) { + return new rcube_result_thread(); } - return $this->icache['threads']; + // get all threads + return $this->conn->thread($folder, $this->threading, + $this->options['skip_deleted'] ? 'UNDELETED' : '', true); } @@ -981,7 +989,7 @@ // use memory less expensive (and quick) method for big result set $index = clone $this->index('', $this->sort_field, $this->sort_order); // get messages uids for one page... - $index->slice($start_msg, min($cnt-$from, $this->page_size)); + $index->slice($from, min($cnt-$from, $this->page_size)); if ($slice) { $index->slice(-$slice, $slice); @@ -1096,16 +1104,17 @@ /** - * Returns current status of folder + * Returns current status of a folder (compared to the last time use) * * We compare the maximum UID to determine the number of * new messages because the RECENT flag is not reliable. * * @param string $folder Folder name + * @param array $diff Difference data * - * @return int Folder status + * @return int Folder status */ - public function folder_status($folder = null) + public function folder_status($folder = null, &$diff = array()) { if (!strlen($folder)) { $folder = $this->folder; @@ -1126,6 +1135,9 @@ // got new messages if ($new['maxuid'] > $old['maxuid']) { $result += 1; + // get new message UIDs range, that can be used for example + // to get the data of these messages + $diff['new'] = ($old['maxuid'] + 1 < $new['maxuid'] ? ($old['maxuid']+1).':' : '') . $new['maxuid']; } // some messages has been deleted if ($new['cnt'] < $old['cnt']) { @@ -1176,12 +1188,15 @@ * @param string $folder Folder to get index from * @param string $sort_field Sort column * @param string $sort_order Sort order [ASC, DESC] + * @param bool $no_threads Get not threaded index + * @param bool $no_search Get index not limited to search result (optionally) * * @return rcube_result_index|rcube_result_thread List of messages (UIDs) */ - public function index($folder = '', $sort_field = NULL, $sort_order = NULL) - { - if ($this->threading) { + public function index($folder = '', $sort_field = NULL, $sort_order = NULL, + $no_threads = false, $no_search = false + ) { + if (!$no_threads && $this->threading) { return $this->thread_index($folder, $sort_field, $sort_order); } @@ -1193,43 +1208,50 @@ // we have a saved search result, get index from there if ($this->search_string) { - if ($this->search_threads) { - $this->search($folder, $this->search_string, $this->search_charset, $this->sort_field); + if ($this->search_set->is_empty()) { + return new rcube_result_index($folder, '* SORT'); } - // use message index sort as default sorting - if (!$this->sort_field || $this->search_sorted) { - if ($this->sort_field && $this->search_sort_field != $this->sort_field) { - $this->search($folder, $this->search_string, $this->search_charset, $this->sort_field); - } + // search result is an index with the same sorting? + if (($this->search_set instanceof rcube_result_index) + && ((!$this->sort_field && !$this->search_sorted) || + ($this->search_sorted && $this->search_sort_field == $this->sort_field)) + ) { $index = $this->search_set; } - else if (!$this->check_connection()) { - return new rcube_result_index(); - } - else { - $index = $this->conn->index($folder, $this->search_set->get(), - $this->sort_field, $this->options['skip_deleted'], true, true); + // $no_search is enabled when we are not interested in + // fetching index for search result, e.g. to sort + // threaded search result we can use full mailbox index. + // This makes possible to use index from cache + else if (!$no_search) {
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_imap_cache.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_imap_cache.php
Changed
@@ -49,6 +49,13 @@ private $userid; /** + * Expiration time in seconds + * + * @var int + */ + private $ttl; + + /** * Internal (in-memory) cache * * @var array @@ -83,13 +90,25 @@ /** * Object constructor. + * + * @param rcube_db $db DB handler + * @param rcube_imap $imap IMAP handler + * @param int $userid User identifier + * @param bool $skip_deleted skip_deleted flag + * @param string $ttl Expiration time of memcache/apc items + * */ - function __construct($db, $imap, $userid, $skip_deleted) + function __construct($db, $imap, $userid, $skip_deleted, $ttl=0) { + // convert ttl string to seconds + $ttl = get_offset_sec($ttl); + if ($ttl > 2592000) $ttl = 2592000; + $this->db = $db; $this->imap = $imap; $this->userid = $userid; $this->skip_deleted = $skip_deleted; + $this->ttl = $ttl; } @@ -215,9 +234,7 @@ * Return messages thread. * If threaded index doesn't exist or is invalid, will be updated. * - * @param string $mailbox Folder name - * @param string $sort_field Sorting column - * @param string $sort_order Sorting order (ASC|DESC) + * @param string $mailbox Folder name * * @return array Messages threaded index */ @@ -256,19 +273,11 @@ if ($index === null) { // Get mailbox data (UIDVALIDITY, counters, etc.) for status check $mbox_data = $this->imap->folder_data($mailbox); - - if ($mbox_data['EXISTS']) { - // get all threads (default sort order) - $threads = $this->imap->fetch_threads($mailbox, true); - } - else { - $threads = new rcube_result_thread($mailbox, '* THREAD'); - } - - $index['object'] = $threads; + // Get THREADS result + $index['object'] = $this->get_thread_data($mailbox, $mbox_data); // insert/update - $this->add_thread_row($mailbox, $threads, $mbox_data, $exists); + $this->add_thread_row($mailbox, $index['object'], $mbox_data, $exists); } $this->icache[$mailbox]['thread'] = $index; @@ -407,8 +416,8 @@ return; } - $msg = serialize($this->db->encode(clone $message)); $flags = 0; + $msg = clone $message; if (!empty($message->flags)) { foreach ($this->flags as $idx => $flag) { @@ -417,30 +426,49 @@ } } } + unset($msg->flags); + $msg = $this->db->encode($msg, true); // update cache record (even if it exists, the update // here will work as select, assume row exist if affected_rows=0) if (!$force) { $res = $this->db->query( "UPDATE ".$this->db->table_name('cache_messages') - ." SET flags = ?, data = ?, changed = ".$this->db->now() + ." SET flags = ?, data = ?, expires = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL') ." WHERE user_id = ?" ." AND mailbox = ?" ." AND uid = ?", $flags, $msg, $this->userid, $mailbox, (int) $message->uid); - if ($this->db->affected_rows()) { + if ($this->db->affected_rows($res)) { return; } } + $this->db->set_option('ignore_key_errors', true); + // insert new record - $this->db->query( + $res = $this->db->query( "INSERT INTO ".$this->db->table_name('cache_messages') - ." (user_id, mailbox, uid, flags, changed, data)" - ." VALUES (?, ?, ?, ?, ".$this->db->now().", ?)", + ." (user_id, mailbox, uid, flags, expires, data)" + ." VALUES (?, ?, ?, ?, ". ($this->ttl ? $this->db->now($this->ttl) : 'NULL') . ", ?)", $this->userid, $mailbox, (int) $message->uid, $flags, $msg); + + // race-condition, insert failed so try update (#1489146) + // thanks to ignore_key_errors "duplicate row" errors will be ignored + if ($force && !$res && !$this->db->is_error($res)) { + $this->db->query( + "UPDATE ".$this->db->table_name('cache_messages') + ." SET expires = " . ($this->ttl ? $this->db->now($this->ttl) : 'NULL') + .", flags = ?, data = ?" + ." WHERE user_id = ?" + ." AND mailbox = ?" + ." AND uid = ?", + $flags, $msg, $this->userid, $mailbox, (int) $message->uid); + } + + $this->db->set_option('ignore_key_errors', false); } @@ -481,11 +509,11 @@ $this->db->query( "UPDATE ".$this->db->table_name('cache_messages') - ." SET changed = ".$this->db->now() + ." SET expires = ". ($this->ttl ? $this->db->now($this->ttl) : 'NULL') .", flags = flags ".($enabled ? "+ $idx" : "- $idx") ." WHERE user_id = ?" ." AND mailbox = ?" - .($uids !== null ? " AND uid IN (".$this->db->array2list($uids, 'integer').")" : "") + .(!empty($uids) ? " AND uid IN (".$this->db->array2list($uids, 'integer').")" : "") ." AND (flags & $idx) ".($enabled ? "= 0" : "= $idx"), $this->userid, $mailbox); } @@ -604,23 +632,21 @@ /** - * Delete cache entries older than TTL - * - * @param string $ttl Lifetime of message cache entries + * Delete expired cache entries */ - function expunge($ttl) + static function gc() { - // get expiration timestamp - $ts = get_offset_time($ttl, -1); + $rcube = rcube::get_instance(); + $db = $rcube->get_dbh(); - $this->db->query("DELETE FROM ".$this->db->table_name('cache_messages') - ." WHERE changed < " . $this->db->fromunixtime($ts)); + $db->query("DELETE FROM ".$db->table_name('cache_messages') + ." WHERE expires < " . $db->now()); - $this->db->query("DELETE FROM ".$this->db->table_name('cache_index') - ." WHERE changed < " . $this->db->fromunixtime($ts)); + $db->query("DELETE FROM ".$db->table_name('cache_index') + ." WHERE expires < " . $db->now()); - $this->db->query("DELETE FROM ".$this->db->table_name('cache_thread') - ." WHERE changed < " . $this->db->fromunixtime($ts)); + $db->query("DELETE FROM ".$db->table_name('cache_thread') + ." WHERE expires < " . $db->now()); } @@ -639,7 +665,7 @@ if ($sql_arr = $this->db->fetch_assoc($sql_result)) { $data = explode('@', $sql_arr['data']); - $index = @unserialize($data[0]); + $index = $this->db->decode($data[0], true); unset($data[0]); if (empty($index)) { @@ -676,7 +702,7 @@ if ($sql_arr = $this->db->fetch_assoc($sql_result)) {
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_imap_generic.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_imap_generic.php
Changed
@@ -72,6 +72,8 @@ const COMMAND_CAPABILITY = 2; const COMMAND_LASTLINE = 4; + const DEBUG_LINE_LENGTH = 4098; // 4KB + 2B for \r\n + /** * Object constructor */ @@ -713,6 +715,10 @@ $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 @@ -746,7 +752,7 @@ } if ($this->prefs['timeout'] <= 0) { - $this->prefs['timeout'] = ini_get('default_socket_timeout'); + $this->prefs['timeout'] = max(0, intval(ini_get('default_socket_timeout'))); } // Connect @@ -794,23 +800,21 @@ // TLS connection if ($this->prefs['ssl_mode'] == 'tls' && $this->getCapability('STARTTLS')) { - if (version_compare(PHP_VERSION, '5.1.0', '>=')) { - $res = $this->execute('STARTTLS'); + $res = $this->execute('STARTTLS'); - if ($res[0] != self::ERROR_OK) { - $this->closeConnection(); - return false; - } - - 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 @@ -906,7 +910,7 @@ */ function closeConnection() { - if ($this->putLine($this->nextTag() . ' LOGOUT')) { + if ($this->logged && $this->putLine($this->nextTag() . ' LOGOUT')) { $this->readReply(); } @@ -1065,8 +1069,8 @@ /** * Executes EXPUNGE command * - * @param string $mailbox Mailbox name - * @param string $messages Message UIDs to expunge + * @param string $mailbox Mailbox name + * @param string|array $messages Message UIDs to expunge * * @return boolean True on success, False on error */ @@ -1077,17 +1081,20 @@ } if (!$this->data['READ-WRITE']) { - $this->setError(self::ERROR_READONLY, "Mailbox is read-only", 'EXPUNGE'); + $this->setError(self::ERROR_READONLY, "Mailbox is read-only"); return false; } // Clear internal status cache unset($this->data['STATUS:'.$mailbox]); - if ($messages) - $result = $this->execute('UID EXPUNGE', array($messages), self::COMMAND_NORESPONSE); - else + if (!empty($messages) && $messages != '*' && $this->hasCapability('UIDPLUS')) { + $messages = self::compressMessageSet($messages); + $result = $this->execute('UID EXPUNGE', array($messages), self::COMMAND_NORESPONSE); + } + else { $result = $this->execute('EXPUNGE', null, self::COMMAND_NORESPONSE); + } if ($result == self::ERROR_OK) { $this->selected = null; // state has changed, need to reselect @@ -1324,9 +1331,8 @@ $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; @@ -1558,11 +1564,12 @@ } // message IDs - if (!empty($add)) + if (!empty($add)) { $add = $this->compressMessageSet($add); + } list($code, $response) = $this->execute($return_uid ? 'UID SORT' : 'SORT', - array("($field)", $encoding, 'ALL' . (!empty($add) ? ' '.$add : ''))); + array("($field)", $encoding, !empty($add) ? $add : 'ALL')); if ($code != self::ERROR_OK) { $response = null; @@ -1649,7 +1656,6 @@ } if (!empty($criteria)) { - $modseq = stripos($criteria, 'MODSEQ') !== false; $params .= ($params ? ' ' : '') . $criteria; } else { @@ -1788,7 +1794,6 @@ if ($skip_deleted && preg_match('/FLAGS \(([^)]+)\)/', $line, $matches)) { $flags = explode(' ', strtoupper($matches[1])); if (in_array('\\DELETED', $flags)) { - $deleted[$id] = $id; continue; } } @@ -1933,7 +1938,7 @@ } if (!$this->data['READ-WRITE']) { - $this->setError(self::ERROR_READONLY, "Mailbox is read-only", 'STORE'); + $this->setError(self::ERROR_READONLY, "Mailbox is read-only"); return false; } @@ -1980,7 +1985,6 @@ /** * Moves message(s) from one folder to another. - * Original message(s) will be marked as deleted. * * @param string|array $messages Message UID(s) * @param string $from Mailbox name @@ -1995,19 +1999,45 @@ } if (!$this->data['READ-WRITE']) { - $this->setError(self::ERROR_READONLY, "Mailbox is read-only", 'STORE'); + $this->setError(self::ERROR_READONLY, "Mailbox is read-only"); return false; } - $r = $this->copy($messages, $from, $to); + // use MOVE command (RFC 6851) + if ($this->hasCapability('MOVE')) { + // Clear last COPYUID data + unset($this->data['COPYUID']); - if ($r) { // Clear internal status cache + unset($this->data['STATUS:'.$to]); unset($this->data['STATUS:'.$from]); - return $this->flag($from, $messages, 'DELETED'); + $result = $this->execute('UID MOVE', array( + $this->compressMessageSet($messages), $this->escape($to)), + self::COMMAND_NORESPONSE); + + return ($result == self::ERROR_OK); + } + + // use COPY + STORE +FLAGS.SILENT \Deleted + EXPUNGE + $result = $this->copy($messages, $from, $to);
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_ldap.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_ldap.php
Changed
@@ -3,8 +3,8 @@ /* +-----------------------------------------------------------------------+ | This file is part of the Roundcube Webmail client | - | Copyright (C) 2006-2012, The Roundcube Dev Team | - | Copyright (C) 2011-2012, Kolab Systems AG | + | Copyright (C) 2006-2013, The Roundcube Dev Team | + | Copyright (C) 2011-2013, Kolab Systems AG | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -27,38 +27,50 @@ */ class rcube_ldap extends rcube_addressbook { - /** public properties */ + // public properties public $primary_key = 'ID'; - public $groups = false; - public $readonly = true; - public $ready = false; - public $group_id = 0; - public $coltypes = array(); - - /** private properties */ - protected $conn; - protected $prop = array(); + public $groups = false; + public $readonly = true; + public $ready = false; + public $group_id = 0; + public $coltypes = array(); + + // private properties + protected $ldap; + protected $prop = array(); protected $fieldmap = array(); + protected $filter = ''; protected $sub_filter; - protected $filter = ''; - protected $result = null; - protected $ldap_result = null; + protected $result; + protected $ldap_result; protected $mail_domain = ''; protected $debug = false; - private $base_dn = ''; + /** + * 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 = null; + private $group_url; private $cache; - private $vlv_active = false; - private $vlv_count = 0; - /** * Object constructor * - * @param array $p LDAP connection properties + * @param array $p LDAP connection properties * @param boolean $debug Enables debug mode * @param string $mail_domain Current user mail domain name */ @@ -66,8 +78,7 @@ { $this->prop = $p; - if (isset($p['searchonly'])) - $this->searchonly = $p['searchonly']; + $fetch_attributes = array('objectClass'); // check if groups are configured if (is_array($p['groups']) && count($p['groups'])) { @@ -82,6 +93,21 @@ $this->prop['groups']['name_attr'] = 'cn'; if (empty($this->prop['groups']['scope'])) $this->prop['groups']['scope'] = 'sub'; + + // add group name attrib to the list of attributes to be fetched + $fetch_attributes[] = $this->prop['groups']['name_attr']; + } + if (is_array($p['group_filters']) && count($p['group_filters'])) { + $this->groups = true; + + foreach ($p['group_filters'] as $k => $group_filter) { + // set default name attribute to cn + if (empty($group_filter['name_attr']) && empty($this->prop['groups']['name_attr'])) + $this->prop['group_filters'][$k]['name_attr'] = $group_filter['name_attr'] = 'cn'; + + if ($group_filter['name_attr']) + $fetch_attributes[] = $group_filter['name_attr']; + } } // fieldmap property is given @@ -169,7 +195,7 @@ // Build sub_fields filter if (!empty($this->prop['sub_fields']) && is_array($this->prop['sub_fields'])) { $this->sub_filter = ''; - foreach ($this->prop['sub_fields'] as $attr => $class) { + foreach ($this->prop['sub_fields'] as $class) { if (!empty($class)) { $class = is_array($class) ? array_pop($class) : $class; $this->sub_filter .= '(objectClass=' . $class . ')'; @@ -186,7 +212,24 @@ // initialize cache $rcube = rcube::get_instance(); - $this->cache = $rcube->get_cache('LDAP.' . asciiwords($this->prop['name']), 'db', 600); + if ($cache_type = $rcube->config->get('ldap_cache', 'db')) { + $cache_ttl = $rcube->config->get('ldap_cache_ttl', '10m'); + $cache_name = 'LDAP.' . asciiwords($this->prop['name']); + + $this->cache = $rcube->get_cache($cache_name, $cache_type, $cache_ttl); + } + + // determine which attributes to fetch + $this->prop['list_attributes'] = array_unique($fetch_attributes); + $this->prop['attributes'] = array_merge(array_values($this->fieldmap), $fetch_attributes); + foreach ($rcube->config->get('contactlist_fields') as $col) { + $this->prop['list_attributes'] = array_merge($this->prop['list_attributes'], $this->_map_field($col)); + } + + // initialize ldap wrapper object + $this->ldap = new rcube_ldap_generic($this->prop); + $this->ldap->set_cache($this->cache); + $this->ldap->set_debug($this->debug); $this->_connect(); } @@ -199,147 +242,149 @@ { $rcube = rcube::get_instance(); - if (!function_exists('ldap_connect')) - rcube::raise_error(array('code' => 100, 'type' => 'ldap', - 'file' => __FILE__, 'line' => __LINE__, - 'message' => "No ldap support in this installation of PHP"), - true, true); - - if (is_resource($this->conn)) + if ($this->ready) return true; if (!is_array($this->prop['hosts'])) $this->prop['hosts'] = array($this->prop['hosts']); - if (empty($this->prop['ldap_version'])) - $this->prop['ldap_version'] = 3; + // try to connect + bind for every host configured + // with OpenLDAP 2.x ldap_connect() always succeeds but ldap_bind will fail if host isn't reachable + // see http://www.php.net/manual/en/function.ldap-connect.php + foreach ($this->prop['hosts'] as $host) { + // skip host if connection failed + if (!$this->ldap->connect($host)) { + continue; + } - foreach ($this->prop['hosts'] as $host) - { - $host = rcube_utils::idn_to_ascii(rcube_utils::parse_host($host)); - $hostname = $host.($this->prop['port'] ? ':'.$this->prop['port'] : ''); + // See if the directory is writeable. + if ($this->prop['writable']) { + $this->readonly = false; + } - $this->_debug("C: Connect [$hostname] [{$this->prop['name']}]"); + $bind_pass = $this->prop['bind_pass']; + $bind_user = $this->prop['bind_user']; + $bind_dn = $this->prop['bind_dn']; - if ($lc = @ldap_connect($host, $this->prop['port'])) - { - if ($this->prop['use_tls'] === true) - if (!ldap_start_tls($lc)) - continue; + $this->base_dn = $this->prop['base_dn']; + $this->groups_base_dn = ($this->prop['groups']['base_dn']) ? + $this->prop['groups']['base_dn'] : $this->base_dn; - $this->_debug("S: OK");
View file
kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_ldap_generic.php
Added
@@ -0,0 +1,1049 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | Roundcube/rcube_ldap_generic.php | + | | + | This file is part of the Roundcube Webmail client | + | Copyright (C) 2006-2013, The Roundcube Dev Team | + | Copyright (C) 2012-2013, Kolab Systems AG | + | | + | 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. | + | | + | PURPOSE: | + | Provide basic functionality for accessing LDAP directories | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + | Aleksander Machniak <machniak@kolabsys.com> | + +-----------------------------------------------------------------------+ +*/ + + +/* + LDAP connection properties + -------------------------- + + $prop = array( + 'host' => '<ldap-server-address>', + // or + 'hosts' => array('directory.verisign.com'), + 'port' => 389, + 'use_tls' => true|false, + 'ldap_version' => 3, // using LDAPv3 + 'auth_method' => '', // SASL authentication method (for proxy auth), e.g. DIGEST-MD5 + 'attributes' => array('dn'), // List of attributes to read from the server + 'vlv' => false, // Enable Virtual List View to more efficiently fetch paginated data (if server supports it) + 'config_root_dn' => 'cn=config', // Root DN to read config (e.g. vlv indexes) from + 'numsub_filter' => '(objectClass=organizationalUnit)', // with VLV, we also use numSubOrdinates to query the total number of records. Set this filter to get all numSubOrdinates attributes for counting + 'sizelimit' => '0', // Enables you to limit the count of entries fetched. Setting this to 0 means no limit. + 'timelimit' => '0', // Sets the number of seconds how long is spend on the search. Setting this to 0 means no limit. + 'network_timeout' => 10, // The timeout (in seconds) for connect + bind arrempts. This is only supported in PHP >= 5.3.0 with OpenLDAP 2.x + 'referrals' => true|false, // Sets the LDAP_OPT_REFERRALS option. Mostly used in multi-domain Active Directory setups + ); +*/ + +/** + * Model class to access an LDAP directories + * + * @package Framework + * @subpackage LDAP + */ +class rcube_ldap_generic +{ + const UPDATE_MOD_ADD = 1; + const UPDATE_MOD_DELETE = 2; + const UPDATE_MOD_REPLACE = 4; + const UPDATE_MOD_FULL = 7; + + public $conn; + public $vlv_active = false; + + /** private properties */ + protected $cache = null; + protected $config = array(); + protected $attributes = array('dn'); + protected $entries = null; + protected $result = null; + protected $debug = false; + protected $list_page = 1; + protected $page_size = 10; + protected $vlv_config = null; + + + /** + * Object constructor + * + * @param array $p LDAP connection properties + */ + function __construct($p) + { + $this->config = $p; + + if (is_array($p['attributes'])) + $this->attributes = $p['attributes']; + + if (!is_array($p['hosts']) && !empty($p['host'])) + $this->config['hosts'] = array($p['host']); + } + + /** + * Activate/deactivate debug mode + * + * @param boolean $dbg True if LDAP commands should be logged + */ + public function set_debug($dbg = true) + { + $this->debug = $dbg; + } + + /** + * Set connection options + * + * @param mixed $opt Option name as string or hash array with multiple options + * @param mixed $val Option value + */ + public function set_config($opt, $val = null) + { + if (is_array($opt)) + $this->config = array_merge($this->config, $opt); + else + $this->config[$opt] = $value; + } + + /** + * Enable caching by passing an instance of rcube_cache to be used by this object + * + * @param object rcube_cache Instance or False to disable caching + */ + public function set_cache($cache_engine) + { + $this->cache = $cache_engine; + } + + /** + * Set properties for VLV-based paging + * + * @param number $page Page number to list (starting at 1) + * @param number $size Number of entries to display on one page + */ + public function set_vlv_page($page, $size = 10) + { + $this->list_page = $page; + $this->page_size = $size; + } + + /** + * Establish a connection to the LDAP server + */ + public function connect($host = null) + { + if (!function_exists('ldap_connect')) { + rcube::raise_error(array('code' => 100, 'type' => 'ldap', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "No ldap support in this installation of PHP"), + true); + return false; + } + + if (is_resource($this->conn) && $this->config['host'] == $host) + return true; + + if (empty($this->config['ldap_version'])) + $this->config['ldap_version'] = 3; + + // iterate over hosts if none specified + if (!$host) { + if (!is_array($this->config['hosts'])) + $this->config['hosts'] = array($this->config['hosts']); + + foreach ($this->config['hosts'] as $host) { + if ($this->connect($host)) { + return true; + } + } + + return false; + } + + // open connection to the given $host + $host = rcube_utils::idn_to_ascii(rcube_utils::parse_host($host)); + $hostname = $host . ($this->config['port'] ? ':'.$this->config['port'] : ''); + + $this->_debug("C: Connect to $hostname [{$this->config['name']}]"); + + if ($lc = @ldap_connect($host, $this->config['port'])) { + if ($this->config['use_tls'] === true) + if (!ldap_start_tls($lc)) + continue; + + $this->_debug("S: OK"); + + ldap_set_option($lc, LDAP_OPT_PROTOCOL_VERSION, $this->config['ldap_version']); + $this->config['host'] = $host; + $this->conn = $lc; + + if (!empty($this->config['network_timeout'])) + ldap_set_option($lc, LDAP_OPT_NETWORK_TIMEOUT, $this->config['network_timeout']); + + if (isset($this->config['referrals'])) + ldap_set_option($lc, LDAP_OPT_REFERRALS, $this->config['referrals']); + } + else { + $this->_debug("S: NOT OK"); + } + + if (!is_resource($this->conn)) { + rcube::raise_error(array('code' => 100, 'type' => 'ldap',
View file
kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_ldap_result.php
Added
@@ -0,0 +1,130 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | Roundcube/rcube_ldap_result.php | + | | + | This file is part of the Roundcube Webmail client | + | Copyright (C) 2006-2013, The Roundcube Dev Team | + | Copyright (C) 2013, Kolab Systems AG | + | | + | 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. | + | | + | PURPOSE: | + | Model class that represents an LDAP search result | + | | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ +*/ + + +/** + * Model class representing an LDAP search result + * + * @package Framework + * @subpackage LDAP + */ +class rcube_ldap_result implements Iterator +{ + public $conn; + public $ldap; + public $base_dn; + public $filter; + + private $count = null; + private $current = null; + private $iteratorkey = 0; + + /** + * Default constructor + * + * @param resource $conn LDAP link identifier + * @param resource $ldap LDAP result entry identifier + * @param string $base_dn Base DN used to get this result + * @param string $filter Filter query used to get this result + * @param integer $count Record count value (pre-calculated) + */ + function __construct($conn, $ldap, $base_dn, $filter, $count = null) + { + $this->conn = $conn; + $this->ldap = $ldap; + $this->base_dn = $base_dn; + $this->filter = $filter; + $this->count = $count; + } + + /** + * Wrapper for ldap_sort() + */ + public function sort($attr) + { + return ldap_sort($this->conn, $this->ldap, $attr); + } + + /** + * Get entries count + */ + public function count() + { + if (!isset($this->count)) + $this->count = ldap_count_entries($this->conn, $this->ldap); + + return $this->count; + } + + /** + * Wrapper for ldap_get_entries() + * + * @param boolean $normalize Optionally normalize the entries to a list of hash arrays + * @return array List of LDAP entries + */ + public function entries($normalize = false) + { + $entries = ldap_get_entries($this->conn, $this->ldap); + return $normalize ? rcube_ldap_generic::normalize_result($entries) : $entries; + } + + /** + * Wrapper for ldap_get_dn() using the current entry pointer + */ + public function get_dn() + { + return $this->current ? ldap_get_dn($this->conn, $this->current) : null; + } + + + /*** Implements the PHP 5 Iterator interface to make foreach work ***/ + + function current() + { + $attrib = ldap_get_attributes($this->conn, $this->current); + $attrib['dn'] = ldap_get_dn($this->conn, $this->current); + return $attrib; + } + + function key() + { + return $this->iteratorkey; + } + + function rewind() + { + $this->iteratorkey = 0; + $this->current = ldap_first_entry($this->conn, $this->ldap); + } + + function next() + { + $this->iteratorkey++; + $this->current = ldap_next_entry($this->conn, $this->current); + } + + function valid() + { + return (bool)$this->current; + } + +}
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_message.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_message.php
Changed
@@ -85,15 +85,16 @@ $this->headers = $this->storage->get_message($uid); - if (!$this->headers) + if (!$this->headers) { return; + } $this->mime = new rcube_mime($this->headers->charset); - $this->subject = $this->mime->decode_mime_string($this->headers->subject); + $this->subject = $this->headers->get('subject'); list(, $this->sender) = each($this->mime->decode_address_list($this->headers->from, 1)); - $this->set_safe((intval($_GET['_safe']) || $_SESSION['safe_messages'][$uid])); + $this->set_safe((intval($_GET['_safe']) || $_SESSION['safe_messages'][$this->folder.':'.$uid])); $this->opt = array( 'safe' => $this->is_safe, 'prefer_html' => $this->app->config->get('prefer_html'), @@ -125,15 +126,11 @@ */ public function get_header($name, $raw = false) { - if (empty($this->headers)) + if (empty($this->headers)) { return null; + } - if ($this->headers->$name) - $value = $this->headers->$name; - else if ($this->headers->others[$name]) - $value = $this->headers->others[$name]; - - return $raw ? $value : $this->mime->decode_header($value); + return $this->headers->get($name, !$raw); } @@ -144,8 +141,7 @@ */ public function set_safe($safe = true) { - $this->is_safe = $safe; - $_SESSION['safe_messages'][$this->uid] = $this->is_safe; + $_SESSION['safe_messages'][$this->folder.':'.$this->uid] = $this->is_safe = $safe; } @@ -153,12 +149,13 @@ * Compose a valid URL for getting a message part * * @param string $mime_id Part MIME-ID + * @param mixed $embed Mimetype class for parts to be embedded * @return string URL or false if part does not exist */ public function get_part_url($mime_id, $embed = false) { if ($this->mime_parts[$mime_id]) - return $this->opt['get_url'] . '&_part=' . $mime_id . ($embed ? '&_embed=1' : ''); + return $this->opt['get_url'] . '&_part=' . $mime_id . ($embed ? '&_embed=1&_mimeclass=' . $embed : ''); else return false; } @@ -171,10 +168,11 @@ * @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) @@ -188,45 +186,89 @@ // 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); } } /** - * Determine if the message contains a HTML part + * 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 $recursive Enables checking in all levels of the structure - * @param bool $enriched Enables checking for text/enriched parts too + * @param bool $enriched Enables checking for text/enriched parts too * * @return bool True if a HTML is available, False if not */ - function has_html_part($recursive = true, $enriched = false) + function has_html_part($enriched = false) { // check all message parts - foreach ($this->parts as $part) { + foreach ($this->mime_parts as $part) { if ($part->mimetype == 'text/html' || ($enriched && $part->mimetype == 'text/enriched')) { - // Level check, we'll skip e.g. HTML attachments - if (!$recursive) { - $level = explode('.', $part->mime_id); + // Skip if part is an attachment, don't use is_attachment() here + if ($part->filename) { + continue; + } - // Skip if level too deep or part has a file name - if (count($level) > 2 || $part->filename) { - continue; + $level = explode('.', $part->mime_id); + + // Check if the part belongs to higher-level's alternative/related + while (array_pop($level) !== null) { + if (!count($level)) { + return true; } - // HTML part can be on the lower level, if not... - if (count($level) > 1) { - array_pop($level); - $parent = $this->mime_parts[join('.', $level)]; - // ... parent isn't multipart/alternative or related - if ($parent->mimetype != 'multipart/alternative' && $parent->mimetype != 'multipart/related') { - continue; - } + $parent = $this->mime_parts[join('.', $level)]; + if ($parent->mimetype != 'multipart/alternative' && $parent->mimetype != 'multipart/related') { + continue 2; } } - return true; + if ($part->size) { + return true; + } + } + } + + return false; + } + + + /** + * Determine if the message contains a text/plain part. This must to be + * a real part not an attachment (or its part) + * + * @return bool True if a plain text part is available, False if not + */ + function has_text_part() + { + // check all message parts + foreach ($this->mime_parts as $part) { + if ($part->mimetype == 'text/plain') { + // Skip if part is an attachment, don't use is_attachment() here + if ($part->filename) { + continue; + } + + $level = explode('.', $part->mime_id); + + // Check if the part belongs to higher-level's alternative/related + 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') { + continue 2; + } + } + + if ($part->size) { + return true; + } } } @@ -321,8 +363,8 @@ $mimetype = $structure->real_mimetype; // parse headers from message/rfc822 part - if (!isset($structure->headers['subject'])) { - list($headers, $dump) = explode("\r\n\r\n", $this->get_part_content($structure->mime_id, null, true, 8192)); + if (!isset($structure->headers['subject']) && !isset($structure->headers['from'])) { + list($headers, ) = explode("\r\n\r\n", $this->get_part_content($structure->mime_id, null, true, 32768)); $structure->headers = rcube_mime::parse_headers($headers); } } @@ -330,7 +372,8 @@
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_message_header.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_message_header.php
Changed
@@ -215,7 +215,12 @@ $value = $this->others[$name]; } - return $decode ? rcube_mime::decode_header($value, $this->charset) : $value; + if ($decode) { + $value = rcube_mime::decode_header($value, $this->charset); + $value = rcube_charset::clean($value); + } + + return $value; } /**
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_mime.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_mime.php
Changed
@@ -127,10 +127,11 @@ * @param int $max List only this number of addresses * @param boolean $decode Decode address strings * @param string $fallback Fallback charset if none specified + * @param boolean $addronly Return flat array with e-mail addresses only * - * @return array Indexed list of addresses + * @return array Indexed list of addresses */ - static function decode_address_list($input, $max = null, $decode = true, $fallback = null) + static function decode_address_list($input, $max = null, $decode = true, $fallback = null, $addronly = false) { $a = self::parse_address_list($input, $decode, $fallback); $out = array(); @@ -145,20 +146,21 @@ foreach ($a as $val) { $j++; $address = trim($val['address']); - $name = trim($val['name']); - if ($name && $address && $name != $address) - $string = sprintf('%s <%s>', preg_match("/$special_chars/", $name) ? '"'.addcslashes($name, '"').'"' : $name, $address); - else if ($address) - $string = $address; - else if ($name) - $string = $name; - - $out[$j] = array( - 'name' => $name, - 'mailto' => $address, - 'string' => $string - ); + if ($addronly) { + $out[$j] = $address; + } + else { + $name = trim($val['name']); + if ($name && $address && $name != $address) + $string = sprintf('%s <%s>', preg_match("/$special_chars/", $name) ? '"'.addcslashes($name, '"').'"' : $name, $address); + else if ($address) + $string = $address; + else if ($name) + $string = $name; + + $out[$j] = array('name' => $name, 'mailto' => $address, 'string' => $string); + } if ($max && $j==$max) break; @@ -359,6 +361,11 @@ $address = $m[1]; $name = ''; } + // special case (#1489092) + else if (preg_match('/(\s*<MAILER-DAEMON>)$/', $val, $m)) { + $address = 'MAILER-DAEMON'; + $name = substr($val, 0, -strlen($m[1])); + } else { $name = $val; } @@ -476,13 +483,20 @@ $q_level = 0; foreach ($text as $idx => $line) { - if ($line[0] == '>' && preg_match('/^(>+\s*)/', $line, $regs)) { - $q = strlen(str_replace(' ', '', $regs[0])); - $line = substr($line, strlen($regs[0])); - - if ($q == $q_level && $line - && isset($text[$last]) - && $text[$last][strlen($text[$last])-1] == ' ' + if (preg_match('/^(>+)/', $line, $m)) { + // remove quote chars + $q = strlen($m[1]); + $line = preg_replace('/^>+/', '', $line); + // remove (optional) space-staffing + $line = preg_replace('/^ /', '', $line); + + // The same paragraph (We join current line with the previous one) when: + // - the same level of quoting + // - previous line was flowed + // - previous line contains more than only one single space (and quote char(s)) + if ($q == $q_level + && isset($text[$last]) && $text[$last][strlen($text[$last])-1] == ' ' + && !preg_match('/^>+ {0,1}$/', $text[$last]) ) { $text[$last] .= $line; unset($text[$idx]); @@ -535,10 +549,13 @@ foreach ($text as $idx => $line) { if ($line != '-- ') { - if ($line[0] == '>' && preg_match('/^(>+ {0,1})+/', $line, $regs)) { - $level = substr_count($regs[0], '>'); + if (preg_match('/^(>+)/', $line, $m)) { + // remove quote chars + $level = strlen($m[1]); + $line = preg_replace('/^>+/', '', $line); + // remove (optional) space-staffing and spaces before the line end + $line = preg_replace('/(^ | +$)/', '', $line); $prefix = str_repeat('>', $level) . ' '; - $line = rtrim(substr($line, strlen($regs[0]))); $line = $prefix . self::wordwrap($line, $length - $level - 2, " \r\n$prefix", false, $charset); } else if ($line) { @@ -556,81 +573,122 @@ /** - * Improved wordwrap function. + * Improved wordwrap function with multibyte support. + * The code is based on Zend_Text_MultiByte::wordWrap(). * - * @param string $string Text to wrap - * @param int $width Line width - * @param string $break Line separator - * @param bool $cut Enable to cut word - * @param string $charset Charset of $string + * @param string $string Text to wrap + * @param int $width Line width + * @param string $break Line separator + * @param bool $cut Enable to cut word + * @param string $charset Charset of $string + * @param bool $wrap_quoted When enabled quoted lines will not be wrapped * * @return string Text */ - public static function wordwrap($string, $width=75, $break="\n", $cut=false, $charset=null) + public static function wordwrap($string, $width=75, $break="\n", $cut=false, $charset=null, $wrap_quoted=true) { - if ($charset && function_exists('mb_internal_encoding')) { + // Note: Never try to use iconv instead of mbstring functions here + // Iconv's substr/strlen are 100x slower (#1489113) + + if ($charset && $charset != RCUBE_CHARSET && function_exists('mb_internal_encoding')) { mb_internal_encoding($charset); } - $para = preg_split('/\r?\n/', $string); - $string = ''; + // Convert \r\n to \n, this is our line-separator + $string = str_replace("\r\n", "\n", $string); + $separator = "\n"; // must be 1 character length + $result = array(); - while (count($para)) { - $line = array_shift($para); - if ($line[0] == '>') { - $string .= $line.$break; - continue; - } + while (($stringLength = mb_strlen($string)) > 0) { + $breakPos = mb_strpos($string, $separator, 0); - $list = explode(' ', $line); - $len = 0; - while (count($list)) { - $line = array_shift($list); - $l = mb_strlen($line); - $newlen = $len + $l + ($len ? 1 : 0); + // quoted line (do not wrap) + if ($wrap_quoted && $string[0] == '>') { + if ($breakPos === $stringLength - 1 || $breakPos === false) { + $subString = $string; + $cutLength = null; + } + else { + $subString = mb_substr($string, 0, $breakPos); + $cutLength = $breakPos + 1; + } + } + // next line found and current line is shorter than the limit + else if ($breakPos !== false && $breakPos < $width) { + if ($breakPos === $stringLength - 1) { + $subString = $string; + $cutLength = null; + } + else { + $subString = mb_substr($string, 0, $breakPos); + $cutLength = $breakPos + 1; + } + } + else { + $subString = mb_substr($string, 0, $width); - if ($newlen <= $width) { - $string .= ($len ? ' ' : '').$line; - $len += (1 + $l); + // last line + if ($breakPos === false && $subString === $string) { + $cutLength = null; } else { - if ($l > $width) { - if ($cut) { - $start = 0; - while ($l) { - $str = mb_substr($line, $start, $width); - $strlen = mb_strlen($str);
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_output.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_output.php
Changed
@@ -162,7 +162,7 @@ header("Cache-Control: private, must-revalidate"); } else { - header("Cache-Control: private, no-cache, must-revalidate, post-check=0, pre-check=0"); + header("Cache-Control: private, no-cache, no-store, must-revalidate, post-check=0, pre-check=0"); header("Pragma: no-cache"); } }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_plugin.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_plugin.php
Changed
@@ -60,6 +60,14 @@ */ public $noframe = false; + /** + * A list of config option names that can be modified + * by the user via user interface (with save-prefs command) + * + * @var array + */ + public $allowed_prefs; + protected $home; protected $urlbase; private $mytask; @@ -84,6 +92,16 @@ abstract function init(); /** + * Provide information about this + * + * @return array Meta information about a plugin or false if not implemented + */ + public static function info() + { + return false; + } + + /** * Attempt to load the given plugin which is required for the current plugin * * @param string Plugin name @@ -209,7 +227,7 @@ $rcube->load_language($lang, $add); // add labels to client - if ($add2client) { + if ($add2client && method_exists($rcube->output, 'add_label')) { if (is_array($add2client)) { $js_labels = array_map(array($this, 'label_map_callback'), $add2client); } @@ -222,6 +240,24 @@ } /** + * Wrapper for add_label() adding the plugin ID as domain + */ + public function add_label() + { + $rcube = rcube::get_instance(); + + if (method_exists($rcube->output, 'add_label')) { + $args = func_get_args(); + if (count($args) == 1 && is_array($args[0])) { + $args = $args[0]; + } + + $args = array_map(array($this, 'label_map_callback'), $args); + $rcube->output->add_label($args); + } + } + + /** * Wrapper for rcube::gettext() adding the plugin ID as domain * * @param string $p Message identifier @@ -237,7 +273,7 @@ /** * Register this plugin to be responsible for a specific task * - * @param string $task Task name (only characters [a-z0-9_.-] are allowed) + * @param string $task Task name (only characters [a-z0-9_-] are allowed) */ public function register_task($task) { @@ -372,6 +408,10 @@ */ private function label_map_callback($key) { + if (strpos($key, $this->ID.'.') === 0) { + return $key; + } + return $this->ID.'.'.$key; } }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_plugin_api.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_plugin_api.php
Changed
@@ -36,6 +36,7 @@ public $task = ''; public $output; public $handlers = array(); + public $allowed_prefs = array(); protected $plugins = array(); protected $tasks = array(); @@ -202,6 +203,11 @@ $plugin->init(); $this->plugins[$plugin_name] = $plugin; } + + if (!empty($plugin->allowed_prefs)) { + $this->allowed_prefs = array_merge($this->allowed_prefs, $plugin->allowed_prefs); + } + return true; } } @@ -222,6 +228,119 @@ } /** + * Get information about a specific plugin. + * This is either provided my a plugin's info() method or extracted from a package.xml or a composer.json file + * + * @param string Plugin name + * @return array Meta information about a plugin or False if plugin was not found + */ + public function get_info($plugin_name) + { + static $composer_lock, $license_uris = array( + 'Apache' => 'http://www.apache.org/licenses/LICENSE-2.0.html', + 'Apache-2' => 'http://www.apache.org/licenses/LICENSE-2.0.html', + 'Apache-1' => 'http://www.apache.org/licenses/LICENSE-1.0', + 'Apache-1.1' => 'http://www.apache.org/licenses/LICENSE-1.1', + 'GPL' => 'http://www.gnu.org/licenses/gpl.html', + 'GPLv2' => 'http://www.gnu.org/licenses/gpl-2.0.html', + 'GPL-2.0' => 'http://www.gnu.org/licenses/gpl-2.0.html', + 'GPLv3' => 'http://www.gnu.org/licenses/gpl-3.0.html', + 'GPL-3.0' => 'http://www.gnu.org/licenses/gpl-3.0.html', + 'GPL-3.0+' => 'http://www.gnu.org/licenses/gpl.html', + 'GPL-2.0+' => 'http://www.gnu.org/licenses/gpl.html', + 'LGPL' => 'http://www.gnu.org/licenses/lgpl.html', + 'LGPLv2' => 'http://www.gnu.org/licenses/lgpl-2.0.html', + 'LGPLv2.1' => 'http://www.gnu.org/licenses/lgpl-2.1.html', + 'LGPLv3' => 'http://www.gnu.org/licenses/lgpl.html', + 'LGPL-2.0' => 'http://www.gnu.org/licenses/lgpl-2.0.html', + 'LGPL-2.1' => 'http://www.gnu.org/licenses/lgpl-2.1.html', + 'LGPL-3.0' => 'http://www.gnu.org/licenses/lgpl.html', + 'LGPL-3.0+' => 'http://www.gnu.org/licenses/lgpl.html', + 'BSD' => 'http://opensource.org/licenses/bsd-license.html', + 'BSD-2-Clause' => 'http://opensource.org/licenses/BSD-2-Clause', + 'BSD-3-Clause' => 'http://opensource.org/licenses/BSD-3-Clause', + 'FreeBSD' => 'http://opensource.org/licenses/BSD-2-Clause', + 'MIT' => 'http://www.opensource.org/licenses/mit-license.php', + 'PHP' => 'http://opensource.org/licenses/PHP-3.0', + 'PHP-3' => 'http://www.php.net/license/3_01.txt', + 'PHP-3.0' => 'http://www.php.net/license/3_0.txt', + 'PHP-3.01' => 'http://www.php.net/license/3_01.txt', + ); + + $dir = dir($this->dir); + $fn = unslashify($dir->path) . DIRECTORY_SEPARATOR . $plugin_name . DIRECTORY_SEPARATOR . $plugin_name . '.php'; + $info = false; + + if (!class_exists($plugin_name)) + include($fn); + + if (class_exists($plugin_name)) + $info = $plugin_name::info(); + + // fall back to composer.json file + if (!$info) { + $composer = INSTALL_PATH . "/plugins/$plugin_name/composer.json"; + if (file_exists($composer) && ($json = @json_decode(file_get_contents($composer), true))) { + list($info['vendor'], $info['name']) = explode('/', $json['name']); + $info['license'] = $json['license']; + if ($license_uri = $license_uris[$info['license']]) + $info['license_uri'] = $license_uri; + } + + // read local composer.lock file (once) + if (!isset($composer_lock)) { + $composer_lock = @json_decode(@file_get_contents(INSTALL_PATH . "/composer.lock"), true); + if ($composer_lock['packages']) { + foreach ($composer_lock['packages'] as $i => $package) { + $composer_lock['installed'][$package['name']] = $package; + } + } + } + + // load additional information from local composer.lock file + if ($lock = $composer_lock['installed'][$json['name']]) { + $info['version'] = $lock['version']; + $info['uri'] = $lock['homepage'] ? $lock['homepage'] : $lock['source']['uri']; + $info['src_uri'] = $lock['dist']['uri'] ? $lock['dist']['uri'] : $lock['source']['uri']; + } + } + + // fall back to package.xml file + if (!$info) { + $package = INSTALL_PATH . "/plugins/$plugin_name/package.xml"; + if (file_exists($package) && ($file = file_get_contents($package))) { + $doc = new DOMDocument(); + $doc->loadXML($file); + $xpath = new DOMXPath($doc); + $xpath->registerNamespace('rc', "http://pear.php.net/dtd/package-2.0"); + + // XPaths of plugin metadata elements + $metadata = array( + 'name' => 'string(//rc:package/rc:name)', + 'version' => 'string(//rc:package/rc:version/rc:release)', + 'license' => 'string(//rc:package/rc:license)', + 'license_uri' => 'string(//rc:package/rc:license/@uri)', + 'src_uri' => 'string(//rc:package/rc:srcuri)', + 'uri' => 'string(//rc:package/rc:uri)', + ); + + foreach ($metadata as $key => $path) { + $info[$key] = $xpath->evaluate($path); + } + + // dependent required plugins (can be used, but not included in config) + $deps = $xpath->evaluate('//rc:package/rc:dependencies/rc:required/rc:package/rc:name'); + for ($i = 0; $i < $deps->length; $i++) { + $dn = $deps->item($i)->nodeValue; + $info['requires'][] = $dn; + } + } + } + + return $info; + } + + /** * Allows a plugin object to register a callback for a certain hook * * @param string $hook Hook name @@ -372,7 +491,7 @@ /** * Register this plugin to be responsible for a specific task * - * @param string $task Task name (only characters [a-z0-9_.-] are allowed) + * @param string $task Task name (only characters [a-z0-9_-] are allowed) * @param string $owner Plugin name that registers this action */ public function register_task($task, $owner) @@ -382,7 +501,7 @@ return true; } - if ($task != asciiwords($task)) { + if ($task != asciiwords($task, true)) { rcube::raise_error(array('code' => 526, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__, 'message' => "Invalid task name: $task."
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_result_set.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_result_set.php
Changed
@@ -3,7 +3,7 @@ /* +-----------------------------------------------------------------------+ | This file is part of the Roundcube Webmail client | - | Copyright (C) 2006-2011, 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. | @@ -17,20 +17,22 @@ */ /** - * Roundcube result set class. + * Roundcube result set class + * * Representing an address directory result set. + * Implenets Iterator and thus be used in foreach() loops. * * @package Framework * @subpackage Addressbook */ -class rcube_result_set +class rcube_result_set implements Iterator { - var $count = 0; - var $first = 0; - var $current = 0; - var $searchonly = false; - var $records = array(); + public $count = 0; + public $first = 0; + public $searchonly = false; + public $records = array(); + private $current = 0; function __construct($c=0, $f=0) { @@ -51,18 +53,39 @@ function first() { $this->current = 0; - return $this->records[$this->current++]; + return $this->records[$this->current]; + } + + function seek($i) + { + $this->current = $i; + } + + /*** PHP 5 Iterator interface ***/ + + function rewind() + { + $this->current = 0; + } + + function current() + { + return $this->records[$this->current]; + } + + function key() + { + return $this->current; } - // alias for iterate() function next() { return $this->iterate(); } - function seek($i) + function valid() { - $this->current = $i; + return isset($this->records[$this->current]); } }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_session.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_session.php
Changed
@@ -32,6 +32,8 @@ private $ip; private $start; private $changed; + private $time_diff = 0; + private $reloaded = false; private $unsets = array(); private $gc_handlers = array(); private $cookiename = 'roundcube_sessauth'; @@ -41,6 +43,7 @@ private $secret = ''; private $ip_check = false; private $logging = false; + private $storage; private $memcache; @@ -51,18 +54,21 @@ { $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; $this->set_lifetime($lifetime); // use memcache backend - if ($config->get('session_storage', 'db') == 'memcache') { + $this->storage = $config->get('session_storage', 'db'); + if ($this->storage == 'memcache') { $this->memcache = rcube::get_instance()->get_memcache(); // set custom functions for PHP session management if memcache is available if ($this->memcache) { + ini_set('session.serialize_handler', 'php'); + session_set_save_handler( array($this, 'open'), array($this, 'close'), @@ -78,7 +84,9 @@ true, true); } } - else { + else if ($this->storage != 'php') { + ini_set('session.serialize_handler', 'php'); + // set custom functions for PHP session management session_set_save_handler( array($this, 'open'), @@ -86,7 +94,23 @@ array($this, 'db_read'), array($this, 'db_write'), array($this, 'db_destroy'), - array($this, 'db_gc')); + array($this, 'gc')); + } + } + + + /** + * Wrapper for session_start() + */ + public function start() + { + session_start(); + + // copy some session properties to object vars + if ($this->storage == 'php') { + $this->key = session_id(); + $this->ip = $_SESSION['__IP']; + $this->changed = $_SESSION['__MTIME']; } } @@ -115,6 +139,25 @@ /** + * Wrapper for session_write_close() + */ + public function write_close() + { + if ($this->storage == 'php') { + $_SESSION['__IP'] = $this->ip; + $_SESSION['__MTIME'] = time(); + } + + session_write_close(); + + // write_close() is called on script shutdown, see rcube::shutdown() + // execute cleanup functionality if enabled by session gc handler + // we do this after closing the session for better performance + $this->gc_shutdown(); + } + + + /** * Read session data from database * * @param string Session ID @@ -124,14 +167,16 @@ public function db_read($key) { $sql_result = $this->db->query( - "SELECT vars, ip, changed FROM ".$this->db->table_name('session') - ." WHERE sess_id = ?", $key); + "SELECT vars, ip, changed, " . $this->db->now() . " AS ts" + . " FROM " . $this->db->table_name('session') + . " WHERE sess_id = ?", $key); if ($sql_result && ($sql_arr = $this->db->fetch_assoc($sql_result))) { - $this->changed = strtotime($sql_arr['changed']); - $this->ip = $sql_arr['ip']; - $this->vars = base64_decode($sql_arr['vars']); - $this->key = $key; + $this->time_diff = time() - strtotime($sql_arr['ts']); + $this->changed = strtotime($sql_arr['changed']); + $this->ip = $sql_arr['ip']; + $this->vars = base64_decode($sql_arr['vars']); + $this->key = $key; return !empty($this->vars) ? (string) $this->vars : ''; } @@ -151,8 +196,9 @@ */ public function db_write($key, $vars) { - $ts = microtime(true); - $now = $this->db->fromunixtime((int)$ts); + $now = $this->db->now(); + $table = $this->db->table_name('session'); + $ts = microtime(true); // no session row in DB (db_read() returns false) if (!$this->key) { @@ -170,22 +216,19 @@ $newvars = $this->_fixvars($vars, $oldvars); if ($newvars !== $oldvars) { - $this->db->query( - sprintf("UPDATE %s SET vars=?, changed=%s WHERE sess_id=?", - $this->db->table_name('session'), $now), - base64_encode($newvars), $key); + $this->db->query("UPDATE $table " + . "SET changed = $now, vars = ? WHERE sess_id = ?", + base64_encode($newvars), $key); } - else if ($ts - $this->changed > $this->lifetime / 2) { - $this->db->query("UPDATE ".$this->db->table_name('session') - ." SET changed=$now WHERE sess_id=?", $key); + else if ($ts - $this->changed + $this->time_diff > $this->lifetime / 2) { + $this->db->query("UPDATE $table SET changed = $now" + . " WHERE sess_id = ?", $key); } } else { - $this->db->query( - sprintf("INSERT INTO %s (sess_id, vars, ip, created, changed) ". - "VALUES (?, ?, ?, %s, %s)", - $this->db->table_name('session'), $now, $now), - $key, base64_encode($vars), (string)$this->ip); + $this->db->query("INSERT INTO $table (sess_id, vars, ip, created, changed)" + . " VALUES (?, ?, ?, $now, $now)", + $key, base64_encode($vars), (string)$this->ip); } return true; @@ -200,8 +243,18 @@ if ($oldvars !== null) { $a_oldvars = $this->unserialize($oldvars); if (is_array($a_oldvars)) { - foreach ((array)$this->unsets as $k) - unset($a_oldvars[$k]); + // remove unset keys on oldvars + foreach ((array)$this->unsets as $var) { + if (isset($a_oldvars[$var])) { + unset($a_oldvars[$var]); + } + else { + $path = explode('.', $var); + $k = array_pop($path); + $node = &$this->get_node($path, $a_oldvars); + unset($node[$k]); + } + } $newvars = $this->serialize(array_merge( (array)$a_oldvars, (array)$this->unserialize($vars))); @@ -235,25 +288,6 @@ /** - * Garbage collecting function - * - * @param string Session lifetime in seconds - * @return boolean True on success - */
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_smtp.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_smtp.php
Changed
@@ -33,6 +33,8 @@ // define headers delimiter const SMTP_MIME_CRLF = "\r\n"; + const DEBUG_LINE_LENGTH = 4098; // 4KB + 2B for \r\n + /** * SMTP Connection and authentication @@ -119,7 +121,7 @@ } // try to connect to server and exit on failure - $result = $this->conn->connect($smtp_timeout); + $result = $this->conn->connect($CONFIG['smtp_timeout']); if (PEAR::isError($result)) { $this->response[] = "Connection failed: ".$result->getMessage(); @@ -327,6 +329,12 @@ */ public function debug_handler(&$smtp, $message) { + if (($len = strlen($message)) > self::DEBUG_LINE_LENGTH) { + $diff = $len - self::DEBUG_LINE_LENGTH; + $message = substr($message, 0, self::DEBUG_LINE_LENGTH) + . "... [truncated $diff bytes]"; + } + rcube::write_log('smtp', preg_replace('/\r\n$/', '', $message)); } @@ -433,9 +441,9 @@ $recipients = rcube_utils::explode_quoted_string(',', $recipients); reset($recipients); - while (list($k, $recipient) = each($recipients)) { + foreach ($recipients as $recipient) { $a = rcube_utils::explode_quoted_string(' ', $recipient); - while (list($k2, $word) = each($a)) { + foreach ($a as $word) { if (strpos($word, "@") > 0 && $word[strlen($word)-1] != '"') { $word = preg_replace('/^<|>$/', '', trim($word)); if (in_array($word, $addresses) === false) {
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_spellchecker.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_spellchecker.php
Changed
@@ -31,7 +31,7 @@ private $lang; private $rc; private $error; - private $separator = '/[\s\r\n\t\(\)\/\[\]{}<>\\"]+|[:;?!,\.]([^\w]|$)/'; + private $separator = '/[\s\r\n\t\(\)\/\[\]{}<>\\"]+|[:;?!,\.](?=\W|$)/'; private $options = array(); private $dict; private $have_dict; @@ -84,6 +84,9 @@ 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 @@ 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 @@ 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); } @@ -314,11 +323,6 @@ if (!$this->plink) { if (!extension_loaded('pspell')) { $this->error = "Pspell extension not available"; - rcube::raise_error(array( - 'code' => 500, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, - 'message' => $this->error), true, false); - return; } @@ -331,6 +335,141 @@ } + /** + * 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 @@ -372,9 +511,19 @@ fclose($fp); } + // parse HTTP response + if (preg_match('!^HTTP/1.\d (\d+)(.+)!', $store, $m)) { + $http_status = $m[1];
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_storage.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_storage.php
Changed
@@ -61,8 +61,6 @@ 'MAIL-FOLLOWUP-TO', 'MAIL-REPLY-TO', 'RETURN-PATH', - 'DELIVERED-TO', - 'ENVELOPE-TO', ); const UNKNOWN = 0; @@ -540,12 +538,13 @@ /** * Append a mail message (source) to a specific folder. * - * @param string $folder Target folder - * @param string $message The message source string or filename - * @param string $headers Headers string if $message contains only the body - * @param boolean $is_file True if $message is a filename - * @param array $flags Message flags - * @param mixed $date Message internal date + * @param string $folder Target folder + * @param string|array $message The message source string or filename + * or array (of strings and file pointers) + * @param string $headers Headers string if $message contains only the body + * @param boolean $is_file True if $message is a filename + * @param array $flags Message flags + * @param mixed $date Message internal date * * @return int|bool Appended message UID or True on success, False on error */ @@ -807,13 +806,14 @@ /** - * Returns current status of a folder + * Returns current status of a folder (compared to the last time use) * * @param string $folder Folder name + * @param array $diff Difference data * * @return int Folder status */ - abstract function folder_status($folder = null); + abstract function folder_status($folder = null, &$diff = array()); /** @@ -985,6 +985,6 @@ /** * Delete outdated cache entries */ - abstract function expunge_cache(); + abstract function cache_gc(); } // end class rcube_storage
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_string_replacer.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_string_replacer.php
Changed
@@ -28,22 +28,25 @@ public $mailto_pattern; public $link_pattern; private $values = array(); + private $options = array(); - function __construct() + function __construct($options = array()) { // Simplified domain expression for UTF8 characters handling // 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 ."(\?[$url1$url2]+)?" // e.g. ?subject=test... .")/"; + + $this->options = $options; } /** @@ -89,15 +92,15 @@ if ($url) { $suffix = $this->parse_url_brackets($url); - $i = $this->add($prefix . html::a(array( - 'href' => $url_prefix . $url, - 'target' => '_blank' - ), rcube::Q($url)) . $suffix); + $attrib = (array)$this->options['link_attribs']; + $attrib['href'] = $url_prefix . $url; + + $i = $this->add(html::a($attrib, rcube::Q($url)) . $suffix); } // Return valid link for recognized schemes, otherwise // return the unmodified string for unrecognized schemes. - return $i >= 0 ? $this->get_replacement($i) : $matches[0]; + return $i >= 0 ? $prefix . $this->get_replacement($i) : $matches[0]; } /**
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_user.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_user.php
Changed
@@ -495,9 +495,9 @@ "INSERT INTO ".$dbh->table_name('users'). " (created, last_login, username, mail_host, language)". " VALUES (".$dbh->now().", ".$dbh->now().", ?, ?, ?)", - strip_newlines($data['user']), - strip_newlines($data['host']), - strip_newlines($data['language'])); + $data['user'], + $data['host'], + $data['language']); if ($user_id = $dbh->insert_id('users')) { // create rcube_user instance to make plugin hooks work @@ -517,7 +517,7 @@ if (empty($user_email)) { $user_email = strpos($data['user'], '@') ? $user : sprintf('%s@%s', $data['user'], $mail_domain); } - $email_list[] = strip_newlines($user_email); + $email_list[] = $user_email; } // identities_level check else if (count($email_list) > 1 && $rcube->config->get('identities_level', 0) > 1) { @@ -547,7 +547,6 @@ $record['name'] = $user_name != $record['email'] ? $user_name : ''; } - $record['name'] = strip_newlines($record['name']); $record['user_id'] = $user_id; $record['standard'] = $standard;
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_utils.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_utils.php
Changed
@@ -156,7 +156,7 @@ { // IPv6, but there's no build-in IPv6 support if (strpos($ip, ':') !== false && !defined('AF_INET6')) { - $parts = explode(':', $domain_part); + $parts = explode(':', $ip); $count = count($parts); if ($count > 8 || $count < 2) { @@ -360,12 +360,8 @@ return $value; } - // strip single quotes if magic_quotes_sybase is enabled - if (ini_get('magic_quotes_sybase')) { - $value = str_replace("''", "'", $value); - } // strip slashes if magic_quotes enabled - else if (get_magic_quotes_gpc() || get_magic_quotes_runtime()) { + if (get_magic_quotes_gpc() || get_magic_quotes_runtime()) { $value = stripslashes($value); } @@ -404,7 +400,7 @@ $out = array(); $src = $mode == self::INPUT_GET ? $_GET : ($mode == self::INPUT_POST ? $_POST : $_REQUEST); - foreach ($src as $key => $value) { + 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); @@ -510,17 +506,24 @@ */ public static function file2class($mimetype, $filename) { + $mimetype = strtolower($mimetype); + $filename = strtolower($filename); + list($primary, $secondary) = explode('/', $mimetype); $classes = array($primary ? $primary : 'unknown'); + if ($secondary) { $classes[] = $secondary; } - if (preg_match('/\.([a-z0-9]+)$/i', $filename, $m)) { - $classes[] = $m[1]; + + if (preg_match('/\.([a-z0-9]+)$/', $filename, $m)) { + if (!in_array($m[1], $classes)) { + $classes[] = $m[1]; + } } - return strtolower(join(" ", $classes)); + return join(" ", $classes); } @@ -663,6 +666,21 @@ /** + * 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 @@ -726,11 +744,23 @@ return mktime(0,0,0, intval($matches[2]), intval($matches[3]), intval($matches[1])); } else if (is_numeric($date)) { - return $date; + return (int) $date; } - // support non-standard "GMTXXXX" literal - $date = preg_replace('/GMT\s*([+-][0-9]+)/', '\\1', $date); + // Clean malformed data + $date = preg_replace( + array( + '/GMT\s*([+-][0-9]+)/', // support non-standard "GMTXXXX" literal + '/[^a-z0-9\x20\x09:+-]/i', // remove any invalid characters + '/\s*(Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s*/i', // remove weekday names + ), + array( + '\\1', + '', + '', + ), $date); + + $date = trim($date); // if date parsing fails, we have a date in non-rfc format. // remove token from the end and try again @@ -743,7 +773,7 @@ $date = implode(' ', $d); } - return $ts; + return (int) $ts; }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_vcard.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_vcard.php
Changed
@@ -90,7 +90,7 @@ */ public function __construct($vcard = null, $charset = RCUBE_CHARSET, $detect = false, $fieldmap = array()) { - if (!empty($fielmap)) { + if (!empty($fieldmap)) { $this->extend_fieldmap($fieldmap); } @@ -481,7 +481,7 @@ $vcard_block = ''; $in_vcard_block = false; - foreach (preg_split("/[\r\n]+/", $data) as $i => $line) { + foreach (preg_split("/[\r\n]+/", $data) as $line) { if ($in_vcard_block && !empty($line)) { $vcard_block .= $line . "\n"; } @@ -491,7 +491,9 @@ if (preg_match('/^END:VCARD$/i', $line)) { // parse vcard $obj = new rcube_vcard(self::cleanup($vcard_block), $charset, true, self::$fieldmap); - if (!empty($obj->displayname) || !empty($obj->email)) { + // FN and N is required by vCard format (RFC 2426) + // on import we can be less restrictive, let's addressbook decide + if (!empty($obj->displayname) || !empty($obj->surname) || !empty($obj->firstname) || !empty($obj->email)) { $out[] = $obj; } @@ -513,7 +515,7 @@ * * @return string Cleaned vcard block */ - private static function cleanup($vcard) + public static function cleanup($vcard) { // Convert special types (like Skype) to normal type='skype' classes with this simple regex ;) $vcard = preg_replace( @@ -712,9 +714,15 @@ $value[] = $attrvalues; } else if (is_bool($attrvalues)) { - // true means just tag, not tag=value, as in PHOTO;BASE64:... + // true means just a tag, not tag=value, as in PHOTO;BASE64:... if ($attrvalues) { - $attr .= strtoupper(";$attrname"); + // vCard v3 uses ENCODING=B (#1489183) + if ($attrname == 'base64') { + $attr .= ";ENCODING=B"; + } + else { + $attr .= strtoupper(";$attrname"); + } } } else { @@ -782,9 +790,30 @@ } return $result; } + + $s = strtr($s, $rep2); + } + + // some implementations (GMail) use non-standard backslash before colon (#1489085) + // we will handle properly any backslashed character - removing dummy backslahes + // return strtr($s, array("\r" => '', '\\\\' => '\\', '\n' => "\n", '\N' => "\n", '\,' => ',', '\;' => ';')); + + $s = str_replace("\r", '', $s); + $pos = 0; + + while (($pos = strpos($s, '\\', $pos)) !== false) { + $next = substr($s, $pos + 1, 1); + if ($next == 'n' || $next == 'N') { + $s = substr_replace($s, "\n", $pos, 2); + } + else { + $s = substr_replace($s, '', $pos, 1); + } + + $pos += 1; } - return strtr($s, array("\r" => '', '\\\\' => '\\', '\n' => "\n", '\N' => "\n", '\,' => ',', '\;' => ';')); + return $s; } /**
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Roundcube/rcube_washtml.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Roundcube/rcube_washtml.php
Changed
@@ -113,10 +113,9 @@ 'type', 'rows', 'cols', 'disabled', 'readonly', 'checked', 'multiple', 'value' ); - /* Block elements which could be empty but cannot be returned in short form (<tag />) */ - static $block_elements = array('div', 'p', 'pre', 'blockquote', 'a', 'font', 'center', - 'table', 'ul', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'dl', 'strong', - 'i', 'b', 'u', 'span', + /* Elements which could be empty and be returned in short form (<tag />) */ + static $void_elements = array('area', 'base', 'br', 'col', 'command', 'embed', 'hr', + 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr' ); /* State for linked objects in HTML */ @@ -134,12 +133,15 @@ /* Ignore these HTML tags but process their content */ private $_ignore_elements = array(); - /* Block elements which could be empty but cannot be returned in short form (<tag />) */ - private $_block_elements = array(); + /* Elements which could be empty and be returned in short form (<tag />) */ + private $_void_elements = array(); /* Allowed HTML attributes */ private $_html_attribs = array(); + /* Max nesting level */ + private $max_nesting_level; + /** * Class constructor @@ -149,9 +151,9 @@ $this->_html_elements = array_flip((array)$p['html_elements']) + array_flip(self::$html_elements) ; $this->_html_attribs = array_flip((array)$p['html_attribs']) + array_flip(self::$html_attribs); $this->_ignore_elements = array_flip((array)$p['ignore_elements']) + array_flip(self::$ignore_elements); - $this->_block_elements = array_flip((array)$p['block_elements']) + array_flip(self::$block_elements); + $this->_void_elements = array_flip((array)$p['void_elements']) + array_flip(self::$void_elements); - unset($p['html_elements'], $p['html_attribs'], $p['ignore_elements'], $p['block_elements']); + unset($p['html_elements'], $p['html_attribs'], $p['ignore_elements'], $p['void_elements']); $this->config = $p + array('show_washed' => true, 'allow_remote' => false, 'cid_map' => array()); } @@ -240,7 +242,8 @@ $value = $node->getAttribute($key); if (isset($this->_html_attribs[$key]) || - ($key == 'href' && !preg_match('!^(javascript|vbscript|data:text)!i', $value) + ($key == 'href' && ($value = trim($value)) + && !preg_match('!^(javascript|vbscript|data:text)!i', $value) && preg_match('!^([a-z][a-z0-9.+-]+:|//|#).+!i', $value)) ) { $t .= ' ' . $key . '="' . htmlspecialchars($value, ENT_QUOTES) . '"'; @@ -283,12 +286,26 @@ * It output only allowed tags with allowed attributes * and allowed inline styles */ - private function dumpHtml($node) + private function dumpHtml($node, $level = 0) { if (!$node->hasChildNodes()) { return ''; } + $level++; + + if ($this->max_nesting_level > 0 && $level == $this->max_nesting_level - 1) { + // log error message once + if (!$this->max_nesting_level_error) { + $this->max_nesting_level_error = true; + rcube::raise_error(array('code' => 500, 'type' => 'php', + 'line' => __LINE__, 'file' => __FILE__, + 'message' => "Maximum nesting level exceeded (xdebug.max_nesting_level={$this->max_nesting_level})"), + true, false); + } + return '<!-- ignored -->'; + } + $node = $node->firstChild; $dump = ''; @@ -298,19 +315,19 @@ $tagName = strtolower($node->tagName); if ($callback = $this->handlers[$tagName]) { $dump .= call_user_func($callback, $tagName, - $this->wash_attribs($node), $this->dumpHtml($node), $this); + $this->wash_attribs($node), $this->dumpHtml($node, $level), $this); } else if (isset($this->_html_elements[$tagName])) { - $content = $this->dumpHtml($node); + $content = $this->dumpHtml($node, $level); $dump .= '<' . $tagName . $this->wash_attribs($node) . - ($content != '' || isset($this->_block_elements[$tagName]) ? ">$content</$tagName>" : ' />'); + ($content === '' && isset($this->_void_elements[$tagName]) ? ' />' : ">$content</$tagName>"); } else if (isset($this->_ignore_elements[$tagName])) { $dump .= '<!-- ' . htmlspecialchars($tagName, ENT_QUOTES) . ' not allowed -->'; } else { $dump .= '<!-- ' . htmlspecialchars($tagName, ENT_QUOTES) . ' ignored -->'; - $dump .= $this->dumpHtml($node); // ignore tags not its content + $dump .= $this->dumpHtml($node, $level); // ignore tags not its content } break; @@ -323,14 +340,14 @@ break; case XML_HTML_DOCUMENT_NODE: - $dump .= $this->dumpHtml($node); + $dump .= $this->dumpHtml($node, $level); break; case XML_DOCUMENT_TYPE_NODE: break; default: - $dump . '<!-- node type ' . $node->nodeType . ' -->'; + $dump .= '<!-- node type ' . $node->nodeType . ' -->'; } } while($node = $node->nextSibling); @@ -357,6 +374,9 @@ $this->config['base_url'] = ''; } + // Detect max nesting level (for dumpHTML) (#1489110) + $this->max_nesting_level = (int) @ini_get('xdebug.max_nesting_level'); + @$node->loadHTML($html); return $this->dumpHtml($node); } @@ -390,6 +410,25 @@ ); $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."; @@ -404,6 +443,7 @@ rcube::raise_error(array('code' => 620, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__, 'message' => $errstr), true, false); + return ''; } @@ -412,7 +452,8 @@ // Remove invalid HTML comments (#1487759) // Don't remove valid conditional comments - $html = preg_replace('/<!--[^->[\n]*>/', '', $html); + // Don't remove MSOutlook (<!-->) conditional comments (#1489004) + $html = preg_replace('/<!--[^->\[\n]+>/', '', $html); // turn relative into absolute urls $html = self::resolve_base($html);
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Backend/ABackend.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Backend/ABackend.php
Changed
@@ -13,7 +13,6 @@ * * @package Backend */ - abstract class Syncroton_Backend_ABackend implements Syncroton_Backend_IBackend { /** @@ -31,6 +30,12 @@ protected $_modelInterfaceName; + /** + * the constructor + * + * @param Zend_Db_Adapter_Abstract $_db + * @param string $_tablePrefix + */ public function __construct(Zend_Db_Adapter_Abstract $_db, $_tablePrefix = 'Syncroton_') { $this->_db = $_db; @@ -58,6 +63,12 @@ return $this->get($data['id']); } + /** + * convert iteratable object to array + * + * @param unknown $model + * @return array + */ protected function _convertModelToArray($model) { $data = array(); @@ -99,6 +110,12 @@ return $this->_getObject($data); } + /** + * convert array to object + * + * @param array $data + * @return object + */ protected function _getObject($data) { foreach ($data as $key => $value) { @@ -114,6 +131,10 @@ return new $this->_modelClassName($data); } + /** + * (non-PHPdoc) + * @see Syncroton_Backend_IBackend::delete() + */ public function delete($id) { $id = $id instanceof $this->_modelInterfaceName ? $id->id : $id; @@ -123,6 +144,10 @@ return (bool) $result; } + /** + * (non-PHPdoc) + * @see Syncroton_Backend_IBackend::update() + */ public function update($model) { if (! $model instanceof $this->_modelInterfaceName) { @@ -138,6 +163,11 @@ return $this->get($model->id); } + /** + * convert from camelCase to camel_case + * @param string $string + * @return string + */ protected function _fromCamelCase($string) { $string = lcfirst($string); @@ -145,6 +175,13 @@ return preg_replace_callback('/([A-Z])/', function ($string) {return '_' . strtolower($string[0]);}, $string); } + /** + * convert from camel_case to camelCase + * + * @param string $string + * @param bool $ucFirst + * @return string + */ protected function _toCamelCase($string, $ucFirst = true) { if ($ucFirst === true) {
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Backend/Policy.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Backend/Policy.php
Changed
@@ -21,4 +21,50 @@ protected $_modelClassName = 'Syncroton_Model_Policy'; protected $_modelInterfaceName = 'Syncroton_Model_IPolicy'; + + /** + * convert iteratable object to array + * + * @param unknown $model + * @return array + */ + protected function _convertModelToArray($model) + { + $policyValues = $model->getProperties('Provision'); + + $policy = array(); + + foreach ($policyValues as $policyName) { + if ($model->$policyName !== NULL) { + $policy[$policyName] = $model->$policyName; + } + + unset($model->$policyName); + } + + $data = parent::_convertModelToArray($model); + + $data['json_policy'] = Zend_Json::encode($policy); + + return $data; + } + + /** + * convert array to object + * + * @param array $data + * @return object + */ + protected function _getObject($data) + { + $policy = Zend_Json::decode($data['json_policy']); + + foreach ($policy as $policyKey => $policyValue) { + $data[$policyKey] = $policyValue; + } + + unset($data['json_policy']); + + return parent::_getObject($data); + } }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Command/FolderSync.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Command/FolderSync.php
Changed
@@ -121,7 +121,8 @@ $this->_headers = array_merge($this->_headers, $optionsCommand->getHeaders()); } - $adds = array(); + $adds = array(); + $updates = array(); $deletes = array(); foreach($this->_classes as $class) { @@ -138,6 +139,13 @@ // retrieve all folders available in data backend $serverFolders = $dataController->getAllFolders(); + if ($this->_syncState->counter > 0) { + // retrieve all folders changed since last sync + $changedFolders = $dataController->getChangedFolders($this->_syncState->lastsync, $this->_syncTimeStamp); + } else { + $changedFolders = array(); + } + // retrieve all folders sent to client $clientFolders = $this->_folderBackend->getFolderState($this->_device, $class); @@ -186,6 +194,17 @@ $adds[] = $add; } + // calculate changed entries + foreach ($changedFolders as $changedFolder) { + $change = $clientFolders[$changedFolder->serverId]; + + $change->displayName = $changedFolder->displayName; + $change->parentId = $changedFolder->parentId; + $change->type = $changedFolder->type; + + $updates[] = $change; + } + // calculate deleted entries $serverDiff = array_diff($clientFoldersIds, $serverFoldersIds); foreach ($serverDiff as $serverFolderId) { @@ -195,7 +214,7 @@ $folderSync->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Status', self::STATUS_SUCCESS)); - $count = count($adds) + /*count($changes) + */count($deletes); + $count = count($adds) + count($updates) + count($deletes); if($count > 0) { $this->_syncState->counter++; $this->_syncState->lastsync = $this->_syncTimeStamp; @@ -218,6 +237,14 @@ } } + foreach($updates as $folder) { + $update = $changes->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Update')); + + $folder->appendXML($update, $this->_device); + + $this->_folderBackend->update($folder); + } + foreach($deletes as $folder) { $delete = $changes->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'Delete')); $delete->appendChild($this->_outputDom->createElementNS('uri:FolderHierarchy', 'ServerId', $folder->serverId));
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Command/GetItemEstimate.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Command/GetItemEstimate.php
Changed
@@ -137,7 +137,7 @@ } // folderState can be NULL in case of not existing folder - if (isset($collectionData['folder'])) { + if (isset($collectionData['folder']) && $collectionData['folder']->isDirty()) { $this->_folderBackend->update($collectionData['folder']); } }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Command/ItemOperations.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Command/ItemOperations.php
Changed
@@ -34,6 +34,13 @@ protected $_fetches = array(); /** + * list of folder to empty + * + * @var array + */ + protected $_emptyFolderContents = array(); + + /** * parse MoveItems request * */ @@ -43,59 +50,13 @@ if (isset($xml->Fetch)) { foreach ($xml->Fetch as $fetch) { - $fetchArray = array( - 'store' => (string)$fetch->Store, - 'options' => array() - ); - - // try to fetch element from namespace AirSync - $airSync = $fetch->children('uri:AirSync'); - - if (isset($airSync->CollectionId)) { - $fetchArray['collectionId'] = (string)$airSync->CollectionId; - $fetchArray['serverId'] = (string)$airSync->ServerId; - } - - // try to fetch element from namespace Search - $search = $fetch->children('uri:Search'); - - if (isset($search->LongId)) { - $fetchArray['longId'] = (string)$search->LongId; - } - - // try to fetch element from namespace AirSyncBase - $airSyncBase = $fetch->children('uri:AirSyncBase'); - - if (isset($airSyncBase->FileReference)) { - $fetchArray['fileReference'] = (string)$airSyncBase->FileReference; - } - - if (isset($fetch->Options)) { - // try to fetch element from namespace AirSyncBase - $airSyncBase = $fetch->Options->children('uri:AirSyncBase'); - - if (isset($airSyncBase->BodyPreference)) { - foreach ($airSyncBase->BodyPreference as $bodyPreference) { - $type = (int) $bodyPreference->Type; - $fetchArray['options']['bodyPreferences'][$type] = array( - 'type' => $type - ); - - // optional - if (isset($bodyPreference->TruncationSize)) { - $fetchArray['options']['bodyPreferences'][$type]['truncationSize'] = (int) $bodyPreference->TruncationSize; - } - - // optional - if (isset($bodyPreference->AllOrNone)) { - $fetchArray['options']['bodyPreferences'][$type]['allOrNone'] = (int) $bodyPreference->AllOrNone; - } - } - } - - - } - $this->_fetches[] = $fetchArray; + $this->_fetches[] = $this->_handleFetch($fetch); + } + } + + if (isset($xml->EmptyFolderContents)) { + foreach ($xml->EmptyFolderContents as $emptyFolderContents) { + $this->_emptyFolderContents[] = $this->_handleEmptyFolderContents($emptyFolderContents); } } @@ -187,6 +148,106 @@ } } + foreach ($this->_emptyFolderContents as $emptyFolderContents) { + + try { + $folder = $this->_folderBackend->getFolder($this->_device, $emptyFolderContents['collectionId']); + + $dataController = Syncroton_Data_Factory::factory($folder->class, $this->_device, $this->_syncTimeStamp); + $dataController->emptyFolderContents($emptyFolderContents['collectionId'], $emptyFolderContents['options']); + + $status = Syncroton_Command_ItemOperations::STATUS_SUCCESS; + } + catch (Syncroton_Exception_Status_ItemOperations $e) { + $status = $e->getCode(); + } + catch (Exception $e) { + $status = Syncroton_Exception_Status_ItemOperations::ITEM_SERVER_ERROR; + } + + $emptyFolderContentsTag = $this->_outputDom->createElementNS('uri:ItemOperations', 'EmptyFolderContents'); + + $emptyFolderContentsTag->appendChild($this->_outputDom->createElementNS('uri:ItemOperations', 'Status', $status)); + $emptyFolderContentsTag->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'CollectionId', $emptyFolderContents['collectionId'])); + + $response->appendChild($emptyFolderContentsTag); + } + return $this->_outputDom; } + + protected function _handleFetch(SimpleXMLElement $fetch) + { + $fetchArray = array( + 'store' => (string)$fetch->Store, + 'options' => array() + ); + + // try to fetch element from namespace AirSync + $airSync = $fetch->children('uri:AirSync'); + + if (isset($airSync->CollectionId)) { + $fetchArray['collectionId'] = (string)$airSync->CollectionId; + $fetchArray['serverId'] = (string)$airSync->ServerId; + } + + // try to fetch element from namespace Search + $search = $fetch->children('uri:Search'); + + if (isset($search->LongId)) { + $fetchArray['longId'] = (string)$search->LongId; + } + + // try to fetch element from namespace AirSyncBase + $airSyncBase = $fetch->children('uri:AirSyncBase'); + + if (isset($airSyncBase->FileReference)) { + $fetchArray['fileReference'] = (string)$airSyncBase->FileReference; + } + + if (isset($fetch->Options)) { + // try to fetch element from namespace AirSyncBase + $airSyncBase = $fetch->Options->children('uri:AirSyncBase'); + + if (isset($airSyncBase->BodyPreference)) { + foreach ($airSyncBase->BodyPreference as $bodyPreference) { + $type = (int) $bodyPreference->Type; + $fetchArray['options']['bodyPreferences'][$type] = array( + 'type' => $type + ); + + // optional + if (isset($bodyPreference->TruncationSize)) { + $fetchArray['options']['bodyPreferences'][$type]['truncationSize'] = (int) $bodyPreference->TruncationSize; + } + + // optional + if (isset($bodyPreference->AllOrNone)) { + $fetchArray['options']['bodyPreferences'][$type]['allOrNone'] = (int) $bodyPreference->AllOrNone; + } + } + } + } + + return $fetchArray; + } + + protected function _handleEmptyFolderContents(SimpleXMLElement $emptyFolderContent) + { + $folderArray = array( + 'collectiondId' => null, + 'options' => array('deleteSubFolders' => FALSE) + ); + + // try to fetch element from namespace AirSync + $airSync = $emptyFolderContent->children('uri:AirSync'); + + $folderArray['collectionId'] = (string)$airSync->CollectionId; + + if (isset($emptyFolderContent->Options)) { + $folderArray['options']['deleteSubFolders'] = isset($emptyFolderContent->Options->DeleteSubFolders); + } + + return $folderArray; + } }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Command/Ping.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Command/Ping.php
Changed
@@ -89,8 +89,13 @@ $intervalEnd = $intervalStart + $lifeTime; $secondsLeft = $intervalEnd; + $folders = unserialize($this->_device->pingfolder); + if ($status === self::STATUS_NO_CHANGES_FOUND && (!is_array($folders) || count($folders) == 0)) { + $status = self::STATUS_MISSING_PARAMETERS; + } + if ($this->_logger instanceof Zend_Log) $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " Folders to monitor($lifeTime / $intervalStart / $intervalEnd / $status): " . print_r($folders, true)); @@ -104,7 +109,7 @@ $now = new DateTime('now', new DateTimeZone('utc')); - foreach ((array) $folders as $folderId) { + foreach ($folders as $folderId) { try { $folder = $this->_folderBackend->get($folderId); $dataController = Syncroton_Data_Factory::factory($folder->class, $this->_device, $this->_syncTimeStamp);
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Command/Provision.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Command/Provision.php
Changed
@@ -60,7 +60,6 @@ if ($this->_device->remotewipe == self::REMOTEWIPE_REQUESTED && isset($xml->RemoteWipe->Status) && (int)$xml->RemoteWipe->Status == self::STATUS_SUCCESS) { $this->_device->remotewipe = self::REMOTEWIPE_CONFIRMED; - $this->_device = $this->_deviceBackend->update($this->_device); } // try to fetch element from Settings namespace @@ -74,9 +73,10 @@ $this->_device->os = $this->_deviceInformation->oS; $this->_device->oslanguage = $this->_deviceInformation->oSLanguage; $this->_device->phonenumber = $this->_deviceInformation->phoneNumber; - - $this->_device = $this->_deviceBackend->update($this->_device); - + } + + if ($this->_device->isDirty()) { + $this->_device = $this->_deviceBackend->update($this->_device); } } @@ -194,6 +194,6 @@ */ public static function generatePolicyKey() { - return sha1(mt_rand(). microtime()); + return mt_rand(1, 2147483647); } }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Command/Settings.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Command/Settings.php
Changed
@@ -54,8 +54,10 @@ $this->_device->os = $this->_deviceInformation->oS; $this->_device->oslanguage = $this->_deviceInformation->oSLanguage; $this->_device->phonenumber = $this->_deviceInformation->phoneNumber; - - $this->_device = $this->_deviceBackend->update($this->_device); + + if ($this->_device->isDirty()) { + $this->_device = $this->_deviceBackend->update($this->_device); + } } if(isset($xml->UserInformation->Get)) {
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Command/Sync.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Command/Sync.php
Changed
@@ -113,60 +113,60 @@ public function handle() { // input xml - $xml = simplexml_import_dom($this->_requestBody); + $requestXML = simplexml_import_dom($this->_mergeSyncRequest($this->_requestBody, $this->_device)); - if (isset($xml->HeartbeatInterval)) { - $this->_heartbeatInterval = (int)$xml->HeartbeatInterval; - } elseif (isset($xml->Wait)) { - $this->_heartbeatInterval = (int)$xml->Wait * 60; + if (! isset($requestXML->Collections)) { + $this->_outputDom->documentElement->appendChild( + $this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_RESEND_FULL_XML) + ); + + return $this->_outputDom; } - $this->_globalWindowSize = isset($xml->WindowSize) ? (int)$xml->WindowSize : 100; + if (isset($requestXML->HeartbeatInterval)) { + $this->_heartbeatInterval = (int)$requestXML->HeartbeatInterval; + } elseif (isset($requestXML->Wait)) { + $this->_heartbeatInterval = (int)$requestXML->Wait * 60; + } + + $this->_globalWindowSize = isset($requestXML->WindowSize) ? (int)$requestXML->WindowSize : 100; if ($this->_globalWindowSize > $this->_maxWindowSize) { $this->_globalWindowSize = $this->_maxWindowSize; } + + // load options from lastsynccollection + $lastSyncCollection = array('options' => array()); + if (!empty($this->_device->lastsynccollection)) { + $lastSyncCollection = Zend_Json::decode($this->_device->lastsynccollection); + if (!array_key_exists('options', $lastSyncCollection) || !is_array($lastSyncCollection['options'])) { + $lastSyncCollection['options'] = array(); + } + } $collections = array(); - $isPartialRequest = isset($xml->Partial); - // try to restore collections from previous request - if ($isPartialRequest) { - $decodedCollections = Zend_Json::decode($this->_device->lastsynccollection); + foreach ($requestXML->Collections->Collection as $xmlCollection) { + $collectionId = (string)$xmlCollection->CollectionId; - if (is_array($decodedCollections)) { - foreach ($decodedCollections as $collection) { - $collections[$collection['collectionId']] = new Syncroton_Model_SyncCollection($collection); - } - } - } - - // Collections element is optional when Partial element is sent - if (isset($xml->Collections)) { - foreach ($xml->Collections->Collection as $xmlCollection) { - $collectionId = (string)$xmlCollection->CollectionId; - - // do we have to update a collection sent in previous sync request? - if ($isPartialRequest && isset($collections[$collectionId])) { - $collections[$collectionId]->setFromSimpleXMLElement($xmlCollection); - } else { - $collections[$collectionId] = new Syncroton_Model_SyncCollection($xmlCollection); - } - } + $collections[$collectionId] = new Syncroton_Model_SyncCollection($xmlCollection); - // store current value of $collections for next Sync command request - $collectionsToSave = array(); - - foreach ($collections as $collection) { - $collectionsToSave[$collection->collectionId] = $collection->toArray(); + // do we have to reuse the options from the previous request? + if (!isset($xmlCollection->Options) && array_key_exists($collectionId, $lastSyncCollection['options'])) { + $collections[$collectionId]->options = $lastSyncCollection['options'][$collectionId]; + if ($this->_logger instanceof Zend_Log) + $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " restored options to " . print_r($collections[$collectionId]->options, TRUE)); } + + // store current options for next Sync command request (sticky options) + $lastSyncCollection['options'][$collectionId] = $collections[$collectionId]->options; + } - $this->_device->lastsynccollection = Zend_Json::encode($collectionsToSave); + $this->_device->lastsynccollection = Zend_Json::encode($lastSyncCollection); - if ($this->_device->isDirty()) { - Syncroton_Registry::getDeviceBackend()->update($this->_device); - } + if ($this->_device->isDirty()) { + Syncroton_Registry::getDeviceBackend()->update($this->_device); } foreach ($collections as $collectionData) { @@ -412,13 +412,7 @@ { $sync = $this->_outputDom->documentElement; - if (count($this->_collections) == 0) { - $sync->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Status', self::STATUS_RESEND_FULL_XML)); - - return $this->_outputDom; - } - - $collections = $sync->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Collections')); + $collections = $this->_outputDom->createElementNS('uri:AirSync', 'Collections'); $totalChanges = 0; @@ -433,7 +427,7 @@ $now = new DateTime(null, new DateTimeZone('utc')); foreach($this->_collections as $collectionData) { - // countinue immediately if folder does not exist + // continue immediately if folder does not exist if (! ($collectionData->folder instanceof Syncroton_Model_IFolder)) { break 2; @@ -488,23 +482,20 @@ // safe battery time by skipping folders which got synchronied less than Syncroton_Command_Ping::$quietTime seconds ago - if (($now->getTimestamp() - $collectionData->syncState->lastsync->getTimestamp()) < Syncroton_Registry::getQuietTime()) { + if ( ! $collectionData->syncState instanceof Syncroton_Model_SyncState || + ($now->getTimestamp() - $collectionData->syncState->lastsync->getTimestamp()) < Syncroton_Registry::getQuietTime()) { continue; } $dataController = Syncroton_Data_Factory::factory($collectionData->folder->class , $this->_device, $this->_syncTimeStamp); - $hasChanges = $dataController->hasChanges($this->_contentStateBackend, $collectionData->folder, $collectionData->syncState); - // countinue immediately if there are any changes available - if ($hasChanges) { + if($dataController->hasChanges($this->_contentStateBackend, $collectionData->folder, $collectionData->syncState)) { break 2; } } } - $this->_syncTimeStamp = clone $now; - // See: http://www.tine20.org/forum/viewtopic.php?f=12&t=12146 // // break if there are less than PingTimeout + 10 seconds left for the next loop @@ -574,11 +565,21 @@ $serverModifications = $collectionData->syncState->pendingdata; - } else { + } elseif ($dataController->hasChanges($this->_contentStateBackend, $collectionData->folder, $collectionData->syncState)) { + + // update _syncTimeStamp as $dataController->hasChanges might have spent some time + $this->_syncTimeStamp = new DateTime(null, new DateTimeZone('utc')); + try { // fetch entries added since last sync - $allClientEntries = $this->_contentStateBackend->getFolderState($this->_device, $collectionData->folder); - $allServerEntries = $dataController->getServerEntries($collectionData->collectionId, $collectionData->options['filterType']); + $allClientEntries = $this->_contentStateBackend->getFolderState( + $this->_device, + $collectionData->folder + ); + $allServerEntries = $dataController->getServerEntries( + $collectionData->collectionId, + $collectionData->options['filterType'] + ); // add entries $serverDiff = array_diff($allServerEntries, $allClientEntries); @@ -601,7 +602,12 @@ $serverModifications['deleted'] = array_diff($allClientEntries, $allServerEntries); // fetch entries changed since last sync - $serverModifications['changed'] = $dataController->getChangedEntries($collectionData->collectionId, $collectionData->syncState->lastsync, $this->_syncTimeStamp, $collectionData->options['filterType']); + $serverModifications['changed'] = $dataController->getChangedEntries( + $collectionData->collectionId, + $collectionData->syncState->lastsync, + $this->_syncTimeStamp, + $collectionData->options['filterType'] + ); $serverModifications['changed'] = array_merge($serverModifications['changed'], $clientModifications['forceChange']); foreach($serverModifications['changed'] as $id => $serverId) { @@ -643,7 +649,7 @@ } // collection header - $collection = $collections->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Collection')); + $collection = $this->_outputDom->createElementNS('uri:AirSync', 'Collection'); if (!empty($collectionData->folder->class)) { $collection->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'Class', $collectionData->folder->class)); } @@ -752,7 +758,7 @@ $add->appendChild($this->_outputDom->createElementNS('uri:AirSync', 'ServerId', $serverId));
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Command/Wbxml.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Command/Wbxml.php
Changed
@@ -180,7 +180,7 @@ if (!empty($this->_device->policyId)) { $policy = $this->_policyBackend->get($this->_device->policyId); - if($policy->policyKey != $this->_policyKey) { + if((int)$policy->policyKey != (int)$this->_policyKey) { $this->_outputDom->documentElement->appendChild($this->_outputDom->createElementNS($this->_defaultNameSpace, 'Status', 142)); $sepn = new Syncroton_Exception_ProvisioningNeeded();
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Data/AData.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Data/AData.php
Changed
@@ -23,6 +23,17 @@ */ public static $changedEntries = array(); + /** + * used by unit tests only to simulated exhausted memory + */ + public static $exhaustedEntries = array(); + + /** + * the constructor + * + * @param Syncroton_Model_IDevice $_device + * @param DateTime $_timeStamp + */ public function __construct(Syncroton_Model_IDevice $_device, DateTime $_timeStamp) { $this->_device = $_device; @@ -32,6 +43,13 @@ $this->_ownerId = '1234'; } + /** + * return one folder identified by id + * + * @param string $id + * @throws Syncroton_Exception_NotFound + * @return Syncroton_Model_Folder + */ public function getFolder($id) { $select = $this->_db->select() @@ -55,6 +73,10 @@ )); } + /** + * (non-PHPdoc) + * @see Syncroton_Data_IData::createFolder() + */ public function createFolder(Syncroton_Model_IFolder $folder) { if (!in_array($folder->type, $this->_supportedFolderTypes)) { @@ -64,16 +86,21 @@ $id = !empty($folder->serverId) ? $folder->serverId : sha1(mt_rand(). microtime()); $this->_db->insert($this->_tablePrefix . 'data_folder', array( - 'id' => $id, - 'type' => $folder->type, - 'name' => $folder->displayName, - 'owner_id' => $this->_ownerId, - 'parent_id' => $folder->parentId + 'id' => $id, + 'type' => $folder->type, + 'name' => $folder->displayName, + 'owner_id' => $this->_ownerId, + 'parent_id' => $folder->parentId, + 'creation_time' => $this->_timestamp->format('Y-m-d H:i:s') )); return $this->getFolder($id); } + /** + * (non-PHPdoc) + * @see Syncroton_Data_IData::createEntry() + */ public function createEntry($_folderId, Syncroton_Model_IEntry $_entry) { $id = sha1(mt_rand(). microtime()); @@ -88,6 +115,10 @@ return $id; } + /** + * (non-PHPdoc) + * @see Syncroton_Data_IData::deleteEntry() + */ public function deleteEntry($_folderId, $_serverId, $_collectionData) { $folderId = $_folderId instanceof Syncroton_Model_IFolder ? $_folderId->serverId : $_folderId; @@ -97,6 +128,10 @@ return (bool) $result; } + /** + * (non-PHPdoc) + * @see Syncroton_Data_IData::deleteFolder() + */ public function deleteFolder($_folderId) { $folderId = $_folderId instanceof Syncroton_Model_IFolder ? $_folderId->serverId : $_folderId; @@ -107,6 +142,19 @@ return (bool) $result; } + /** + * (non-PHPdoc) + * @see Syncroton_Data_IData::emptyFolderContents() + */ + public function emptyFolderContents($folderId, $options) + { + return true; + } + + /** + * (non-PHPdoc) + * @see Syncroton_Data_IData::getAllFolders() + */ public function getAllFolders() { $select = $this->_db->select() @@ -132,6 +180,10 @@ return $result; } + /** + * (non-PHPdoc) + * @see Syncroton_Data_IData::getChangedEntries() + */ public function getChangedEntries($_folderId, DateTime $_startTimeStamp, DateTime $_endTimeStamp = NULL, $filterType = NULL) { if (!isset(Syncroton_Data_AData::$changedEntries[get_class($this)])) { @@ -142,6 +194,40 @@ } /** + * retrieve folders which were modified since last sync + * + * @param DateTime $startTimeStamp + * @param DateTime $endTimeStamp + * @return array list of Syncroton_Model_Folder + */ + public function getChangedFolders(DateTime $startTimeStamp, DateTime $endTimeStamp) + { + $select = $this->_db->select() + ->from($this->_tablePrefix . 'data_folder') + ->where('type IN (?)', $this->_supportedFolderTypes) + ->where('owner_id = ?', $this->_ownerId) + ->where('last_modified_time > ?', $startTimeStamp->format('Y-m-d H:i:s')) + ->where('last_modified_time <= ?', $endTimeStamp->format('Y-m-d H:i:s')); + + $stmt = $this->_db->query($select); + $folders = $stmt->fetchAll(); + $stmt = null; # see https://bugs.php.net/bug.php?id=44081 + + $result = array(); + + foreach ((array) $folders as $folder) { + $result[$folder['id']] = new Syncroton_Model_Folder(array( + 'serverId' => $folder['id'], + 'displayName' => $folder['name'], + 'type' => $folder['type'], + 'parentId' => $folder['parent_id'] + )); + } + + return $result; + } + + /** * @param Syncroton_Model_IFolder|string $_folderId * @param string $_filter * @return array @@ -164,6 +250,10 @@ return $ids; } + /** + * (non-PHPdoc) + * @see Syncroton_Data_IData::getCountOfChanges() + */ public function getCountOfChanges(Syncroton_Backend_IContent $contentBackend, Syncroton_Model_IFolder $folder, Syncroton_Model_ISyncState $syncState) { $allClientEntries = $contentBackend->getFolderState($this->_device, $folder); @@ -176,6 +266,10 @@ return count($addedEntries) + count($deletedEntries) + count($changedEntries); } + /** + * (non-PHPdoc) + * @see Syncroton_Data_IData::getFileReference() + */ public function getFileReference($fileReference) { throw new Syncroton_Exception_NotFound('filereference not found'); @@ -186,7 +280,10 @@ * @see Syncroton_Data_IData::getEntry() */ public function getEntry(Syncroton_Model_SyncCollection $collection, $serverId) - { + { + if (isset(self::$exhaustedEntries[get_class($this)]) && is_array(self::$exhaustedEntries[get_class($this)]) && in_array($serverId, self::$exhaustedEntries[get_class($this)])) { + throw new Syncroton_Exception_MemoryExhausted('memory exchausted for ' . $serverId); + } $select = $this->_db->select() ->from($this->_tablePrefix . 'data', array('data')) ->where('id = ?', $serverId); @@ -210,6 +307,10 @@ return !!$this->getCountOfChanges($contentBackend, $folder, $syncState);
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Data/IData.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Data/IData.php
Changed
@@ -43,7 +43,20 @@ */ public function deleteEntry($_folderId, $_serverId, $_collectionData); - public function deleteFolder($_folderId); + /** + * delete folder + * + * @param string $folderId + */ + public function deleteFolder($folderId); + + /** + * empty folder + * + * @param string $folderId + * @param array $options + */ + public function emptyFolderContents($folderId, $options); /** * return list off all folders @@ -53,6 +66,14 @@ public function getChangedEntries($folderId, DateTime $startTimeStamp, DateTime $endTimeStamp = NULL, $filterType = NULL); + /** + * retrieve folders which were modified since last sync + * + * @param DateTime $startTimeStamp + * @param DateTime $endTimeStamp + */ + public function getChangedFolders(DateTime $startTimeStamp, DateTime $endTimeStamp); + public function getCountOfChanges(Syncroton_Backend_IContent $contentBackend, Syncroton_Model_IFolder $folder, Syncroton_Model_ISyncState $syncState); /**
View file
kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Exception/MemoryExhausted.php
Added
@@ -0,0 +1,20 @@ +<?php +/** + * Syncroton + * + * @package Syncroton + * @subpackage Exception + * @license http://www.tine20.org/licenses/lgpl.html LGPL Version 3 + * @copyright Copyright (c) 2013-2013 Metaways Infosystems GmbH (http://www.metaways.de) + * @author Lars Kneschke <l.kneschke@metaways.de> + */ + +/** + * exception for memory exhausted + * + * @package Syncroton + * @subpackage Exception + */ +class Syncroton_Exception_MemoryExhausted extends Syncroton_Exception +{ +}
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Model/AXMLEntry.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Model/AXMLEntry.php
Changed
@@ -93,11 +93,14 @@ * (non-PHPdoc) * @see Syncroton_Model_IEntry::getProperties() */ - public function getProperties() + public function getProperties($selectedNamespace = null) { $properties = array(); foreach($this->_properties as $namespace => $namespaceProperties) { + if ($selectedNamespace !== null && $namespace != $selectedNamespace) { + continue; + } $properties = array_merge($properties, array_keys($namespaceProperties)); } @@ -169,7 +172,8 @@ if (!ctype_print($value)) { $value = $this->_removeControlChars($value); } - $element->appendChild($element->ownerDocument->createTextNode($value)); + + $element->appendChild($element->ownerDocument->createTextNode($this->_enforeUTF8($value))); } } } @@ -186,6 +190,29 @@ } /** + * enforce >valid< utf-8 encoding + * + * @param string $dirty the string with maybe invalid utf-8 data + * @return string string with valid utf-8 + */ + protected function _enforeUTF8($dirty) + { + if (function_exists('iconv')) { + if (($clean = @iconv('UTF-8', 'UTF-8//IGNORE', $dirty)) !== false) { + return $clean; + } + } + + if (function_exists('mb_convert_encoding')) { + if (($clean = mb_convert_encoding($dirty, 'UTF-8', 'UTF-8')) !== false) { + return $clean; + } + } + + return $dirty; + } + + /** * * @param unknown_type $element * @throws InvalidArgumentException
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Model/Content.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Model/Content.php
Changed
@@ -15,18 +15,7 @@ * @package Syncroton * @subpackage Model */ -class Syncroton_Model_Content implements Syncroton_Model_IContent +class Syncroton_Model_Content extends Syncroton_Model_AEntry implements Syncroton_Model_IContent { - public function __construct(array $_data = array()) - { - $this->setFromArray($_data); - } - - public function setFromArray(array $_data) - { - foreach($_data as $key => $value) { - $this->$key = $value; - } - } }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Model/Event.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Model/Event.php
Changed
@@ -65,32 +65,45 @@ ) ); - public function setFromArray(array $properties) - { - parent::setFromArray($properties); - - $this->_copyFieldsFromParent(); - } - - /** - * set properties from SimpleXMLElement object - * - * @param SimpleXMLElement $xmlCollection - * @throws InvalidArgumentException - */ - public function setFromSimpleXMLElement(SimpleXMLElement $properties) + /** + * (non-PHPdoc) + * @see Syncroton_Model_IEntry::appendXML() + * @todo handle Attendees element + */ + public function appendXML(DOMElement $domParrent, Syncroton_Model_IDevice $device) { - parent::setFromSimpleXMLElement($properties); - - $this->_copyFieldsFromParent(); + parent::appendXML($domParrent, $device); + + $exceptionElements = $domParrent->getElementsByTagName('Exception'); + $parentFields = array('AllDayEvent'/*, 'Attendees'*/, 'Body', 'BusyStatus'/*, 'Categories'*/, 'DtStamp', 'EndTime', 'Location', 'MeetingStatus', 'Reminder', 'ResponseType', 'Sensitivity', 'StartTime', 'Subject'); + + if ($exceptionElements->length > 0) { + $mainEventElement = $exceptionElements->item(0)->parentNode->parentNode; + + foreach ($mainEventElement->childNodes as $childNode) { + if (in_array($childNode->localName, $parentFields)) { + foreach ($exceptionElements as $exception) { + $elementsToLeftOut = $exception->getElementsByTagName($childNode->localName); + + foreach ($elementsToLeftOut as $elementToLeftOut) { + if ($elementToLeftOut->nodeValue == $childNode->nodeValue) { + $exception->removeChild($elementToLeftOut); + } + } + } + } + } + } } /** - * copy some fileds of the main event to the exception if they are missing - * these fields can be left out, if they have the same value in the main event - * and the exception + * some elements of an exception can be left out, if they have the same value + * like the main event + * + * this function copies these elements to the exception for backends which need + * this elements in the exceptions too. Tine 2.0 needs this for example. */ - protected function _copyFieldsFromParent() + public function copyFieldsFromParent() { if (isset($this->_elements['exceptions']) && is_array($this->_elements['exceptions'])) { foreach ($this->_elements['exceptions'] as $exception) { @@ -99,7 +112,7 @@ continue; } - $parentFields = array('allDayEvent', 'attendees', 'busyStatus', 'meetingStatus', 'sensitivity', 'subject'); + $parentFields = array('allDayEvent', 'attendees', 'body', 'busyStatus', 'categories', 'dtStamp', 'endTime', 'location', 'meetingStatus', 'reminder', 'responseType', 'sensitivity', 'startTime', 'subject'); foreach ($parentFields as $field) { if (!isset($exception->$field) && isset($this->_elements[$field])) {
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Model/EventException.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Model/EventException.php
Changed
@@ -27,6 +27,9 @@ protected $_dateTimeFormat = "Ymd\THis\Z"; protected $_properties = array( + 'AirSyncBase' => array( + 'body' => array('type' => 'container', 'class' => 'Syncroton_Model_EmailBody') + ), 'Calendar' => array( 'allDayEvent' => array('type' => 'number'), 'appointmentReplyTime' => array('type' => 'datetime'),
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Model/IContent.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Model/IContent.php
Changed
@@ -24,6 +24,5 @@ interface Syncroton_Model_IContent { - }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Model/ISyncState.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Model/ISyncState.php
Changed
@@ -21,6 +21,5 @@ interface Syncroton_Model_ISyncState { - }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Model/SyncCollection.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Model/SyncCollection.php
Changed
@@ -273,6 +273,11 @@ // optional if (isset($bodyPreference->TruncationSize)) { $this->_elements['options']['bodyPreferences'][$type]['truncationSize'] = (int) $bodyPreference->TruncationSize; + } + + // optional + if (isset($bodyPreference->Preview)) { + $this->_elements['options']['bodyPreferences'][$type]['preview'] = (int) $bodyPreference->Preview; } } }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Model/SyncState.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Model/SyncState.php
Changed
@@ -15,18 +15,7 @@ * @package Model */ -class Syncroton_Model_SyncState implements Syncroton_Model_ISyncState +class Syncroton_Model_SyncState extends Syncroton_Model_AEntry implements Syncroton_Model_ISyncState { - public function __construct(array $_data = array()) - { - $this->setFromArray($_data); - } - - public function setFromArray(array $_data) - { - foreach($_data as $key => $value) { - $this->$key = $value; - } - } }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Server.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Server.php
Changed
@@ -148,12 +148,12 @@ if ($this->_logger instanceof Zend_Log) $this->_logger->info(__METHOD__ . '::' . __LINE__ . " provisioning needed"); + header("HTTP/1.1 449 Retry after sending a PROVISION command"); + if (version_compare($device->acsversion, '14.0', '>=')) { $response = $sepn->domDocument; } else { // pre 14.0 method - header("HTTP/1.1 449 Retry after sending a PROVISION command"); - return; } @@ -184,7 +184,19 @@ $outputStream = fopen("php://temp", 'r+'); $encoder = new Syncroton_Wbxml_Encoder($outputStream, 'UTF-8', 3); - $encoder->encode($response); + + try { + $encoder->encode($response); + } catch (Syncroton_Wbxml_Exception $swe) { + if ($this->_logger instanceof Zend_Log) { + $this->_logger->err(__METHOD__ . '::' . __LINE__ . " Could not encode output: " . $swe); + $this->_logger->err(__METHOD__ . '::' . __LINE__ . " xml response:\n" . $response->saveXML()); + } + + header("HTTP/1.1 500 Internal server error"); + + return; + } if ($requestParameters['acceptMultipart'] == true) { $parts = $command->getParts();
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Wbxml/Abstract.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Wbxml/Abstract.php
Changed
@@ -151,26 +151,12 @@ protected function _getOpaque($_length) { - $string = ''; - - // it might happen that not complete data is read from stream. - // loop until all data is read or EOF - while ($_length) { - $chunk = fread($this->_stream, $_length); - - if ($chunk === false) { - throw new Syncroton_Wbxml_Exception("failed reading opaque data"); - } - - if ($len = strlen($chunk)) { - $string .= $chunk; - $_length -= $len; - } - else if (feof($this->_stream)) { - break; - } + $string = fread($this->_stream, $_length); + + if($string === false) { + throw new Syncroton_Wbxml_Exception("failed reading opaque data"); } - + return $string; }
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Wbxml/Decoder.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Wbxml/Decoder.php
Changed
@@ -129,8 +129,6 @@ case Syncroton_Wbxml_Abstract::OPAQUE: $length = $this->_getMultibyteUInt(); if($length > 0) { - // @TODO: handle big data with streams - // E.g. in SendMail command "opaqued" <Mime> contains full email body $opaque = $this->_getOpaque($length); try { // let see if we can decode it. maybe the opaque data is wbxml encoded content
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Wbxml/Dtd/ActiveSync/CodePage14.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Wbxml/Dtd/ActiveSync/CodePage14.php
Changed
@@ -49,5 +49,34 @@ 'AllowStorageCard' => 0x1b, 'AllowCamera' => 0x1c, 'RequireDeviceEncryption' => 0x1d, + 'AllowUnsignedApplications' => 0x1e, + 'AllowUnsignedInstallationPackages' => 0x1f, + 'MinDevicePasswordComplexCharacters' => 0x20, + 'AllowWiFi' => 0x21, + 'AllowTextMessaging' => 0x22, + 'AllowPOPIMAPEmail' => 0x23, + 'AllowBluetooth' => 0x24, + 'AllowIrDA' => 0x25, + 'RequireManualSyncWhenRoaming' => 0x26, + 'AllowDesktopSync' => 0x27, + 'MaxCalendarAgeFilter' => 0x28, + 'AllowHTMLEmail' => 0x29, + 'MaxEmailAgeFilter' => 0x2a, + 'MaxEmailBodyTruncationSize' => 0x2b, + 'MaxEmailHTMLBodyTruncationSize' => 0x2c, + 'RequireSignedSMIMEMessages' => 0x2d, + 'RequireEncryptedSMIMEMessages' => 0x2e, + 'RequireSignedSMIMEAlgorithm' => 0x2F, + 'RequireEncryptionSMIMEAlgorithm' => 0x30, + 'AllowSMIMEEncryptionAlgorithmNegotiation' => 0x31, + 'AllowSMIMESoftCerts' => 0x32, + 'AllowBrowser' => 0x33, + 'AllowConsumerEmail' => 0x34, + 'AllowRemoteDesktop' => 0x35, + 'AllowInternetSharing' => 0x36, + 'UnapprovedInROMApplicationList' => 0x37, + 'ApplicationName' => 0x38, + 'ApprovedApplicationList' => 0x39, + 'Hash' => 0x3a, ); -} \ No newline at end of file +}
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/Syncroton/Wbxml/Dtd/ActiveSync/CodePage9.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/Syncroton/Wbxml/Dtd/ActiveSync/CodePage9.php
Changed
@@ -54,8 +54,8 @@ 'Rtf' => 0x21, 'OrdinalDate' => 0x22, 'SubOrdinalDate' => 0x23, - 'CalendarType' => 0x23, - 'IsLeapMonth' => 0x23, - 'FirstDayOfWeek' => 0x23 + 'CalendarType' => 0x24, + 'IsLeapMonth' => 0x25, + 'FirstDayOfWeek' => 0x26, ); } \ No newline at end of file
View file
kolab-syncroton-2.1.0.tar.gz/lib/ext/tnef_decoder.php -> kolab-syncroton-2.2.0.tar.gz/lib/ext/tnef_decoder.php
Changed
@@ -243,16 +243,16 @@ /* Store any interesting attributes. */ switch ($attr_name) { case self::MAPI_ATTACH_LONG_FILENAME: + $value = str_replace("\0", '', $value); /* Used in preference to AFILENAME value. */ $attachment_data[0]['name'] = preg_replace('/.*[\/](.*)$/', '\1', $value); - $attachment_data[0]['name'] = str_replace("\0", '', $attachment_data[0]['name']); break; case self::MAPI_ATTACH_MIME_TAG: + $value = str_replace("\0", '', $value); /* Is this ever set, and what is format? */ - $attachment_data[0]['type'] = preg_replace('/^(.*)\/.*/', '\1', $value); + $attachment_data[0]['type'] = preg_replace('/^(.*)\/.*/', '\1', $value); $attachment_data[0]['subtype'] = preg_replace('/.*\/(.*)$/', '\1', $value); - $attachment_data[0]['subtype'] = str_replace("\0", '', $attachment_data[0]['subtype']); break; } } @@ -295,9 +295,10 @@ break; case self::AFILENAME: + $value = $this->_getx($data, $this->_geti($data, 32)); + $value = str_replace("\0", '', $value); /* Strip path. */ - $attachment_data[0]['name'] = preg_replace('/.*[\/](.*)$/', '\1', $this->_getx($data, $this->_geti($data, 32))); - $attachment_data[0]['name'] = str_replace("\0", '', $attachment_data[0]['name']); + $attachment_data[0]['name'] = preg_replace('/.*[\/](.*)$/', '\1', $value); /* Checksum */ $this->_geti($data, 16);
View file
kolab-syncroton-2.1.0.tar.gz/lib/kolab_sync.php -> kolab-syncroton-2.2.0.tar.gz/lib/kolab_sync.php
Changed
@@ -43,7 +43,7 @@ public $user; const CHARSET = 'UTF-8'; - const VERSION = "2.1.0"; + const VERSION = "2.2.0"; /** @@ -75,8 +75,8 @@ // WARNING: We can use only plugins that are prepared for this // e.g. are not using output or rcmail objects or // doesn't throw errors when using them - $plugins = (array)$this->config->get('activesync_plugins', array('kolab_auth', 'kolab_folders')); - $required = array('libkolab', 'kolab_folders'); + $plugins = (array)$this->config->get('activesync_plugins', array('kolab_auth')); + $required = array('libkolab'); // Initialize/load plugins $this->plugins = kolab_sync_plugin_api::get_instance(); @@ -172,16 +172,34 @@ */ public function authenticate($username, $password) { - $auth = $this->plugins->exec_hook('authenticate', array( - 'host' => $this->select_host($username), - 'user' => $username, - 'pass' => $password, - 'valid' => true, - )); + // use shared cache for kolab_auth plugin result (username canonification) + $cache = $this->get_cache_shared('activesync_auth'); + $cache_key = md5($username . '::' . $password); + + if (!$cache || !($auth = $cache->get($cache_key))) { + $auth = $this->plugins->exec_hook('authenticate', array( + 'host' => $this->select_host($username), + 'user' => $username, + 'pass' => $password, + 'valid' => true, + )); + + if ($auth['valid'] && $cache) { + $cache->set($cache_key, array( + 'user' => $auth['user'], + 'host' => $auth['host'], + 'valid' => $auth['valid'], + )); + } + } + else { + $auth['pass'] = $password; + } // Authenticate - get Roundcube user ID if ($auth['valid'] && !$auth['abort'] - && ($userid = $this->login($auth['user'], $auth['pass'], $auth['host']))) { + && ($userid = $this->login($auth['user'], $auth['pass'], $auth['host'])) + ) { return $userid; } @@ -350,6 +368,11 @@ } $this->config->set('log_dir', $log_dir); + + // re-set PHP error logging + if (($this->config->get('debug_level') & 1) && $this->config->get('log_driver') != 'syslog') { + ini_set('error_log', $log_dir . '/errors'); + } } @@ -360,6 +383,9 @@ { parent::shutdown(); + // cache garbage collector + $this->gc_run(); + // write performance stats to logs/console if ($this->config->get('devel_mode')) { if (function_exists('memory_get_usage'))
View file
kolab-syncroton-2.1.0.tar.gz/lib/kolab_sync_backend.php -> kolab-syncroton-2.2.0.tar.gz/lib/kolab_sync_backend.php
Changed
@@ -171,9 +171,10 @@ } // Activesync folder identifier (serverId) - $folder_id = self::folder_id($folder, $typedata[$folder]); + $folder_type = $typedata[$folder]; + $folder_id = self::folder_id($folder, $folder_type); - $folders_list[$folder_id] = $this->folder_data($folder, $typedata[$folder]); + $folders_list[$folder_id] = $this->folder_data($folder, $folder_type); } return $folders_list; @@ -371,6 +372,12 @@ // Fill local cache $this->devices_list(); + // Some devices create dummy devices with name "validate" (#1109) + // This device entry is used in two initial requests, but later + // the device registers a real name. We can remove this dummy entry + // on new device creation + $this->device_delete('validate'); + // Old Kolab_ZPush device parameters // MODE: -1 | 0 | 1 (not set | flatmode | foldermode) // TYPE: device type string @@ -392,30 +399,33 @@ $this->root_meta['DEVICE'][$id] = $device; // Subscribe to default folders - $foldertypes = $this->storage->get_metadata('*', array(kolab_storage::CTYPE_KEY, kolab_storage::CTYPE_KEY_PRIVATE)); - $types = array( - 'mail.drafts', - 'mail.wastebasket', - 'mail.sentitems', - 'mail.outbox', - 'event.default', - 'contact.default', - 'task.default', - 'event', - 'contact', - 'task' - ); - - $foldertypes = array_map(array('kolab_storage', 'folder_select_metadata'), $foldertypes); - $foldertypes = array_intersect($foldertypes, $types); - - // get default folders - foreach ($foldertypes as $folder => $type) { - // only personal folders - if ($this->storage->folder_namespace($folder) == 'personal') { - $this->folder_set($folder, $id, 1); + $foldertypes = kolab_storage::folders_typedata(); + + if (!empty($foldertypes)) { + $types = array( + 'mail.drafts', + 'mail.wastebasket', + 'mail.sentitems', + 'mail.outbox', + 'event.default', + 'contact.default', + 'task.default', + 'event', + 'contact', + 'task' + ); + + $foldertypes = array_intersect($foldertypes, $types); + + // get default folders + foreach ($foldertypes as $folder => $type) { + // only personal folders + if ($this->storage->folder_namespace($folder) == 'personal') { + $this->folder_set($folder, $id, 1); + } } } + // INBOX always exists $this->folder_set('INBOX', $id, 1); } @@ -423,7 +433,14 @@ return $result; } - + /** + * Device update. + * + * @param array $device Device data + * @param string $id Device ID + * + * @return bool True on success, False on failure + */ public function device_update($device, $id) { $devices_list = $this->devices_list(); @@ -593,7 +610,7 @@ $name = array_pop($items); // Folder UID - $folder_id = $this->folder_id($folder); + $folder_id = $this->folder_id($folder, $type); // Folder type $type = self::type_kolab2activesync($type);
View file
kolab-syncroton-2.1.0.tar.gz/lib/kolab_sync_backend_common.php -> kolab-syncroton-2.2.0.tar.gz/lib/kolab_sync_backend_common.php
Changed
@@ -56,6 +56,13 @@ */ protected $db; + /** + * Internal cache (in-memory) + * + * @var array + */ + protected $cache = array(); + /** * Constructor
View file
kolab-syncroton-2.1.0.tar.gz/lib/kolab_sync_backend_content.php -> kolab-syncroton-2.2.0.tar.gz/lib/kolab_sync_backend_content.php
Changed
@@ -44,7 +44,11 @@ $result = $this->db->query('UPDATE ' . $this->table_name . ' SET is_deleted = 1 WHERE id = ?', array($id)); - return (bool) $this->db->affected_rows($result); + if ($result = (bool) $this->db->affected_rows($result)) { + unset($this->cache['content_folderstate']); + } + + return $result; } /** @@ -84,6 +88,13 @@ { $deviceId = $_deviceId instanceof Syncroton_Model_IDevice ? $_deviceId->id : $_deviceId; $folderId = $_folderId instanceof Syncroton_Model_IFolder ? $_folderId->id : $_folderId; + $cachekey = $deviceId . ':' . $folderId; + + // in Sync request we call this function twice in case when + // folder state changed - use cache to skip at least one SELECT query + if (isset($this->cache['content_folderstate'][$cachekey])) { + return $this->cache['content_folderstate'][$cachekey]; + } $where[] = $this->db->quote_identifier('device_id') . ' = ' . $this->db->quote($deviceId); $where[] = $this->db->quote_identifier('folder_id') . ' = ' . $this->db->quote($folderId); @@ -96,7 +107,7 @@ $result[] = $state['contentid']; } - return $result; + return $this->cache['content_folderstate'][$cachekey] = $result; } /** @@ -109,6 +120,9 @@ { $deviceId = $_deviceId instanceof Syncroton_Model_IDevice ? $_deviceId->id : $_deviceId; $folderId = $_folderId instanceof Syncroton_Model_IFolder ? $_folderId->id : $_folderId; + $cachekey = $deviceId . ':' . $folderId; + + unset($this->cache['content_folderstate'][$cache_key]); $where[] = $this->db->quote_identifier('device_id') . ' = ' . $this->db->quote($deviceId); $where[] = $this->db->quote_identifier('folder_id') . ' = ' . $this->db->quote($folderId);
View file
kolab-syncroton-2.1.0.tar.gz/lib/kolab_sync_backend_state.php -> kolab-syncroton-2.2.0.tar.gz/lib/kolab_sync_backend_state.php
Changed
@@ -150,15 +150,13 @@ { $device_id = $deviceid instanceof Syncroton_Model_IDevice ? $deviceid->id : $deviceid; $folder_id = $folderid instanceof Syncroton_Model_IFolder ? $folderid->id : $folderid; + $states = array(); - // get sync data for current request and check if it's the last one - // by fetching synckey+1 - $states = array(); - $keys = array($sync_key, $sync_key + 1); - + // get sync data + // we'll get all records, thanks to this we'll be able to + // skip _deleteOtherStates() call below (one DELETE query less) $where['device_id'] = $this->db->quote_identifier('device_id') . ' = ' . $this->db->quote($device_id); $where['type'] = $this->db->quote_identifier('type') . ' = ' . $this->db->quote($folder_id); - $where['counter'] = $this->db->quote_identifier('counter') . ' IN (' . $this->db->array2list($keys, 'int') . ')'; $select = $this->db->query('SELECT * FROM ' . $this->table_name .' WHERE ' . implode(' AND ', $where)); @@ -166,19 +164,21 @@ $states[$row['counter']] = $this->get_object($row); } + // last state not found if (empty($states) || empty($states[$sync_key])) { return false; } $state = $states[$sync_key]; + $next = max(array_keys($states)); $where = array(); $where['device_id'] = $this->db->quote_identifier('device_id') . ' = ' . $this->db->quote($device_id); $where['folder_id'] = $this->db->quote_identifier('folder_id') . ' = ' . $this->db->quote($folder_id); $where['is_deleted'] = $this->db->quote_identifier('is_deleted') . ' = 1'; - // found more recent synckey => the last sync repsonse got not received by the client - if (!empty($states[$sync_key + 1])) { + // found more recent synckey => the last sync response got not received by the client + if ($next > $sync_key) { $where['synckey'] = $this->db->quote_identifier('creation_synckey') . ' = ' . $this->db->quote($state->counter); // undelete entries marked as deleted in syncroton_content table $this->db->query('UPDATE syncroton_content SET is_deleted = 0 WHERE ' . implode(' AND ', $where)); @@ -195,7 +195,9 @@ } // remove all other synckeys - $this->_deleteOtherStates($state); + if (count($states) > 1) { + $this->_deleteOtherStates($state); + } return $state; }
View file
kolab-syncroton-2.1.0.tar.gz/lib/kolab_sync_data.php -> kolab-syncroton-2.2.0.tar.gz/lib/kolab_sync_data.php
Changed
@@ -85,12 +85,32 @@ protected $folders = array(); /** + * Internal cache for IMAP folders list + * + * @var array + */ + protected $imap_folders = array(); + + /** * Timezone * * @var string */ protected $timezone; + /** + * List of device types with multiple folders support + * + * @var array + */ + protected $ext_devices = array( + 'iphone', + 'ipad', + 'thundertine', + 'windowsphone', + 'playbook', + ); + const RESULT_OBJECT = 0; const RESULT_UID = 1; const RESULT_COUNT = 2; @@ -186,9 +206,9 @@ $list = array(); // device supports multiple folders ? - if (in_array(strtolower($this->device->devicetype), array('iphone', 'ipad', 'thundertine', 'windowsphone'))) { + if (in_array(strtolower($this->device->devicetype), $this->ext_devices)) { // get the folders the user has access to - $list = $this->backend->folders_list($this->device->deviceid, $this->modelName); + $list = $this->listFolders(); } else if ($default = $this->getDefaultFolder()) { $list = array($default['serverId'] => $default); @@ -208,12 +228,25 @@ } /** + * Retrieve folders which were modified since last sync + * + * @param DateTime $startTimeStamp + * @param DateTime $endTimeStamp + * + * @return array List of folders + */ + public function getChangedFolders(DateTime $startTimeStamp, DateTime $endTimeStamp) + { + return array(); + } + + /** * Returns default folder for current class type. */ protected function getDefaultFolder() { // Check if there's any folder configured for sync - $folders = $this->backend->folders_list($this->device->deviceid, $this->modelName); + $folders = $this->listFolders(); if (empty($folders)) { return $folders; @@ -331,6 +364,47 @@ } /** + * Empty folder (remove all entries and optionally subfolders) + * + * @param string $folderId Folder identifier + * @param array $options Options + */ + public function emptyFolderContents($folderid, $options) + { + $folders = $this->extractFolders($folderid); + + foreach ($folders as $folderid) { + $foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid); + + if ($foldername === null) { + continue; + } + + $folder = $this->getFolderObject($foldername); + + // Remove all entries + $folder->delete_all(); + + // Remove subfolders + if (!empty($options['deleteSubFolders'])) { + $list = $this->listFolders($folderid); + foreach ($list as $folderid => $folder) { + $foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid); + + if ($foldername === null) { + continue; + } + + $folder = $this->getFolderObject($foldername); + + // Remove all entries + $folder->delete_all(); + } + } + } + } + + /** * Moves object into another location (folder) * * @param string $srcFolderId Source folder identifier @@ -444,7 +518,7 @@ protected function searchEntries($folderid, $filter = array(), $result_type = self::RESULT_UID) { if ($folderid == $this->defaultRootFolder) { - $folders = $this->backend->folders_list($this->device->deviceid, $this->modelName); + $folders = $this->listFolders(); if (!is_array($folders)) { throw new Syncroton_Exception_Status(Syncroton_Exception_Status::SERVER_ERROR); @@ -644,22 +718,7 @@ */ protected function getObject($folderid, $entryid, &$folder = null) { - if ($folderid instanceof Syncroton_Model_IFolder) { - $folderid = $folderid->serverId; - } - - if ($folderid == $this->defaultRootFolder) { - $folders = $this->backend->folders_list($this->device->deviceid, $this->modelName); - - if (!is_array($folders)) { - return null; - } - - $folders = array_keys($folders); - } - else { - $folders = array($folderid); - } + $folders = $this->extractFolders($folderid); foreach ($folders as $folderid) { $foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid); @@ -728,6 +787,69 @@ $folder = $this->getFolderObject($object['_mailbox']); return $folder && $folder->delete($entryid); } + + // object doesn't exist, confirm deletion + return true; + } + + /** + * Returns internal folder IDs + * + * @param string $folderid Folder identifier + * + * @return array List of folder identifiers + */ + protected function extractFolders($folderid) + { + if ($folderid instanceof Syncroton_Model_IFolder) { + $folderid = $folderid->serverId; + } + + if ($folderid == $this->defaultRootFolder) { + $folders = $this->listFolders(); + + if (!is_array($folders)) { + return null; + } + + $folders = array_keys($folders); + } + else { + $folders = array($folderid); + } + + return $folders; + } + + /** + * List of all IMAP folders (or subtree) + * + * @param string $parentid Parent folder identifier + * + * @return array List of folder identifiers + */ + protected function listFolders($parentid = null) + {
View file
kolab-syncroton-2.1.0.tar.gz/lib/kolab_sync_data_calendar.php -> kolab-syncroton-2.2.0.tar.gz/lib/kolab_sync_data_calendar.php
Changed
@@ -173,8 +173,11 @@ * * @param Syncroton_Model_SyncCollection $collection Collection data * @param string $serverId Local entry identifier + * @param boolean $as_array Return entry as array + * + * @return array|Syncroton_Model_Event|array Event object */ - public function getEntry(Syncroton_Model_SyncCollection $collection, $serverId) + public function getEntry(Syncroton_Model_SyncCollection $collection, $serverId, $as_array = false) { $event = is_array($serverId) ? $serverId : $this->getObject($collection->collectionId, $serverId); $config = $this->getFolderConfig($event['_mailbox']); @@ -217,6 +220,11 @@ } } + // set this date for use in exceptions handling + if ($name == 'start') { + $event['_start'] = $date; + } + $value = self::date_from_kolab($date); } @@ -247,6 +255,9 @@ $result['reminder'] = $minutes; } + $result['categories'] = array(); + $result['attendees'] = array(); + // Categories, Roundcube Calendar plugin supports only one category at a time if (!empty($event['categories'])) { $result['categories'] = (array) $event['categories']; @@ -272,8 +283,6 @@ // Attendees if (!empty($event['attendees'])) { - $result['attendees'] = array(); - foreach ($event['attendees'] as $idx => $attendee) { $att = array(); @@ -294,26 +303,15 @@ $result['attendees'][] = new Syncroton_Model_EventAttendee($att); } -/* - // set own status - if (($ownAttendee = Calendar_Model_Attender::getOwnAttender($event->attendee)) !== null - && ($busyType = array_search($ownAttendee->status, $this->_busyStatusMapping)) !== false - ) { - $result['BusyStatus'] = $busyType; - } -*/ } // Event meeting status $result['meetingStatus'] = intval(!empty($result['attendees'])); - // Recurrence - $result['recurrence'] = $this->recurrence_from_kolab($event); - - // Recurrence exceptions - $result['exceptions'] = $this->exceptions_from_kolab($event, $result['startTime']); + // Recurrence (and exceptions) + $this->recurrence_from_kolab($collection, $event, $result); - return new Syncroton_Model_Event($result); + return $as_array ? $result : new Syncroton_Model_Event($result); } /** @@ -322,19 +320,21 @@ * @param Syncroton_Model_IEntry $data Contact to convert * @param string $folderid Folder identifier * @param array $entry Existing entry + * @param DateTimeZone $timezone Timezone of the event * * @return array */ - public function toKolab(Syncroton_Model_IEntry $data, $folderid, $entry = null) + public function toKolab(Syncroton_Model_IEntry $data, $folderid, $entry = null, $timezone = null) { - $foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid); - $event = !empty($entry) ? $entry : array(); - $config = $this->getFolderConfig($foldername); + $foldername = $this->backend->folder_id2name($folderid, $this->device->deviceid); + $event = !empty($entry) ? $entry : array(); + $config = $this->getFolderConfig($foldername); + $is_exception = $data instanceof Syncroton_Model_EventException; $event['allday'] = 0; // Timezone - if (isset($data->timezone)) { + if (!$timezone && isset($data->timezone)) { $tzc = kolab_sync_timezone_converter::getInstance(); $expected = kolab_format::$timezone->getName(); @@ -356,6 +356,12 @@ // Calendar namespace fields foreach ($this->mapping as $key => $name) { + // skip UID field, unsupported in event exceptions + // we need to do this here, because the next line (data getter) will throw an exception + if ($is_exception && $key == 'uID') { + continue; + } + $value = $data->$key; switch ($name) { @@ -432,14 +438,16 @@ } // Organizer - $name = $data->organizerName; - $email = $data->organizerEmail; - if ($name || $email) { - $event['attendees'][] = array( - 'role' => 'ORGANIZER', - 'name' => $name, - 'email' => $email, - ); + if (!$is_exception) { + $name = $data->organizerName; + $email = $data->organizerEmail; + if ($name || $email) { + $event['attendees'][] = array( + 'role' => 'ORGANIZER', + 'name' => $name, + 'email' => $email, + ); + } } // Attendees @@ -463,12 +471,9 @@ } } - // recurrence - $event['recurrence'] = $this->recurrence_to_kolab($data->recurrence, $timezone); - - // recurrence exceptions - if ($exdate = $this->exceptions_to_kolab($data->exceptions, $timezone)) { - $event['recurrence']['EXDATE'] = $exdate; + // recurrence (and exceptions) + if (!$is_exception) { + $event['recurrence'] = $this->recurrence_to_kolab($data, $folderid, $timezone); } return $event;
View file
kolab-syncroton-2.1.0.tar.gz/lib/kolab_sync_data_email.php -> kolab-syncroton-2.2.0.tar.gz/lib/kolab_sync_data_email.php
Changed
@@ -103,6 +103,9 @@ parent::__construct($device, $syncTimeStamp); $this->storage = rcube::get_instance()->get_storage(); + + // Outlook 2013 support multi-folder + $this->ext_devices[] = 'windowsoutlook15'; } /** @@ -141,14 +144,9 @@ $addresses = rcube_mime::decode_address_list($headers->$name, null, true, $headers->charset); foreach ($addresses as $idx => $part) { - $name = $part['name']; - $mailto = $part['mailto']; - $string = $part['string']; - - // @TODO: convert to utf8? - // @FIXME: set name + address or address only? - //rcube_utils::idn_to_utf8(); - $addresses[$idx] = format_email_recipient($mailto, $name); + // @FIXME: set name + address or address only? + $addresses[$idx] = format_email_recipient($part['mailto'], $part['name']); + $addresses[$idx] = rcube_charset::clean($addresses[$idx]); } $value = implode(',', $addresses); @@ -301,7 +299,7 @@ // original body type // @TODO: get this value from getMessageBody() - $result['nativeBodyType'] = $message->has_html_part(false) ? 2 : 1; + $result['nativeBodyType'] = $message->has_html_part() ? 2 : 1; // Message class // @TODO: add messageClass suffix for encrypted messages @@ -418,14 +416,14 @@ */ public function getAllFolders() { - $list = $this->backend->folders_list($this->device->deviceid, $this->modelName); + $list = $this->listFolders(); if (!is_array($list)) { throw new Syncroton_Exception_Status_FolderSync(Syncroton_Exception_Status_FolderSync::FOLDER_SERVER_ERROR); } // device doesn't support multiple folders - if (!in_array(strtolower($this->device->devicetype), array('iphone', 'ipad', 'thundertine', 'windowsphone'))) { + if (!in_array(strtolower($this->device->devicetype), $this->ext_devices)) { // We'll return max. one folder of supported type $result = array(); $types = $this->folder_types; @@ -459,7 +457,7 @@ */ protected function extractFolders($folder_id) { - $list = $this->backend->folders_list($this->device->deviceid, $this->modelName); + $list = $this->listFolders(); $result = array(); if (!is_array($list)) { @@ -467,7 +465,7 @@ } // device supports multiple folders? - if (in_array(strtolower($this->device->devicetype), array('iphone', 'ipad', 'thundertine', 'windowsphone'))) { + if (in_array(strtolower($this->device->devicetype), $this->ext_devices)) { if ($list[$folder_id]) { $result[] = $folder_id; } @@ -1004,7 +1002,7 @@ // @TODO: DeepTraversal if (empty($folders)) { - $folders = $this->backend->folders_list($this->device->deviceid, $this->modelName); + $folders = $this->listFolders(); if (is_array($folders)) { $folders = array_keys($folders);
View file
kolab-syncroton-2.1.0.tar.gz/lib/kolab_sync_data_tasks.php -> kolab-syncroton-2.2.0.tar.gz/lib/kolab_sync_data_tasks.php
Changed
@@ -102,8 +102,11 @@ * * @param Syncroton_Model_SyncCollection $collection Collection data * @param string $serverId Local entry identifier + * @param boolean $as_array Return entry as an array + * + * @return array|Syncroton_Model_Task|array Task object */ - public function getEntry(Syncroton_Model_SyncCollection $collection, $serverId) + public function getEntry(Syncroton_Model_SyncCollection $collection, $serverId, $as_array = false) { $task = is_array($serverId) ? $serverId : $this->getObject($collection->collectionId, $serverId); $config = $this->getFolderConfig($task['_mailbox']); @@ -149,9 +152,9 @@ } // Recurrence - $result['recurrence'] = $this->recurrence_from_kolab($task, 'Task'); + $this->recurrence_from_kolab($collection, $task, $result, 'Task'); - return new Syncroton_Model_Task($result); + return $as_array ? $result : new Syncroton_Model_Task($result); } /** @@ -203,7 +206,7 @@ } // recurrence - $task['recurrence'] = $this->recurrence_to_kolab($data->recurrence); + $task['recurrence'] = $this->recurrence_to_kolab($data, $folderid, null); return $task; }
View file
kolab-syncroton-2.1.0.tar.gz/lib/kolab_sync_message.php -> kolab-syncroton-2.2.0.tar.gz/lib/kolab_sync_message.php
Changed
@@ -159,10 +159,9 @@ { $rcube = rcube::get_instance(); $headers = $this->headers; + $mailto = $headers['To']; - $mailto = $headers['To']; - - $headers['User-Agent'] .= sprintf('%s v.%.1f', $rcube->app_name, kolab_sync::VERSION); + $headers['User-Agent'] .= $rcube->app_name . ' ' . kolab_sync::VERSION; if ($agent = $rcube->config->get('useragent')) { $headers['User-Agent'] .= '/' . $agent; } @@ -170,8 +169,9 @@ if (empty($headers['From'])) { $headers['From'] = $this->get_identity(); } + if (empty($headers['Message-ID'])) { - $headers['Message-ID'] = $this->gen_message_id(); + $headers['Message-ID'] = $rcube->gen_message_id(); } // remove empty headers @@ -275,6 +275,7 @@ return $sent; } + /** * MIME message parser * @@ -333,6 +334,9 @@ $this->body = $message; } + /** + * Normalize (fix) header names + */ protected function normalize_header_name($name) { $headers_map = array( @@ -411,30 +415,6 @@ } } - /** - * Unique Message-ID generator. - * - * @return string Message-ID - */ - protected function gen_message_id() - { - $user = kolab_sync::get_instance()->user; - $local_part = md5(uniqid('rcmail'.mt_rand(),true)); - $domain_part = $user->get_username('domain'); - - // Try to find FQDN, some spamfilters doesn't like 'localhost' (#1486924) - if (!preg_match('/\.[a-z]+$/i', $domain_part)) { - foreach (array($_SERVER['HTTP_HOST'], $_SERVER['SERVER_NAME']) as $host) { - $host = preg_replace('/:[0-9]+$/', '', $host); - if ($host && preg_match('/\.[a-z]+$/i', $host)) { - $domain_part = $host; - } - } - } - - return sprintf('<%s@%s>', $local_part, $domain_part); - } - protected function save_content_type($ctype, $params = array()) { $this->ctype = $ctype;
View file
kolab-syncroton-2.1.0.tar.gz/lib/kolab_sync_timezone_converter.php -> kolab-syncroton-2.2.0.tar.gz/lib/kolab_sync_timezone_converter.php
Changed
@@ -80,11 +80,10 @@ * first entry to the returned array. * * @param string|array $_offsets - * @param string $_expectedTimezone Expected timezone * * @return array */ - public function getListOfTimezones($_offsets, $_expectedTimezone = null) + public function getListOfTimezones($_offsets) { if (is_string($_offsets) && isset($this->_knownTimezones[$_offsets])) { $timezones = $this->_knownTimezones[$_offsets]; @@ -100,7 +99,6 @@ } $this->_setDefaultStartDateIfEmpty($_offsets); - // don't use __METHOD__ ":" is not allowed as cache identifier $cacheId = $this->_getCacheId('timezones', $_offsets); $timezones = $this->_loadFromCache($cacheId); @@ -109,13 +107,7 @@ foreach (DateTimeZone::listIdentifiers() as $timezoneIdentifier) { $timezone = new DateTimeZone($timezoneIdentifier); if (false !== ($matchingTransition = $this->_checkTimezone($timezone, $_offsets))) { - if ($timezoneIdentifier === $_expectedTimezone) { - $timezones = array($timezoneIdentifier => $matchingTransition['abbr']); - break; - } - else { - $timezones[$timezoneIdentifier] = $matchingTransition['abbr']; - } + $timezones[$timezoneIdentifier] = $matchingTransition['abbr']; } } $this->_saveInCache($timezones, $cacheId); @@ -143,13 +135,13 @@ */ public function getTimezone($_offsets, $_expectedTimezone = null) { - $timezones = $this->getListOfTimezones($_offsets, $_expectedTimezone); + $timezones = $this->getListOfTimezones($_offsets); if ($_expectedTimezone && isset($timezones[$_expectedTimezone])) { return $_expectedTimezone; } else { - return current($timezones); + return key($timezones); } } @@ -536,12 +528,11 @@ */ protected function _checkTimezone(DateTimeZone $timezone, $offsets) { -// $this->_log(__METHOD__, __LINE__, 'Checking for matches with timezone: ' . $timezone->getName()); list($standardTransition, $daylightTransition) = $this->_getTransitionsForTimezoneAndYear($timezone, $this->_startDate['year']); if ($this->_checkTransition($standardTransition, $daylightTransition, $offsets)) { - //$this->_log(__METHOD__, __LINE__, 'Matching timezone ' . $timezone->getName(), 7); - //$this->_log(__METHOD__, __LINE__, 'Matching daylight transition ' . print_r($daylightTransition, 1), 7); - //$this->_log(__METHOD__, __LINE__, 'Matching standard transition ' . print_r($standardTransition, 1), 7); +// echo 'Matching timezone ' . $timezone->getName(); +// echo 'Matching daylight transition ' . print_r($daylightTransition, 1); +// echo 'Matching standard transition ' . print_r($standardTransition, 1); return $standardTransition; } @@ -620,17 +611,10 @@ { if ($this->cache === null) { $rcube = rcube::get_instance(); - - if ($cache = $rcube->config->get('activesync_cache')) { - $ttl = $rcube->config->get('activesync_cache_lifetime', '10d'); - $this->cache = $rcube->get_cache('ACTIVESYNC', $cache, $ttl); - } - else { - $this->cache = false; - } + $cache = $rcube->get_cache_shared('activesync'); + $this->cache = $cache ? $cache : false; } return $this->cache; } - }
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/kolab_auth/config.inc.php.dist -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_auth/config.inc.php.dist
Changed
@@ -2,6 +2,17 @@ // The id of the LDAP address book (which refers to the $rcmail_config['ldap_public']) // or complete addressbook definition array. +// -------------------------------------------------------------------- +// Note: Multi-domain (hosted) installations can resolve domain aliases +// by adding following settings in kolab_auth_addressbook spec.: +// +// 'domain_base_dn' => 'cn=kolab,cn=config', +// 'domain_filter' => '(&(objectclass=domainrelatedobject)(associateddomain=%s))', +// 'domain_name_attr' => 'associateddomain', +// +// With this %dc variable in base_dn and groups/base_dn will be +// replaced with DN string of resolved domain +//--------------------------------------------------------------------- $rcmail_config['kolab_auth_addressbook'] = ''; // This will overwrite defined filter @@ -14,8 +25,9 @@ // If the value array contains more than one field, first non-empty will be used // Note: These aren't LDAP attributes, but field names in config // Note: If there's more than one email address, as many identities will be created -$rcmail_config['kolab_auth_name'] = array('name', 'cn'); -$rcmail_config['kolab_auth_email'] = array('email'); +$rcmail_config['kolab_auth_name'] = array('name', 'cn'); +$rcmail_config['kolab_auth_email'] = array('email'); +$rcmail_config['kolab_auth_organization'] = array('organization'); // Login and password of the admin user. Enables "Login As" feature. $rcmail_config['kolab_auth_admin_login'] = '';
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/kolab_auth/kolab_auth.php -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_auth/kolab_auth.php
Changed
@@ -12,7 +12,7 @@ * @version @package_version@ * @author Aleksander Machniak <machniak@kolabsys.com> * - * Copyright (C) 2011, Kolab Systems AG <contact@kolabsys.com> + * Copyright (C) 2011-2012, 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 @@ -30,7 +30,7 @@ class kolab_auth extends rcube_plugin { - private $ldap; + static $ldap; private $data = array(); public function init() @@ -61,12 +61,12 @@ $rcmail->config->set('imap_debug', true); $rcmail->config->set('ldap_debug', true); $rcmail->config->set('smtp_debug', true); - } } - public function startup($args) { + public function startup($args) + { // Arguments are task / action, not interested if (!empty($_SESSION['user_roledns'])) { $this->load_user_role_plugins_and_settings($_SESSION['user_roledns']); @@ -75,7 +75,8 @@ return $args; } - public function load_user_role_plugins_and_settings($role_dns) { + public function load_user_role_plugins_and_settings($role_dns) + { $rcmail = rcube::get_instance(); $this->load_config(); @@ -86,6 +87,8 @@ // 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'); @@ -100,9 +103,29 @@ // ), // ), // ); + // + // 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) { @@ -151,7 +174,8 @@ } } - public function write_log($args) { + public function write_log($args) + { $rcmail = rcube::get_instance(); if (!$rcmail->config->get('kolab_auth_auditlog', false)) { @@ -208,7 +232,19 @@ if (!empty($this->data['user_email'])) { // addresses list is supported if (array_key_exists('email_list', $args)) { - $args['email_list'] = array_unique($this->data['user_email']); + $email_list = array_unique($this->data['user_email']); + + // add organization to the list + if (!empty($this->data['user_organization'])) { + foreach ($email_list as $idx => $email) { + $email_list[$idx] = array( + 'organization' => $this->data['user_organization'], + 'email' => $email, + ); + } + } + + $args['email_list'] = $email_list; } else { $args['user_email'] = $this->data['user_email'][0]; @@ -256,22 +292,8 @@ */ public function authenticate($args) { - $this->load_config(); - - if (!$this->init_ldap()) { - $args['abort'] = true; - return $args; - } - - $rcmail = rcube::get_instance(); - $admin_login = $rcmail->config->get('kolab_auth_admin_login'); - $admin_pass = $rcmail->config->get('kolab_auth_admin_password'); - $login_attr = $rcmail->config->get('kolab_auth_login'); - $name_attr = $rcmail->config->get('kolab_auth_name'); - $email_attr = $rcmail->config->get('kolab_auth_email'); - // get username and host - $host = rcube_utils::parse_host($args['host']); + $host = $args['host']; $user = $args['user']; $pass = $args['pass']; $loginas = trim(rcube_utils::get_input_value('_loginas', rcube_utils::INPUT_POST)); @@ -281,15 +303,28 @@ return $args; } + $ldap = self::ldap(); + if (!$ldap || !$ldap->ready) { + $args['abort'] = true; + return $args; + } + // Find user record in LDAP - $record = $this->get_user_record($user, $host); + $record = $ldap->get_user_record($user, $host); if (empty($record)) { $args['abort'] = true; return $args; } - $role_attr = $rcmail->config->get('kolab_auth_role'); + $rcmail = rcube::get_instance(); + $admin_login = $rcmail->config->get('kolab_auth_admin_login'); + $admin_pass = $rcmail->config->get('kolab_auth_admin_password'); + $login_attr = $rcmail->config->get('kolab_auth_login'); + $name_attr = $rcmail->config->get('kolab_auth_name'); + $email_attr = $rcmail->config->get('kolab_auth_email'); + $org_attr = $rcmail->config->get('kolab_auth_organization'); + $role_attr = $rcmail->config->get('kolab_auth_role'); if (!empty($role_attr) && !empty($record[$role_attr])) { $_SESSION['user_roledns'] = (array)($record[$role_attr]); @@ -298,40 +333,32 @@ // Login As... if (!empty($loginas) && $admin_login) { // Authenticate to LDAP - $dn = $this->ldap->dn_decode($record['ID']); - $result = $this->ldap->bind($dn, $pass); + $result = $ldap->bind($record['dn'], $pass); if (!$result) { + $args['abort'] = true; return $args; } // check if the original user has/belongs to administrative role/group - $isadmin = false; - $group = $rcmail->config->get('kolab_auth_group'); - $role_attr = $rcmail->config->get('kolab_auth_role'); - $role_dn = $rcmail->config->get('kolab_auth_role_value'); + $isadmin = false; + $group = $rcmail->config->get('kolab_auth_group'); + $role_dn = $rcmail->config->get('kolab_auth_role_value'); // check role attribute if (!empty($role_attr) && !empty($role_dn) && !empty($record[$role_attr])) { - $role_dn = $this->parse_vars($role_dn, $user, $host); - foreach ((array)$record[$role_attr] as $role) { - if ($role == $role_dn) { - $isadmin = true; - break; - } + $role_dn = $ldap->parse_vars($role_dn, $user, $host); + if (in_array($role_dn, (array)$record[$role_attr])) {
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_auth/kolab_auth_ldap.php
Added
@@ -0,0 +1,485 @@ +<?php + +/** + * Kolab Authentication + * + * @version @package_version@ + * @author Aleksander Machniak <machniak@kolabsys.com> + * + * Copyright (C) 2011-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 + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * Wrapper class for rcube_ldap_generic + */ +class kolab_auth_ldap extends rcube_ldap_generic +{ + private $icache = array(); + + + function __construct($p) + { + $rcmail = rcube::get_instance(); + + $this->debug = (bool) $rcmail->config->get('ldap_debug'); + $this->fieldmap = $p['fieldmap']; + $this->fieldmap['uid'] = 'uid'; + + $p['attributes'] = array_values($this->fieldmap); + + // Connect to the server (with bind) + parent::__construct($p); + $this->_connect(); + + $rcmail->add_shutdown_function(array($this, 'close')); + } + + /** + * Establish a connection to the LDAP server + */ + private function _connect() + { + $rcube = rcube::get_instance(); + + // try to connect + bind for every host configured + // with OpenLDAP 2.x ldap_connect() always succeeds but ldap_bind will fail if host isn't reachable + // see http://www.php.net/manual/en/function.ldap-connect.php + foreach ((array)$this->config['hosts'] as $host) { + // skip host if connection failed + if (!$this->connect($host)) { + continue; + } + + $bind_pass = $this->config['bind_pass']; + $bind_user = $this->config['bind_user']; + $bind_dn = $this->config['bind_dn']; + + if (empty($bind_pass)) { + $this->ready = true; + } + else { + if (!empty($bind_dn)) { + $this->ready = $this->bind($bind_dn, $bind_pass); + } + else if (!empty($this->config['auth_cid'])) { + $this->ready = $this->sasl_bind($this->config['auth_cid'], $bind_pass, $bind_user); + } + else { + $this->ready = $this->sasl_bind($bind_user, $bind_pass); + } + } + + // connection established, we're done here + if ($this->ready) { + break; + } + + } // end foreach hosts + + if (!is_resource($this->conn)) { + rcube::raise_error(array('code' => 100, 'type' => 'ldap', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Could not connect to any LDAP server, last tried $host"), true); + + $this->ready = false; + } + + return $this->ready; + } + + /** + * Fetches user data from LDAP addressbook + */ + function get_user_record($user, $host) + { + $rcmail = rcube::get_instance(); + $filter = $rcmail->config->get('kolab_auth_filter'); + $filter = $this->parse_vars($filter, $user, $host); + $base_dn = $this->parse_vars($this->config['base_dn'], $user, $host); + $scope = $this->config['scope']; + + // @TODO: print error if filter is empty + + // get record + if ($result = parent::search($base_dn, $filter, $scope, $this->attributes)) { + if ($result->count() == 1) { + $entries = $result->entries(true); + $dn = key($entries); + $entry = array_pop($entries); + $entry = $this->field_mapping($dn, $entry); + + return $entry; + } + } + } + + /** + * Fetches user data from LDAP addressbook + */ + function get_user_groups($dn, $user, $host) + { + if (empty($dn) || empty($this->config['groups'])) { + return array(); + } + + $base_dn = $this->parse_vars($this->config['groups']['base_dn'], $user, $host); + $name_attr = $this->config['groups']['name_attr'] ? $this->config['groups']['name_attr'] : 'cn'; + $member_attr = $this->get_group_member_attr(); + $filter = "(member=$dn)(uniqueMember=$dn)"; + + if ($member_attr != 'member' && $member_attr != 'uniqueMember') + $filter .= "($member_attr=$dn)"; + $filter = strtr("(|$filter)", array("\\" => "\\\\")); + + $result = parent::search($base_dn, $filter, 'sub', array('dn', $name_attr)); + + if (!$result) { + return array(); + } + + $groups = array(); + foreach ($result as $entry) { + $entry = rcube_ldap_generic::normalize_entry($entry); + if (!$entry['dn']) { + $entry['dn'] = $result->get_dn(); + } + $groups[$entry['dn']] = $entry[$name_attr]; + } + + return $groups; + } + + /** + * Get a specific LDAP record + * + * @param string DN + * + * @return array Record data + */ + function get_record($dn) + { + if (!$this->ready) { + return; + } + + if ($rec = $this->get_entry($dn)) { + $rec = rcube_ldap_generic::normalize_entry($rec); + $rec = $this->field_mapping($dn, $rec); + } + + return $rec; + } + + /** + * Replace LDAP record data items + * + * @param string $dn DN + * @param array $entry LDAP entry + * + * return bool True on success, False on failure + */ + function replace($dn, $entry) + { + // fields mapping + foreach ($this->fieldmap as $field => $attr) { + if (array_key_exists($field, $entry)) { + $entry[$attr] = $entry[$field];
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_auth/localization/es_ES.inc
Added
@@ -0,0 +1,5 @@ +<?php + +$labels['loginas'] = 'Login As'; + +?>
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_auth/localization/et_EE.inc
Added
@@ -0,0 +1,5 @@ +<?php + +$labels['loginas'] = 'Login As'; + +?>
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_auth/localization/fr_FR.inc
Added
@@ -0,0 +1,5 @@ +<?php + +$labels['loginas'] = 'Se connecter en tant que'; + +?>
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_auth/localization/ja_JP.inc
Added
@@ -0,0 +1,5 @@ +<?php + +$labels['loginas'] = 'Login As'; + +?>
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_auth/localization/nl_NL.inc
Added
@@ -0,0 +1,5 @@ +<?php + +$labels['loginas'] = 'Log in als'; + +?>
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_auth/localization/ru_RU.inc
Added
@@ -0,0 +1,5 @@ +<?php + +$labels['loginas'] = 'Войти как'; + +?>
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/kolab_auth/package.xml -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_auth/package.xml
Changed
@@ -18,10 +18,10 @@ <email>machniak@kolabsys.com</email> <active>yes</active> </lead> - <date>2012-10-08</date> + <date>2013-06-25</date> <version> - <release>0.4</release> - <api>0.1</api> + <release>0.7</release> + <api>0.2</api> </version> <stability> <release>stable</release> @@ -35,6 +35,10 @@ <tasks:replace from="@name@" to="name" type="package-info"/> <tasks:replace from="@package_version@" to="version" type="package-info"/> </file> + <file name="kolab_auth_ldap.php" role="php"> + <tasks:replace from="@name@" to="name" type="package-info"/> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> <file name="config.inc.php.dist" role="data"></file> <file name="LICENSE" role="data"></file>
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/kolab_folders/config.inc.php.dist -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_folders/config.inc.php.dist
Changed
@@ -18,6 +18,10 @@ $rcmail_config['kolab_folders_note_default'] = ''; // Default Journal folder $rcmail_config['kolab_folders_journal_default'] = ''; +// Default Files folder +$rcmail_config['kolab_folders_file_default'] = ''; +// Default FreeBusy folder +$rcmail_config['kolab_folders_freebusy_default'] = ''; // INBOX folder $rcmail_config['kolab_folders_mail_inbox'] = '';
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/kolab_folders/kolab_folders.php -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_folders/kolab_folders.php
Changed
@@ -26,7 +26,7 @@ { public $task = '?(?!login).*'; - public $types = array('mail', 'event', 'journal', 'task', 'note', 'contact', 'configuration'); + public $types = array('mail', 'event', 'journal', 'task', 'note', 'contact', 'configuration', 'file', 'freebusy'); public $mail_types = array('inbox', 'drafts', 'sentitems', 'outbox', 'wastebasket', 'junkemail'); private $rc; @@ -54,6 +54,9 @@ $this->add_hook('folder_delete', array($this, 'folder_save')); $this->add_hook('folder_rename', array($this, 'folder_save')); $this->add_hook('folders_list', array($this, 'folders_list')); + + // Special folders setting + $this->add_hook('preferences_save', array($this, 'prefs_save')); } /** @@ -100,25 +103,25 @@ return $args; } - $table = $args['table']; - $storage = $this->rc->get_storage(); - // get folders types - $folderdata = $storage->get_metadata('*', kolab_storage::CTYPE_KEY); + $folderdata = kolab_storage::folders_typedata(); if (!is_array($folderdata)) { return $args; } + $table = $args['table']; + // Add type-based style for table rows // See kolab_folders::folder_class_name() for ($i=1, $cnt=$table->size(); $i<=$cnt; $i++) { $attrib = $table->get_row_attribs($i); $folder = $attrib['foldername']; // UTF7-IMAP - $type = !empty($folderdata[$folder]) ? $folderdata[$folder][kolab_storage::CTYPE_KEY] : null; + $type = $folderdata[$folder]; - if (!$type) + if (!$type) { $type = 'mail'; + } $class_name = self::folder_class_name($type); @@ -178,7 +181,7 @@ // Don't allow changing type of shared folder, according to ACL if (strlen($mbox)) { $options = $storage->folder_info($mbox); - if ($options['namespace'] != 'personal' && !in_array('a', $options['rights'])) { + if ($options['namespace'] != 'personal' && !in_array('a', (array)$options['rights'])) { if (in_array($ctype, $this->types)) { $value = $this->gettext('foldertype'.$ctype); } @@ -315,6 +318,83 @@ } /** + * Handler for user preferences save (preferences_save hook) + * + * @param array $args Hash array with hook parameters + * + * @return array Hash array with modified hook parameters + */ + public function prefs_save($args) + { + if ($args['section'] != 'folders') { + return $args; + } + + // Load configuration + $this->load_config(); + + // Check that configuration is not disabled + $dont_override = (array) $this->rc->config->get('dont_override', array()); + + // special handling for 'default_folders' + if (in_array('default_folders', $dont_override)) { + return $args; + } + + // map config option name to kolab folder type annotation + $opts = array( + 'drafts_mbox' => 'mail.drafts', + 'sent_mbox' => 'mail.sentitems', + 'junk_mbox' => 'mail.junkemail', + 'trash_mbox' => 'mail.wastebasket', + ); + + // check if any of special folders has been changed + foreach ($opts as $opt_name => $type) { + $new = $args['prefs'][$opt_name]; + $old = $this->rc->config->get($opt_name); + if ($new === $old) { + unset($opts[$opt_name]); + } + } + + if (empty($opts)) { + return $args; + } + + $folderdata = kolab_storage::folders_typedata(); + + if (!is_array($folderdata)) { + return $args; + } + + foreach ($opts as $opt_name => $type) { + $foldername = $args['prefs'][$opt_name]; + if (strlen($foldername)) { + + // get all folders of specified type + $folders = array_intersect($folderdata, array($type)); + + // folder already annotated with specified type + if (!empty($folders[$foldername])) { + continue; + } + + // set type to the new folder + $this->set_folder_type($foldername, $type); + + // unset old folder(s) type annotation + list($maintype, $subtype) = explode('.', $type); + foreach (array_keys($folders) as $folder) { + $this->set_folder_type($folder, $maintype); + } + } + } + + return $args; + } + + /** * Checks if IMAP server supports any of METADATA, ANNOTATEMORE, ANNOTATEMORE2 * * @return boolean @@ -337,14 +417,7 @@ */ function get_folder_type($folder) { - $storage = $this->rc->get_storage(); - $folderdata = $storage->get_metadata($folder, array(kolab_storage::CTYPE_KEY_PRIVATE, kolab_storage::CTYPE_KEY)); - - if (!($ctype = $folderdata[$folder][kolab_storage::CTYPE_KEY_PRIVATE])) { - $ctype = $folderdata[$folder][kolab_storage::CTYPE_KEY]; - } - - return explode('.', $ctype); + return explode('.', (string)kolab_storage::folder_type($folder)); } /** @@ -355,7 +428,7 @@ * * @return boolean True on success */ - function set_folder_type($folder, $type='mail') + function set_folder_type($folder, $type = 'mail') { return kolab_storage::set_folder_type($folder, $type); } @@ -369,45 +442,16 @@ */ function get_default_folder($type) { - $storage = $this->rc->get_storage(); - $folderdata = $storage->get_metadata('*', array(kolab_storage::CTYPE_KEY_PRIVATE, kolab_storage::CTYPE_KEY)); + $folderdata = kolab_storage::folders_typedata(); if (!is_array($folderdata)) { return null; } - $type .= '.default'; - $namespace = $storage->get_namespace(); - // get all folders of specified type - $folderdata = array_map(array($this, 'folder_select_metadata'), $folderdata); - $folderdata = array_intersect($folderdata, array($type)); - - foreach ($folderdata as $folder => $data) { - // check if folder is in personal namespace - foreach (array('shared', 'other') as $nskey) { - if (!empty($namespace[$nskey])) { - foreach ($namespace[$nskey] as $ns) { - if ($ns[0] && substr($folder, 0, strlen($ns[0])) == $ns[0]) { - continue 3; - } - } - } - } -
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/kolab_folders/localization/en_US.inc -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_folders/localization/en_US.inc
Changed
@@ -10,6 +10,8 @@ $labels['foldertypenote'] = 'Notes'; $labels['foldertypecontact'] = 'Contacts'; $labels['foldertypeconfiguration'] = 'Configuration'; +$labels['foldertypefile'] = 'Files'; +$labels['foldertypefreebusy'] = 'Free-Busy'; $labels['default'] = 'Default'; $labels['inbox'] = 'Inbox';
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_folders/localization/es_ES.inc
Added
@@ -0,0 +1,26 @@ +<?php + +$labels = array(); + +$labels['folderctype'] = 'Content type'; +$labels['foldertypemail'] = 'Mail'; +$labels['foldertypeevent'] = 'Calendar'; // Events? +$labels['foldertypejournal'] = 'Journal'; +$labels['foldertypetask'] = 'Tareas'; +$labels['foldertypenote'] = 'Notas'; +$labels['foldertypecontact'] = 'Contactos'; +$labels['foldertypeconfiguration'] = 'Configuración'; +$labels['foldertypefile'] = 'Files'; +$labels['foldertypefreebusy'] = 'Free-Busy'; + +$labels['default'] = 'Default'; +$labels['inbox'] = 'Inbox'; +$labels['drafts'] = 'Drafts'; +$labels['sentitems'] = 'Sent'; +$labels['outbox'] = 'Outbox'; +$labels['wastebasket'] = 'Trash'; +$labels['junkemail'] = 'Junk'; + +$messages['defaultfolderexists'] = 'There is already default folder of specified type'; + +?>
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_folders/localization/et_EE.inc
Added
@@ -0,0 +1,26 @@ +<?php + +$labels = array(); + +$labels['folderctype'] = 'Content type'; +$labels['foldertypemail'] = 'Mail'; +$labels['foldertypeevent'] = 'Calendar'; // Events? +$labels['foldertypejournal'] = 'Journal'; +$labels['foldertypetask'] = 'Tasks'; +$labels['foldertypenote'] = 'Notes'; +$labels['foldertypecontact'] = 'Contacts'; +$labels['foldertypeconfiguration'] = 'Configuration'; +$labels['foldertypefile'] = 'Files'; +$labels['foldertypefreebusy'] = 'Free-Busy'; + +$labels['default'] = 'Default'; +$labels['inbox'] = 'Inbox'; +$labels['drafts'] = 'Drafts'; +$labels['sentitems'] = 'Sent'; +$labels['outbox'] = 'Outbox'; +$labels['wastebasket'] = 'Trash'; +$labels['junkemail'] = 'Junk'; + +$messages['defaultfolderexists'] = 'There is already default folder of specified type'; + +?>
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_folders/localization/fr_FR.inc
Added
@@ -0,0 +1,26 @@ +<?php + +$labels = array(); + +$labels['folderctype'] = 'Type de contenu'; +$labels['foldertypemail'] = 'Courriel'; +$labels['foldertypeevent'] = 'Calendrier'; // Events? +$labels['foldertypejournal'] = 'Journal'; +$labels['foldertypetask'] = 'Tâches'; +$labels['foldertypenote'] = 'Notes'; +$labels['foldertypecontact'] = 'Contacts'; +$labels['foldertypeconfiguration'] = 'Configuration'; +$labels['foldertypefile'] = 'Fichiers'; +$labels['foldertypefreebusy'] = 'Disponible/Occupé'; + +$labels['default'] = 'Par Défaut'; +$labels['inbox'] = 'Courrier entrant'; +$labels['drafts'] = 'Brouillons'; +$labels['sentitems'] = 'Envoyés'; +$labels['outbox'] = 'Courrier sortant'; +$labels['wastebasket'] = 'Corbeille'; +$labels['junkemail'] = 'Indésirables'; + +$messages['defaultfolderexists'] = 'Il existe déjà un répertoire par défaut pour le type spécifié'; + +?>
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_folders/localization/ja_JP.inc
Added
@@ -0,0 +1,26 @@ +<?php + +$labels = array(); + +$labels['folderctype'] = 'Content type'; +$labels['foldertypemail'] = 'Mail'; +$labels['foldertypeevent'] = 'Calendar'; // Events? +$labels['foldertypejournal'] = 'Journal'; +$labels['foldertypetask'] = 'Tasks'; +$labels['foldertypenote'] = 'Notes'; +$labels['foldertypecontact'] = 'Contacts'; +$labels['foldertypeconfiguration'] = '設定'; +$labels['foldertypefile'] = 'Files'; +$labels['foldertypefreebusy'] = 'Free-Busy'; + +$labels['default'] = 'Default'; +$labels['inbox'] = 'Inbox'; +$labels['drafts'] = 'Drafts'; +$labels['sentitems'] = 'Sent'; +$labels['outbox'] = 'Outbox'; +$labels['wastebasket'] = 'Trash'; +$labels['junkemail'] = 'Junk'; + +$messages['defaultfolderexists'] = 'There is already default folder of specified type'; + +?>
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_folders/localization/nl_NL.inc
Added
@@ -0,0 +1,26 @@ +<?php + +$labels = array(); + +$labels['folderctype'] = 'Inhoudstype'; +$labels['foldertypemail'] = 'Mail'; +$labels['foldertypeevent'] = 'Agenda'; // Events? +$labels['foldertypejournal'] = 'Dagboek'; +$labels['foldertypetask'] = 'Taken'; +$labels['foldertypenote'] = 'Notities'; +$labels['foldertypecontact'] = 'Adresboek'; +$labels['foldertypeconfiguration'] = 'Configuratie'; +$labels['foldertypefile'] = 'Bestanden'; +$labels['foldertypefreebusy'] = 'Free/Busy'; + +$labels['default'] = 'Standaard'; +$labels['inbox'] = 'Inbox'; +$labels['drafts'] = 'Concepten'; +$labels['sentitems'] = 'Verzonden'; +$labels['outbox'] = 'Te versturen'; +$labels['wastebasket'] = 'Prullenbak'; +$labels['junkemail'] = 'Ongewenst'; + +$messages['defaultfolderexists'] = 'Er is reeds een standaard map voor dit type inhoud'; + +?>
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/kolab_folders/localization/pl_PL.inc -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_folders/localization/pl_PL.inc
Changed
@@ -9,6 +9,8 @@ $labels['foldertypenote'] = 'Notatki'; $labels['foldertypecontact'] = 'Kontakty'; $labels['foldertypeconfiguration'] = 'Konfiguracja'; +$labels['foldertypefile'] = 'Pliki'; +$labels['foldertypefreebusy'] = 'Free-Busy'; $labels['default'] = 'Domyślny'; $labels['inbox'] = 'Odebrane'; $labels['drafts'] = 'Szkice';
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_folders/localization/ru_RU.inc
Added
@@ -0,0 +1,26 @@ +<?php + +$labels = array(); + +$labels['folderctype'] = 'Тип ящика'; +$labels['foldertypemail'] = 'Почта'; +$labels['foldertypeevent'] = 'Календарь'; // Events? +$labels['foldertypejournal'] = 'Журнал'; +$labels['foldertypetask'] = 'Задачи'; +$labels['foldertypenote'] = 'Заметки'; +$labels['foldertypecontact'] = 'Контакты'; +$labels['foldertypeconfiguration'] = 'Настройки'; +$labels['foldertypefile'] = 'Файлы'; +$labels['foldertypefreebusy'] = 'Занят/Свободен'; + +$labels['default'] = 'По умолчанию'; +$labels['inbox'] = 'Входящие'; +$labels['drafts'] = 'Черновики'; +$labels['sentitems'] = 'Отправленные'; +$labels['outbox'] = 'Исходящие'; +$labels['wastebasket'] = 'Корзина'; +$labels['junkemail'] = 'Спам'; + +$messages['defaultfolderexists'] = 'Уже назначен ящик по умолчанию для указанного типа'; + +?>
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/kolab_folders/package.xml -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/kolab_folders/package.xml
Changed
@@ -21,9 +21,9 @@ <email>machniak@kolabsys.com</email> <active>yes</active> </lead> - <date>2012-05-14</date> + <date>2012-10.25</date> <version> - <release>2.0</release> + <release>2.1</release> <api>2.0</api> </version> <stability>
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/LICENSE
Added
@@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/README -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/README
Changed
@@ -15,29 +15,14 @@ * PEAR: HTTP/Request2 * PEAR: Net/URL2 -* Optional for old format support: - Horde Kolab_Format package and all of its dependencies - which are at least Horde_(Browser,DOM,NLS,String,Utils) - INSTALLATION ------------ To use local cache you need to create a dedicated table in Roundcube's database. -To do so, execute the SQL commands in SQL/<yourdatabase>.sql +To do so, execute the SQL commands in SQL/<yourdatabase>.initial.sql CONFIGURATION ------------- -The following options can be configured in Roundcube's main config file -or a local config file (config.inc.php) located in the plugin folder. - -// Enable caching of Kolab objects in local database -$rcmail_config['kolab_cache'] = true; - -// Optional override of the URL to read and trigger Free/Busy information of Kolab users -// 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; - +Rename config.inc.php.dist to config.inc.php in the plugin folder. +For available configuration options see config.inc.php.dist file.
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/SQL/mysql
Added
+(directory)
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/SQL/mysql.initial.sql
Added
@@ -0,0 +1,29 @@ +/** + * libkolab database schema + * + * @version @package_version@ + * @author Thomas Bruederli + * @licence GNU AGPL + **/ + +DROP TABLE IF EXISTS `kolab_cache`; + +CREATE TABLE `kolab_cache` ( + `resource` VARCHAR(255) CHARACTER SET ascii NOT NULL, + `type` VARCHAR(32) CHARACTER SET ascii 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, + `dtstart` DATETIME, + `dtend` DATETIME, + `tags` VARCHAR(255) NOT NULL, + `words` TEXT NOT NULL, + `filename` varchar(255) DEFAULT NULL, + PRIMARY KEY(`resource`,`type`,`msguid`), + INDEX `resource_filename` (`resource`, `filename`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +INSERT INTO `system` (`name`, `value`) VALUES ('libkolab-version', '2013041900');
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/SQL/mysql/2013011000.sql
Added
@@ -0,0 +1,1 @@ +-- empty \ No newline at end of file
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/SQL/mysql/2013041900.sql
Added
@@ -0,0 +1,3 @@ +DELETE FROM `kolab_cache` WHERE `type` = 'file'; +ALTER TABLE `kolab_cache` ADD `filename` varchar(255) DEFAULT NULL; +ALTER TABLE `kolab_cache` ADD INDEX `resource_filename` (`resource`, `filename`);
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/SQL/postgres.initial.sql
Added
@@ -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');
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/UPGRADING
Added
@@ -0,0 +1,9 @@ +UPGRADING instructions +====================== + +To update database schema please run in Roundcube bin/ directory: + +updatedb.sh --package=libkolab --version=<version> --dir=../plugins/libkolab/SQL + +[*] Replace <version> with Roundcube version e.g. 0.7.3 +[*] Roundcube should be upgraded before plugin upgrades
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/bin/modcache.sh -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/bin/modcache.sh
Changed
@@ -81,7 +81,7 @@ 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 @@ $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) {
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/config.inc.php.dist -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/config.inc.php.dist
Changed
@@ -1,9 +1,26 @@ <?php - /* Configuration for libkolab */ - $rcmail_config['kolab_cache'] = true; +/* Configuration for libkolab */ - $rcmail_config['kolab_freebusy_server'] = 'https://' . $_SESSION['imap_host'] . '/freebusy'; - $rcmail_config['kolab_ssl_verify_peer'] = true; +// Enable caching of Kolab objects in local database +$rcmail_config['kolab_cache'] = true; + +// Specify format version to write Kolab objects (must be a string value!) +$rcmail_config['kolab_format_version'] = '3.0'; + +// Optional override of the URL to read and trigger Free/Busy information of Kolab users +// 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; + +// Enables the use of displayname folder annotations as introduced in KEP:? +// for displaying resource folder names (experimental!) +$rcmail_config['kolab_custom_display_names'] = false; ?>
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/lib/kolab_date_recurrence.php -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/lib/kolab_date_recurrence.php
Changed
@@ -3,7 +3,8 @@ /** * Recurrence computation class for xcal-based Kolab format objects * - * Uitility class to compute instances of recurring events. + * Utility class to compute instances of recurring events. + * It requires the libcalendaring PHP module to be installed and loaded. * * @version @package_version@ * @author Thomas Bruederli <bruederli@kolabsys.com> @@ -25,14 +26,12 @@ */ class kolab_date_recurrence { - private $engine; - private $object; - private $next; - private $duration; - private $tz_offset = 0; - private $dst_start = 0; - private $allday = false; - private $hour = 0; + private /* EventCal */ $engine; + private /* kolab_format_xcal */ $object; + private /* DateTime */ $start; + private /* DateTime */ $next; + private /* cDateTime */ $cnext; + private /* DateInterval */ $duration; /** * Default constructor @@ -41,27 +40,17 @@ */ function __construct($object) { + $data = $object->to_array(); + $this->object = $object; - $this->next = new Horde_Date($object['start'], kolab_format::$timezone->getName()); + $this->engine = $object->to_libcal(); + $this->start = $this->next = $data['start']; + $this->cnext = kolab_format::get_datetime($this->next); - if (is_object($object['start']) && is_object($object['end'])) - $this->duration = $object['start']->diff($object['end']); + if (is_object($data['start']) && is_object($data['end'])) + $this->duration = $data['start']->diff($data['end']); else - $this->duration = new DateInterval('PT' . ($object['end'] - $object['start']) . 'S'); - - // use (copied) Horde classes to compute recurring instances - // TODO: replace with something that has less than 6'000 lines of code - $this->engine = new Horde_Date_Recurrence($this->next); - $this->engine->fromRRule20($this->to_rrule($object['recurrence'])); // TODO: get that string directly from libkolabxml - - foreach ((array)$object['recurrence']['EXDATE'] as $exdate) - $this->engine->addException($exdate->format('Y'), $exdate->format('n'), $exdate->format('j')); - - $now = new DateTime('now', kolab_format::$timezone); - $this->tz_offset = $object['allday'] ? $now->getOffset() - date('Z') : 0; - $this->dst_start = $this->next->format('I'); - $this->allday = $object['allday']; - $this->hour = $this->next->hour; + $this->duration = new DateInterval('PT' . ($data['end'] - $data['start']) . 'S'); } /** @@ -73,20 +62,14 @@ public function next_start($timestamp = false) { $time = false; - if ($this->next && ($next = $this->engine->nextActiveRecurrence(array('year' => $this->next->year, 'month' => $this->next->month, 'mday' => $this->next->mday + 1, 'hour' => $this->next->hour, 'min' => $this->next->min, 'sec' => $this->next->sec)))) { - if ($this->allday) { - $next->hour = $this->hour; # fix time for all-day events - $next->min = 0; - } - if ($timestamp) { - # consider difference in daylight saving between base event and recurring instance - $dst_diff = ($this->dst_start - $next->format('I')) * 3600; - $time = $next->timestamp() - $this->tz_offset - $dst_diff; - } - else { - $time = $next->toDateTime(); + + if ($this->engine && $this->next) { + if (($cnext = new cDateTime($this->engine->getNextOccurence($this->cnext))) && $cnext->isValid()) { + $next = kolab_format::php_datetime($cnext); + $time = $timestamp ? $next->format('U') : $next; + $this->cnext = $cnext; + $this->next = $next; } - $this->next = $next; } return $time; @@ -103,7 +86,7 @@ $next_end = clone $next_start; $next_end->add($this->duration); - $next = $this->object; + $next = $this->object->to_array(); $next['recurrence_id'] = $next_start->format('Y-m-d'); $next['start'] = $next_start; $next['end'] = $next_end; @@ -123,49 +106,11 @@ */ public function end($limit = 'now +1 year') { - if ($this->object['recurrence']['UNTIL']) - return $this->object['recurrence']['UNTIL']->format('U'); - - $limit_time = strtotime($limit); - while ($next_start = $this->next_start(true)) { - if ($next_start > $limit_time) - break; - } - - if ($this->next) { - $next_end = $this->next->toDateTime(); - $next_end->add($this->duration); - return $next_end->format('U'); + $limit_dt = new DateTime($limit); + if ($this->engine && ($cend = $this->engine->getLastOccurrence()) && ($end_dt = kolab_format::php_datetime(new cDateTime($cend))) && $end_dt < $limit_dt) { + return $end_dt->format('U'); } return false; } - - /** - * Convert the internal structured data into a vcalendar RRULE 2.0 string - */ - private function to_rrule($recurrence) - { - if (is_string($recurrence)) - return $recurrence; - - $rrule = ''; - foreach ((array)$recurrence as $k => $val) { - $k = strtoupper($k); - switch ($k) { - case 'UNTIL': - $val = $val->format('Ymd\THis'); - break; - case 'EXDATE': - foreach ((array)$val as $i => $ex) - $val[$i] = $ex->format('Ymd\THis'); - $val = join(',', (array)$val); - break; - } - $rrule .= $k . '=' . $val . ';'; - } - - return $rrule; - } - }
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/lib/kolab_format.php -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/lib/kolab_format.php
Changed
@@ -30,40 +30,62 @@ public static $timezone; public /*abstract*/ $CTYPE; + public /*abstract*/ $CTYPEv2; + protected /*abstract*/ $objclass; protected /*abstract*/ $read_func; protected /*abstract*/ $write_func; protected $obj; protected $data; protected $xmldata; + protected $xmlobject; protected $loaded = false; + protected $version = '3.0'; - const VERSION = '3.0'; const KTYPE_PREFIX = 'application/x-vnd.kolab.'; + const PRODUCT_ID = 'Roundcube-libkolab-0.9'; /** - * Factory method to instantiate a kolab_format object of the given type + * Factory method to instantiate a kolab_format object of the given type and version * * @param string Object type to instantiate + * @param float Format version * @param string Cached xml data to initialize with * @return object kolab_format */ - public static function factory($type, $xmldata = null) + public static function factory($type, $version = '3.0', $xmldata = null) { if (!isset(self::$timezone)) self::$timezone = new DateTimeZone('UTC'); + if (!self::supports($version)) + return PEAR::raiseError("No support for Kolab format version " . $version); + $type = preg_replace('/configuration\.[a-z.]+$/', 'configuration', $type); $suffix = preg_replace('/[^a-z]+/', '', $type); $classname = 'kolab_format_' . $suffix; if (class_exists($classname)) - return new $classname($xmldata); + return new $classname($xmldata, $version); return PEAR::raiseError("Failed to load Kolab Format wrapper for type " . $type); } /** + * Determine support for the given format version + * + * @param float Format version to check + * @return boolean True if supported, False otherwise + */ + public static function supports($version) + { + if ($version == '2.0') + return class_exists('kolabobject'); + // default is version 3 + return class_exists('kolabformat'); + } + + /** * Convert the given date/time value into a cDateTime object * * @param mixed Date/Time value either as unix timestamp, date string or PHP DateTime object @@ -184,6 +206,23 @@ return preg_replace('/dictionary.[a-z.]+$/', 'dictionary', substr($x_kolab_type, strlen(self::KTYPE_PREFIX))); } + + /** + * Default constructor of all kolab_format_* objects + */ + public function __construct($xmldata = null, $version = null) + { + $this->obj = new $this->objclass; + $this->xmldata = $xmldata; + + if ($version) + $this->version = $version; + + // use libkolab module if available + if (class_exists('kolabobject')) + $this->xmlobject = new XMLObject(); + } + /** * Check for format errors after calling kolabformat::write*() * @@ -211,7 +250,7 @@ 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__, - 'message' => "kolabformat write $log: " . kolabformat::errorMessage(), + 'message' => "kolabformat $log: " . kolabformat::errorMessage(), ), true); } @@ -226,7 +265,12 @@ { // get generated UID if (!$this->data['uid']) { - $this->data['uid'] = kolabformat::getSerializedUID(); + if ($this->xmlobject) { + $this->data['uid'] = $this->xmlobject->getSerializedUID(); + } + if (empty($this->data['uid'])) { + $this->data['uid'] = kolabformat::getSerializedUID(); + } $this->obj->setUid($this->data['uid']); } } @@ -246,6 +290,39 @@ } /** + * Get constant value for libkolab's version parameter + * + * @param float Version value to convert + * @return int Constant value of either kolabobject::KolabV2 or kolabobject::KolabV3 or false if kolabobject module isn't available + */ + protected function libversion($v = null) + { + if (class_exists('kolabobject')) { + $version = $v ?: $this->version; + if ($version <= '2.0') + return kolabobject::KolabV2; + else + return kolabobject::KolabV3; + } + + return false; + } + + /** + * Determine the correct libkolab(xml) wrapper function for the given call + * depending on the available PHP modules + */ + protected function libfunc($func) + { + if (is_array($func) || strpos($func, '::')) + return $func; + else if (class_exists('kolabobject')) + return array($this->xmlobject, $func); + else + return 'kolabformat::' . $func; + } + + /** * Direct getter for object properties */ public function __get($var) @@ -257,22 +334,39 @@ * Load Kolab object data from the given XML block * * @param string XML data + * @return boolean True on success, False on failure */ public function load($xml) { - $this->obj = call_user_func($this->read_func, $xml, false); + $read_func = $this->libfunc($this->read_func); + + if (is_array($read_func)) + $r = call_user_func($read_func, $xml, $this->libversion()); + else + $r = call_user_func($read_func, $xml, false); + + if (is_resource($r)) + $this->obj = new $this->objclass($r); + else if (is_a($r, $this->objclass)) + $this->obj = $r; + $this->loaded = !$this->format_errors(); } /** * Write object data to XML format * + * @param float Format version to write * @return string XML data */ - public function write() + public function write($version = null) { $this->init(); - $this->xmldata = call_user_func($this->write_func, $this->obj); + $write_func = $this->libfunc($this->write_func); + if (is_array($write_func)) + $this->xmldata = call_user_func($write_func, $this->obj, $this->libversion($version), self::PRODUCT_ID); + else + $this->xmldata = call_user_func($write_func, $this->obj, self::PRODUCT_ID); if (!$this->format_errors()) $this->update_uid(); @@ -287,26 +381,82 @@ * * @param array Object data as hash array
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/lib/kolab_format_configuration.php -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/lib/kolab_format_configuration.php
Changed
@@ -25,9 +25,11 @@ class kolab_format_configuration extends kolab_format { public $CTYPE = 'application/x-vnd.kolab.configuration'; + public $CTYPEv2 = 'application/x-vnd.kolab.configuration'; - protected $read_func = 'kolabformat::readConfiguration'; - protected $write_func = 'kolabformat::writeConfiguration'; + protected $objclass = 'Configuration'; + protected $read_func = 'readConfiguration'; + protected $write_func = 'writeConfiguration'; private $type_map = array( 'dictionary' => Configuration::TypeDictionary, @@ -35,12 +37,6 @@ ); - function __construct($xmldata = null) - { - $this->obj = new Configuration; - $this->xmldata = $xmldata; - } - /** * Set properties to the kolabformat object * @@ -48,7 +44,8 @@ */ public function set(&$object) { - $this->init(); + // set common object properties + parent::set($object); // read type-specific properties switch ($object['type']) { @@ -67,14 +64,8 @@ return false; } - // set some automatic values if missing - if (!empty($object['uid'])) - $this->obj->setUid($object['uid']); - if (!empty($object['created'])) - $this->obj->setCreated(self::get_datetime($object['created'])); - // adjust content-type string - $this->CTYPE = 'application/x-vnd.kolab.configuration.' . $object['type']; + $this->CTYPE = $this->CTYPEv2 = 'application/x-vnd.kolab.configuration.' . $object['type']; // cache this data $this->data = $object; @@ -92,24 +83,22 @@ /** * Convert the Configuration object into a hash array data structure * + * @param array Additional data for merge + * * @return array Config object data as hash array */ - public function to_array() + public function to_array($data = array()) { // return cached result if (!empty($this->data)) return $this->data; - $this->init(); + // read common object props into local data object + $object = parent::to_array($data); + $type_map = array_flip($this->type_map); - // read object properties - $object = array( - 'uid' => $this->obj->uid(), - 'created' => self::php_datetime($this->obj->created()), - 'changed' => self::php_datetime($this->obj->lastModified()), - 'type' => $type_map[$this->obj->type()], - ); + $object['type'] = $type_map[$this->obj->type()]; // read type-specific properties switch ($object['type']) { @@ -126,26 +115,13 @@ // adjust content-type string if ($object['type']) - $this->CTYPE = 'application/x-vnd.kolab.configuration.' . $object['type']; + $this->CTYPE = $this->CTYPEv2 = 'application/x-vnd.kolab.configuration.' . $object['type']; $this->data = $object; return $this->data; } /** - * Load data from old Kolab2 format - */ - public function fromkolab2($record) - { - $object = array( - 'uid' => $record['uid'], - 'changed' => $record['last-modification-date'], - ); - - $this->data = $object + $record; - } - - /** * Callback for kolab_storage_cache to get object specific tags to cache * * @return array List of tags to save in cache
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/lib/kolab_format_contact.php -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/lib/kolab_format_contact.php
Changed
@@ -25,11 +25,13 @@ class kolab_format_contact extends kolab_format { public $CTYPE = 'application/vcard+xml'; + public $CTYPEv2 = 'application/x-vnd.kolab.contact'; - protected $read_func = 'kolabformat::readContact'; - protected $write_func = 'kolabformat::writeContact'; + protected $objclass = 'Contact'; + protected $read_func = 'readContact'; + protected $write_func = 'writeContact'; - public static $fulltext_cols = array('name', 'firstname', 'surname', 'middlename', 'email'); + public static $fulltext_cols = array('name', 'firstname', 'surname', 'middlename', 'email:address'); public $phonetypes = array( 'home' => Telephone::Home, @@ -45,6 +47,12 @@ 'other' => Telephone::Textphone, ); + public $emailtypes = array( + 'home' => Email::Home, + 'work' => Email::Work, + 'other' => Email::NoType, + ); + public $addresstypes = array( 'home' => Address::Home, 'work' => Address::Work, @@ -63,53 +71,13 @@ 'children' => Related::Child, ); - // old Kolab 2 format field map - private $kolab2_fieldmap = array( - // kolab => roundcube - 'full-name' => 'name', - 'given-name' => 'firstname', - 'middle-names' => 'middlename', - 'last-name' => 'surname', - 'prefix' => 'prefix', - 'suffix' => 'suffix', - 'nick-name' => 'nickname', - 'organization' => 'organization', - 'department' => 'department', - 'job-title' => 'jobtitle', - 'birthday' => 'birthday', - 'anniversary' => 'anniversary', - 'phone' => 'phone', - 'im-address' => 'im', - 'web-page' => 'website', - 'profession' => 'profession', - 'manager-name' => 'manager', - 'assistant' => 'assistant', - 'spouse-name' => 'spouse', - 'children' => 'children', - 'body' => 'notes', - 'pgp-publickey' => 'pgppublickey', - 'free-busy-url' => 'freebusyurl', - 'picture' => 'photo', - ); - private $kolab2_phonetypes = array( - 'home1' => 'home', - 'business1' => 'work', - 'business2' => 'work', - 'businessfax' => 'workfax', - ); - private $kolab2_addresstypes = array( - 'business' => 'work' - ); - private $kolab2_gender = array(0 => 'male', 1 => 'female'); - /** * Default constructor */ - function __construct($xmldata = null) + function __construct($xmldata = null, $version = 3.0) { - $this->obj = new Contact; - $this->xmldata = $xmldata; + parent::__construct($xmldata, $version); // complete phone types $this->phonetypes['homefax'] |= Telephone::Home; @@ -123,20 +91,8 @@ */ public function set(&$object) { - $this->init(); - - // set some automatic values if missing - if (false && !$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['uid'])) - $this->obj->setUid($object['uid']); - - $object['changed'] = new DateTime('now', self::$timezone); - $this->obj->setLastModified(self::get_datetime($object['changed'], new DateTimeZone('UTC'))); + // set common object properties + parent::set($object); // do the hard work of setting object values $nc = new NameComponents; @@ -147,6 +103,7 @@ $nc->setSuffixes(self::array2vector($object['suffix'])); $this->obj->setNameComponents($nc); $this->obj->setName($object['name']); + $this->obj->setCategories(self::array2vector($object['categories'])); if (isset($object['nickname'])) $this->obj->setNickNames(self::array2vector($object['nickname'])); @@ -174,10 +131,21 @@ } $org->setRelateds($rels); - // email, im, url - $this->obj->setEmailAddresses(self::array2vector($object['email'])); + // im, email, url $this->obj->setIMaddresses(self::array2vector($object['im'])); + if (class_exists('vectoremail')) { + $vemails = new vectoremail; + foreach ((array)$object['email'] as $email) { + $type = $this->emailtypes[$email['type']]; + $vemails->push(new Email($email['address'], intval($type))); + } + } + else { + $vemails = self::array2vector(array_map(function($v){ return $v['address']; }, $object['email'])); + } + $this->obj->setEmailAddresses($vemails); + $vurls = new vectorurl; foreach ((array)$object['website'] as $url) { $type = $url['type'] == 'blog' ? Url::Blog : Url::NoType; @@ -287,6 +255,8 @@ // TODO: handle language, gpslocation, etc. + // set type property for proper caching + $object['_type'] = 'contact'; // cache this data $this->data = $object; @@ -304,22 +274,20 @@ /** * Convert the Contact object into a hash array data structure * + * @param array Additional data for merge + * * @return array Contact data as hash array */ - public function to_array() + public function to_array($data = array()) { // return cached result if (!empty($this->data)) return $this->data; - $this->init(); + // read common object props into local data object + $object = parent::to_array($data); - // read object properties into local data object - $object = array( - 'uid' => $this->obj->uid(), - 'name' => $this->obj->name(), - 'changed' => self::php_datetime($this->obj->lastModified()), - ); + $object['name'] = $this->obj->name(); $nc = $this->obj->nameComponents(); $object['surname'] = join(' ', self::vector2array($nc->surnames())); @@ -329,6 +297,7 @@ $object['suffix'] = join(' ', self::vector2array($nc->suffixes())); $object['nickname'] = join(' ', self::vector2array($this->obj->nickNames())); $object['profession'] = join(' ', self::vector2array($this->obj->titles())); + $object['categories'] = self::vector2array($this->obj->categories()); // organisation related properties (affiliation) $orgs = $this->obj->affiliations(); @@ -340,8 +309,19 @@ $this->read_relateds($org->relateds(), $object); } - $object['email'] = self::vector2array($this->obj->emailAddresses()); - $object['im'] = self::vector2array($this->obj->imAddresses()); + $object['im'] = self::vector2array($this->obj->imAddresses()); + + $emails = $this->obj->emailAddresses(); + if ($emails instanceof vectoremail) { + $emailtypes = array_flip($this->emailtypes); + for ($i=0; $i < $emails->size(); $i++) { + $email = $emails->get($i);
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/lib/kolab_format_distributionlist.php -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/lib/kolab_format_distributionlist.php
Changed
@@ -25,17 +25,13 @@ class kolab_format_distributionlist extends kolab_format { public $CTYPE = 'application/vcard+xml'; + public $CTYPEv2 = 'application/x-vnd.kolab.distribution-list'; - protected $read_func = 'kolabformat::readDistlist'; - protected $write_func = 'kolabformat::writeDistlist'; + protected $objclass = 'DistList'; + protected $read_func = 'readDistlist'; + protected $write_func = 'writeDistlist'; - function __construct($xmldata = null) - { - $this->obj = new DistList; - $this->xmldata = $xmldata; - } - /** * Set properties to the kolabformat object * @@ -43,18 +39,11 @@ */ public function set(&$object) { - $this->init(); - - // set some automatic values if missing - if (!empty($object['uid'])) - $this->obj->setUid($object['uid']); - - $object['changed'] = new DateTime('now', self::$timezone); - $this->obj->setLastModified(self::get_datetime($object['changed'], new DateTimeZone('UTC'))); + // set common object properties + parent::set($object); $this->obj->setName($object['name']); - $seen = array(); $members = new vectorcontactref; foreach ((array)$object['member'] as $member) { if ($member['uid']) @@ -66,7 +55,6 @@ $m->setName($member['name']); $members->push($m); - $seen[$member['email']]++; } $this->obj->setMembers($members); @@ -85,45 +73,23 @@ } /** - * Load data from old Kolab2 format - */ - public function fromkolab2($record) - { - $object = array( - 'uid' => $record['uid'], - 'changed' => $record['last-modification-date'], - 'name' => $record['last-name'], - 'member' => array(), - ); - - foreach ((array)$record['member'] as $member) { - $object['member'][] = array( - 'email' => $member['smtp-address'], - 'name' => $member['display-name'], - 'uid' => $member['uid'], - ); - } - - $this->data = $object; - } - - /** * Convert the Distlist object into a hash array data structure * + * @param array Additional data for merge + * * @return array Distribution list data as hash array */ - public function to_array() + public function to_array($data = array()) { // return cached result if (!empty($this->data)) return $this->data; - $this->init(); + // read common object props into local data object + $object = parent::to_array($data); - // read object properties - $object = array( - 'uid' => $this->obj->uid(), - 'changed' => self::php_datetime($this->obj->lastModified()), + // add object properties + $object += array( 'name' => $this->obj->name(), 'member' => array(), '_type' => 'distribution-list', @@ -132,11 +98,11 @@ $members = $this->obj->members(); for ($i=0; $i < $members->size(); $i++) { $member = $members->get($i); -# if ($member->type() == ContactReference::UidReference && ($uid = $member->uid())) +// if ($member->type() == ContactReference::UidReference && ($uid = $member->uid())) $object['member'][] = array( - 'uid' => $member->uid(), + 'uid' => $member->uid(), 'email' => $member->email(), - 'name' => $member->name(), + 'name' => $member->name(), ); }
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/lib/kolab_format_event.php -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/lib/kolab_format_event.php
Changed
@@ -24,31 +24,47 @@ class kolab_format_event extends kolab_format_xcal { - protected $read_func = 'kolabformat::readEvent'; - protected $write_func = 'kolabformat::writeEvent'; - - private $kolab2_rolemap = array( - 'required' => 'REQ-PARTICIPANT', - 'optional' => 'OPT-PARTICIPANT', - 'resource' => 'CHAIR', - ); - private $kolab2_statusmap = array( - 'none' => 'NEEDS-ACTION', - 'tentative' => 'TENTATIVE', - 'accepted' => 'CONFIRMED', - 'accepted' => 'ACCEPTED', - 'declined' => 'DECLINED', - ); - private $kolab2_monthmap = array('', 'january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december'); + public $CTYPEv2 = 'application/x-vnd.kolab.event'; + protected $objclass = 'Event'; + protected $read_func = 'readEvent'; + protected $write_func = 'writeEvent'; /** * Default constructor */ - function __construct($xmldata = null) + function __construct($data = null, $version = 3.0) { - $this->obj = new Event; - $this->xmldata = $xmldata; + parent::__construct(is_string($data) ? $data : null, $version); + + // got an Event object as argument + if (is_object($data) && is_a($data, $this->objclass)) { + $this->obj = $data; + $this->loaded = true; + } + } + + /** + * Clones into an instance of libcalendaring's extended EventCal class + * + * @return mixed EventCal object or false on failure + */ + public function to_libcal() + { + static $error_logged = false; + + if (class_exists('kolabcalendaring')) { + return new EventCal($this->obj); + } + else if (!$error_logged) { + $error_logged = true; + rcube::raise_error(array( + 'code' => 900, 'type' => 'php', + 'message' => "required kolabcalendaring module not found" + ), true); + } + + return false; } /** @@ -58,8 +74,6 @@ */ public function set(&$object) { - $this->init(); - // set common xcal properties parent::set($object); @@ -75,17 +89,17 @@ $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); + // save recurrence exceptions + if ($object['recurrence']['EXCEPTIONS']) { + $vexceptions = new vectorevent; + foreach((array)$object['recurrence']['EXCEPTIONS'] as $exception) { + $exevent = new kolab_format_event; + $exevent->set($this->compact_exception($exception, $object)); // only save differing values + $exevent->obj->setRecurrenceID(self::get_datetime($exception['start'], null, true), (bool)$exception['thisandfuture']); + $vexceptions->push($exevent->obj); + } + $this->obj->setExceptions($vexceptions); } - $this->obj->setAttachments($vattach); // cache this data $this->data = $object; @@ -103,18 +117,18 @@ /** * Convert the Event object into a hash array data structure * + * @param array Additional data for merge + * * @return array Event data as hash array */ - public function to_array() + public function to_array($data = array()) { // return cached result if (!empty($this->data)) return $this->data; - $this->init(); - // read common xcal props - $object = parent::to_array(); + $object = parent::to_array($data); // read object properties $object += array( @@ -135,28 +149,25 @@ if ($status == kolabformat::StatusTentative) $object['free_busy'] = 'tentative'; else if ($status == kolabformat::StatusCancelled) - $objec['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') { - $name = $attach->label(); - $data = $attach->data(); - $object['_attachments'][$name] = array( - 'name' => $name, - 'mimetype' => $attach->mimetype(), - 'size' => strlen($data), - 'content' => $data, - ); + $object['cancelled'] = true; + + // read exception event objects + if (($exceptions = $this->obj->exceptions()) && is_object($exceptions) && $exceptions->size()) { + for ($i=0; $i < $exceptions->size(); $i++) { + if (($exobj = $exceptions->get($i))) { + $exception = new kolab_format_event($exobj); + if ($exception->is_valid()) { + $object['recurrence']['EXCEPTIONS'][] = $this->expand_exception($exception->to_array(), $object); + } + } } } + // this is an exception object + else if ($this->obj->recurrenceID()->isValid()) { + $object['thisandfuture'] = $this->obj->thisAndFuture(); + } - $this->data = $object; - return $this->data; + return $this->data = $object; } /** @@ -180,124 +191,32 @@ } /** - * Load data from old Kolab2 format + * Remove some attributes from the exception container */ - public function fromkolab2($rec) + private function compact_exception($exception, $master) { - if (PEAR::isError($rec)) - return; - - $start_time = date('H:i:s', $rec['start-date']); - $allday = $rec['_is_all_day'] || ($start_time == '00:00:00' && $start_time == date('H:i:s', $rec['end-date'])); - - // in Roundcube all-day events go from 12:00 to 13:00 - if ($allday) { - $now = new DateTime('now', self::$timezone); - $gmt_offset = $now->getOffset(); + $forbidden = array('recurrence','organizer','attendees','sequence'); - $rec['start-date'] += 12 * 3600; - $rec['end-date'] -= 11 * 3600; - $rec['end-date'] -= $gmt_offset - date('Z', $rec['end-date']); // shift times from server's timezone to user's timezone - $rec['start-date'] -= $gmt_offset - date('Z', $rec['start-date']); // because generated with mktime() in Horde_Kolab_Format_Date::decodeDate() - // sanity check - if ($rec['end-date'] <= $rec['start-date'])
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/lib/kolab_format_file.php
Added
@@ -0,0 +1,156 @@ +<?php + +/** + * Kolab File model class + * + * @version @package_version@ + * @author Thomas Bruederli <bruederli@kolabsys.com> + * @author Aleksander Machniak <machniak@kolabsys.com> + * + * Copyright (C) 2012, 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 + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +class kolab_format_file extends kolab_format +{ + public $CTYPE = 'application/x-vnd.kolab.file'; + + protected $objclass = 'File'; + protected $read_func = 'kolabformat::readKolabFile'; + protected $write_func = 'kolabformat::writeKolabFile'; + + protected $sensitivity_map = array( + 'public' => kolabformat::ClassPublic, + 'private' => kolabformat::ClassPrivate, + 'confidential' => kolabformat::ClassConfidential, + ); + + /** + * Set properties to the kolabformat object + * + * @param array Object data as hash array + */ + public function set(&$object) + { + // set common object properties + parent::set($object); + + $this->obj->setClassification($this->sensitivity_map[$object['sensitivity']]); + $this->obj->setCategories(self::array2vector($object['categories'])); + + if (isset($object['notes'])) { + $this->obj->setNote($object['notes']); + } + + // Add file attachment + if (!empty($object['_attachments'])) { + $cid = key($object['_attachments']); + $attach_attr = $object['_attachments'][$cid]; + $attach = new Attachment; + + $attach->setLabel((string)$attach_attr['name']); + $attach->setUri('cid:' . $cid, $attach_attr['mimetype']); + $this->obj->setFile($attach); + + // make sure size is set, so object saved in cache contains this info + if (!isset($attach_attr['size'])) { + $size = 0; + + if (!empty($attach_attr['content'])) { + if (is_resource($attach_attr['content'])) { + $stat = fstat($attach_attr['content']); + $size = $stat ? $stat['size'] : 0; + } + else { + $size = strlen($attach_attr['content']); + } + } + else if (isset($attach_attr['path'])) { + $size = @filesize($attach_attr['path']); + } + + $object['_attachments'][$cid]['size'] = $size; + } + } + + // cache this data + $this->data = $object; + unset($this->data['_formatobj']); + } + + /** + * Check if object's data validity + */ + public function is_valid() + { + return $this->data || (is_object($this->obj) && $this->obj->isValid()); + } + + /** + * Convert the Configuration object into a hash array data structure + * + * @param array Additional data for merge + * + * @return array Config object data as hash array + */ + public function to_array($data = array()) + { + // return cached result + if (!empty($this->data)) { + return $this->data; + } + + // read common object props into local data object + $object = parent::to_array($data); + + $sensitivity_map = array_flip($this->sensitivity_map); + + // read object properties + $object += array( + 'sensitivity' => $sensitivity_map[$this->obj->classification()], + 'categories' => self::vector2array($this->obj->categories()), + 'notes' => $this->obj->note(), + ); + + return $this->data = $object; + } + + /** + * Callback for kolab_storage_cache to get object specific tags to cache + * + * @return array List of tags to save in cache + */ + public function get_tags() + { + $tags = array(); + + foreach ((array)$this->data['categories'] as $cat) { + $tags[] = rcube_utils::normalize_string($cat); + } + + // Add file mimetype to tags + if (!empty($this->data['_attachments'])) { + reset($this->data['_attachments']); + $key = key($this->data['_attachments']); + $attachment = $this->data['_attachments'][$key]; + + if ($attachment['mimetype']) { + $tags[] = $attachment['mimetype']; + } + } + + return $tags; + } +}
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/lib/kolab_format_journal.php -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/lib/kolab_format_journal.php
Changed
@@ -25,17 +25,13 @@ class kolab_format_journal extends kolab_format { public $CTYPE = 'application/calendar+xml'; + public $CTYPEv2 = 'application/x-vnd.kolab.journal'; - protected $read_func = 'kolabformat::readJournal'; - protected $write_func = 'kolabformat::writeJournal'; + protected $objclass = 'Journal'; + protected $read_func = 'readJournal'; + protected $write_func = 'writeJournal'; - function __construct($xmldata = null) - { - $this->obj = new Journal; - $this->xmldata = $xmldata; - } - /** * Set properties to the kolabformat object * @@ -43,14 +39,8 @@ */ public function set(&$object) { - $this->init(); - - // set some automatic values if missing - if (!empty($object['uid'])) - $this->obj->setUid($object['uid']); - - $object['changed'] = new DateTime('now', self::$timezone); - $this->obj->setLastModified(self::get_datetime($object['changed'], new DateTimeZone('UTC'))); + // set common object properties + parent::set($object); // TODO: set object propeties @@ -68,40 +58,20 @@ } /** - * Load data from old Kolab2 format - */ - public function fromkolab2($record) - { - $object = array( - 'uid' => $record['uid'], - 'changed' => $record['last-modification-date'], - ); - - // TODO: implement this - - $this->data = $object; - } - - /** * Convert the Configuration object into a hash array data structure * + * @param array Additional data for merge + * * @return array Config object data as hash array */ - public function to_array() + public function to_array($data = array()) { // return cached result if (!empty($this->data)) return $this->data; - $this->init(); - - // read object properties - $object = array( - 'uid' => $this->obj->uid(), - 'created' => self::php_datetime($this->obj->created()), - 'changed' => self::php_datetime($this->obj->lastModified()), - ); - + // read common object props into local data object + $object = parent::to_array($data); // TODO: read object properties
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/lib/kolab_format_note.php -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/lib/kolab_format_note.php
Changed
@@ -25,17 +25,13 @@ class kolab_format_note extends kolab_format { public $CTYPE = 'application/x-vnd.kolab.note'; + public $CTYPEv2 = 'application/x-vnd.kolab.note'; - protected $read_func = 'kolabformat::readNote'; - protected $write_func = 'kolabformat::writeNote'; + protected $objclass = 'Note'; + protected $read_func = 'readNote'; + protected $write_func = 'writeNote'; - function __construct($xmldata = null) - { - $this->obj = new Note; - $this->xmldata = $xmldata; - } - /** * Set properties to the kolabformat object * @@ -43,14 +39,8 @@ */ public function set(&$object) { - $this->init(); - - // set some automatic values if missing - if (!empty($object['uid'])) - $this->obj->setUid($object['uid']); - - $object['changed'] = new DateTime('now', self::$timezone); - $this->obj->setLastModified(self::get_datetime($object['changed'], new DateTimeZone('UTC'))); + // set common object properties + parent::set($object); // TODO: set object propeties @@ -68,39 +58,20 @@ } /** - * Load data from old Kolab2 format - */ - public function fromkolab2($record) - { - $object = array( - 'uid' => $record['uid'], - 'changed' => $record['last-modification-date'], - ); - - - $this->data = $object; - } - - /** * Convert the Configuration object into a hash array data structure * + * @param array Additional data for merge + * * @return array Config object data as hash array */ - public function to_array() + public function to_array($data = array()) { // return cached result if (!empty($this->data)) return $this->data; - $this->init(); - - // read object properties - $object = array( - 'uid' => $this->obj->uid(), - 'created' => self::php_datetime($this->obj->created()), - 'changed' => self::php_datetime($this->obj->lastModified()), - ); - + // read common object props into local data object + $object = parent::to_array($data); // TODO: read object properties
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/lib/kolab_format_task.php -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/lib/kolab_format_task.php
Changed
@@ -24,15 +24,12 @@ class kolab_format_task extends kolab_format_xcal { - protected $read_func = 'kolabformat::readTodo'; - protected $write_func = 'kolabformat::writeTodo'; + public $CTYPEv2 = 'application/x-vnd.kolab.task'; + protected $objclass = 'Todo'; + protected $read_func = 'readTodo'; + protected $write_func = 'writeTodo'; - function __construct($xmldata = null) - { - $this->obj = new Todo; - $this->xmldata = $xmldata; - } /** * Set properties to the kolabformat object @@ -41,8 +38,6 @@ */ public function set(&$object) { - $this->init(); - // set common xcal properties parent::set($object); @@ -74,18 +69,18 @@ /** * Convert the Configuration object into a hash array data structure * + * @param array Additional data for merge + * * @return array Config object data as hash array */ - public function to_array() + public function to_array($data = array()) { // return cached result if (!empty($this->data)) return $this->data; - $this->init(); - // read common xcal props - $object = parent::to_array(); + $object = parent::to_array($data); $object['complete'] = intval($this->obj->percentComplete()); @@ -105,21 +100,6 @@ } /** - * Load data from old Kolab2 format - */ - public function fromkolab2($record) - { - $object = array( - 'uid' => $record['uid'], - 'changed' => $record['last-modification-date'], - ); - - // TODO: implement this - - $this->data = $object; - } - - /** * Callback for kolab_storage_cache to get object specific tags to cache * * @return array List of tags to save in cache
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/lib/kolab_format_xcal.php -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/lib/kolab_format_xcal.php
Changed
@@ -43,6 +43,14 @@ 'CHAIR' => kolabformat::Chair, ); + protected $cutype_map = array( + 'INDIVIDUAL' => kolabformat::CutypeIndividual, + 'GROUP' => kolabformat::CutypeGroup, + 'ROOM' => kolabformat::CutypeRoom, + 'RESOURCE' => kolabformat::CutypeResource, + 'UNKNOWN' => kolabformat::CutypeUnknown, + ); + protected $rrule_type_map = array( 'MINUTELY' => RecurrenceRule::Minutely, 'HOURLY' => RecurrenceRule::Hourly, @@ -88,21 +96,25 @@ /** * Convert common xcard properties into a hash array data structure * + * @param array Additional data for merge + * * @return array Object data as hash array */ - public function to_array() + public function to_array($data = array()) { + // read common object props + $object = parent::to_array($data); + $status_map = array_flip($this->status_map); $sensitivity_map = array_flip($this->sensitivity_map); - $object = array( - 'uid' => $this->obj->uid(), - 'created' => self::php_datetime($this->obj->created()), - 'changed' => self::php_datetime($this->obj->lastModified()), + $object += array( + 'sequence' => intval($this->obj->sequence()), 'title' => $this->obj->summary(), 'location' => $this->obj->location(), 'description' => $this->obj->description(), - 'status' => $this->status_map[$this->obj->status()], + 'url' => $this->obj->url(), + 'status' => $status_map[$this->obj->status()], 'sensitivity' => $sensitivity_map[$this->obj->classification()], 'priority' => $this->obj->priority(), 'categories' => self::vector2array($this->obj->categories()), @@ -110,7 +122,7 @@ ); // read organizer and attendees - if ($organizer = $this->obj->organizer()) { + if (($organizer = $this->obj->organizer()) && ($organizer->email() || $organizer->name())) { $object['organizer'] = array( 'email' => $organizer->email(), 'name' => $organizer->name(), @@ -118,18 +130,22 @@ } $role_map = array_flip($this->role_map); + $cutype_map = array_flip($this->cutype_map); $part_status_map = array_flip($this->part_status_map); $attvec = $this->obj->attendees(); for ($i=0; $i < $attvec->size(); $i++) { $attendee = $attvec->get($i); $cr = $attendee->contact(); - $object['attendees'][] = array( - 'role' => $role_map[$attendee->role()], - 'status' => $part_status_map[$attendee->partStat()], - 'rsvp' => $attendee->rsvp(), - 'email' => $cr->email(), - 'name' => $cr->name(), - ); + if ($cr->email() != $object['organizer']['email']) { + $object['attendees'][] = array( + 'role' => $role_map[$attendee->role()], + 'cutype' => $cutype_map[$attendee->cutype()], + 'status' => $part_status_map[$attendee->partStat()], + 'rsvp' => $attendee->rsvp(), + 'email' => $cr->email(), + 'name' => $cr->name(), + ); + } } // read recurrence rule @@ -167,9 +183,9 @@ $object['recurrence']['BYMONTH'] = join(',', self::vector2array($bymonth)); } - if ($exceptions = $this->obj->exceptionDates()) { - for ($i=0; $i < $exceptions->size(); $i++) { - if ($exdate = self::php_datetime($exceptions->get($i))) + if ($exdates = $this->obj->exceptionDates()) { + for ($i=0; $i < $exdates->size(); $i++) { + if ($exdate = self::php_datetime($exdates->get($i))) $object['recurrence']['EXDATE'][] = $exdate; } } @@ -202,6 +218,27 @@ } } + // 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; } @@ -213,21 +250,17 @@ */ public function set(&$object) { - // set some automatic values if missing - if (!$this->obj->created()) { - if (!empty($object['created'])) - $object['created'] = new DateTime('now', self::$timezone); - $this->obj->setCreated(self::get_datetime($object['created'])); - } + $this->init(); - if (!empty($object['uid'])) - $this->obj->setUid($object['uid']); + $is_new = !$this->obj->uid(); - $object['changed'] = new DateTime('now', self::$timezone); - $this->obj->setLastModified(self::get_datetime($object['changed'], new DateTimeZone('UTC'))); + // set common object properties + parent::set($object); - // increment sequence - $this->obj->setSequence($this->obj->sequence()+1); + // increment sequence on updates + if (empty($object['sequence'])) + $object['sequence'] = !$is_new ? $this->obj->sequence()+1 : 0; + $this->obj->setSequence($object['sequence']); $this->obj->setSummary($object['title']); $this->obj->setLocation($object['location']); @@ -235,6 +268,7 @@ $this->obj->setPriority($object['priority']); $this->obj->setClassification($this->sensitivity_map[$object['sensitivity']]); $this->obj->setCategories(self::array2vector($object['categories'])); + $this->obj->setUrl(strval($object['url'])); // process event attendees $attendees = new vectorattendee; @@ -242,14 +276,15 @@ if ($attendee['role'] == 'ORGANIZER') { $object['organizer'] = $attendee; } - else { + else if ($attendee['email'] != $object['organizer']['email']) { $cr = new ContactReference(ContactReference::EmailReference, $attendee['email']); $cr->setName($attendee['name']); $att = new Attendee; $att->setContact($cr); - $att->setPartStat($this->status_map[$attendee['status']]); + $att->setPartStat($this->part_status_map[$attendee['status']]); $att->setRole($this->role_map[$attendee['role']] ? $this->role_map[$attendee['role']] : kolabformat::Required); + $att->setCutype($this->cutype_map[$attendee['cutype']] ? $this->cutype_map[$attendee['cutype']] : kolabformat::CutypeIndividual); $att->setRSVP((bool)$attendee['rsvp']); if ($att->isValid()) { @@ -273,8 +308,10 @@ } // save recurrence rule + $rr = new RecurrenceRule; + $rr->setFrequency(RecurrenceRule::FreqNone); + if ($object['recurrence']) { - $rr = new RecurrenceRule; $rr->setFrequency($this->rrule_type_map[$object['recurrence']['FREQ']]); if ($object['recurrence']['INTERVAL']) @@ -314,8 +351,6 @@ $rr->setEnd(self::get_datetime($object['recurrence']['UNTIL'], null, true)); if ($rr->isValid()) { - $this->obj->setRecurrenceRule($rr);
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/lib/kolab_storage.php -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/lib/kolab_storage.php
Changed
@@ -5,6 +5,7 @@ * * @version @package_version@ * @author Thomas Bruederli <bruederli@kolabsys.com> + * @author Aleksander Machniak <machniak@kolabsys.com> * * Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com> * @@ -26,18 +27,37 @@ { const CTYPE_KEY = '/shared/vendor/kolab/folder-type'; const CTYPE_KEY_PRIVATE = '/private/vendor/kolab/folder-type'; - const COLOR_KEY_SHARED = '/shared/vendor/kolab/color'; + const COLOR_KEY_SHARED = '/shared/vendor/kolab/color'; const COLOR_KEY_PRIVATE = '/private/vendor/kolab/color'; - const SERVERSIDE_SUBSCRIPTION = 0; - const CLIENTSIDE_SUBSCRIPTION = 1; + const NAME_KEY_SHARED = '/shared/vendor/kolab/displayname'; + const NAME_KEY_PRIVATE = '/private/vendor/kolab/displayname'; + public static $version = '3.0'; public static $last_error; private static $ready = false; + private static $subscriptions; + private static $states; private static $config; - private static $cache; 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 @@ -49,6 +69,7 @@ $rcmail = rcube::get_instance(); self::$config = $rcmail->config; + self::$version = strval($rcmail->config->get('kolab_format_version', self::$version)); self::$imap = $rcmail->get_storage(); self::$ready = class_exists('kolabformat') && (self::$imap->get_capability('METADATA') || self::$imap->get_capability('ANNOTATEMORE') || self::$imap->get_capability('ANNOTATEMORE2')); @@ -61,6 +82,18 @@ )); self::$imap->set_pagesize(9999); } + else if (!class_exists('kolabformat')) { + rcube::raise_error(array( + 'code' => 900, 'type' => 'php', + 'message' => "required kolabformat module not found" + ), true); + } + else { + rcube::raise_error(array( + 'code' => 900, 'type' => 'php', + 'message' => "IMAP server doesn't support METADATA or ANNOTATEMORE" + ), true); + } return self::$ready; } @@ -78,7 +111,7 @@ $folders = $folderdata = array(); if (self::setup()) { - foreach ((array)self::list_folders('', '*', $type, false, $folderdata) as $foldername) { + foreach ((array)self::list_folders('', '*', $type, null, $folderdata) as $foldername) { $folders[$foldername] = new kolab_storage_folder($foldername, $folderdata[$foldername]); } } @@ -86,6 +119,23 @@ return $folders; } + /** + * Getter for the storage folder for the given type + * + * @param string Data type to list folders for (contact,distribution-list,event,task,note) + * @return object kolab_storage_folder The folder object + */ + public static function get_default_folder($type) + { + if (self::setup()) { + foreach ((array)self::list_folders('', '*', $type . '.default', false, $folderdata) as $foldername) { + return new kolab_storage_folder($foldername, $folderdata[$foldername]); + } + } + + return null; + } + /** * Getter for a specific storage folder @@ -178,13 +228,14 @@ /** * Creates IMAP folder * - * @param string $name Folder name (UTF7-IMAP) - * @param string $type Folder type - * @param bool $subscribed Sets folder subscription + * @param string $name Folder name (UTF7-IMAP) + * @param string $type Folder type + * @param bool $subscribed Sets folder subscription + * @param bool $active Sets folder state (client-side subscription) * * @return bool True on success, false on failure */ - public static function folder_create($name, $type = null, $subscribed = false) + public static function folder_create($name, $type = null, $subscribed = false, $active = false) { self::setup(); @@ -197,6 +248,10 @@ if (!$saved) { self::$imap->delete_folder($name); } + // activate folder + else if ($active) { + self::set_state($name, true); + } } } @@ -208,6 +263,7 @@ return false; } + /** * Renames IMAP folder * @@ -233,10 +289,12 @@ * Does additional checks for permissions and folder name restrictions * * @param array Hash array with folder properties and metadata - * - name: Folder name - * - oldname: Old folder name when changed - * - parent: Parent folder to create the new one in - * - type: Folder type to create + * - name: Folder name + * - oldname: Old folder name when changed + * - parent: Parent folder to create the new one in + * - type: Folder type to create + * - subscribed: Subscribed flag (IMAP subscription) + * - active: Activation flag (client-side subscription) * @return mixed New folder name or False on failure */ public static function folder_update(&$prop) @@ -266,7 +324,7 @@ else { // these characters are problematic e.g. when used in LIST/LSUB foreach (array($delimiter, '%', '*') as $char) { - if (strpos($folder, $delimiter) !== false) { + if (strpos($folder, $char) !== false) { self::$last_error = 'forbiddencharacter'; return false; } @@ -305,21 +363,11 @@ } // create new folder else { - $result = self::folder_create($folder, $prop['type'], $prop['subscribed'] === self::SERVERSIDE_SUBSCRIPTION); + $result = self::folder_create($folder, $prop['type'], $prop['subscribed'], $prop['active']); } - // save color in METADATA - // TODO: also save 'showalarams' and other properties here - - if ($result && $prop['color']) { - $meta_saved = false; - $ns = self::$imap->folder_namespace($folder); - if ($ns == 'personal') // save in shared namespace for personal folders - $meta_saved = self::$imap->set_metadata($folder, array(self::COLOR_KEY_SHARED => $prop['color'])); - if (!$meta_saved) // try in private namespace - $meta_saved = self::$imap->set_metadata($folder, array(self::COLOR_KEY_PRIVATE => $prop['color'])); - if ($meta_saved) - unset($prop['color']); // unsetting will prevent fallback to local user prefs + if ($result) { + self::set_folder_props($folder, $prop); } return $result ? $folder : false; @@ -339,6 +387,14 @@
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/lib/kolab_storage_cache.php -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/lib/kolab_storage_cache.php
Changed
@@ -35,8 +35,13 @@ private $synched = false; private $synclock = false; private $ready = false; - private $max_sql_packet = 1046576; // 1 MB - 2000 bytes - private $binary_cols = array('photo','pgppublickey','pkcs7publickey'); + 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', + ); /** @@ -52,9 +57,6 @@ if ($this->enabled) { // remove sync-lock on script termination $rcmail->add_shutdown_function(array($this, '_sync_unlock')); - - // read max_allowed_packet from mysql config - $this->max_sql_packet = min($this->db->get_variable('max_allowed_packet', 1048500), 4*1024*1024) - 2000; // mysql limit or max 4 MB } if ($storage_folder) @@ -92,7 +94,7 @@ return; // increase time limit - @set_time_limit(500); + @set_time_limit($this->max_sync_lock_time); // lock synchronization for this folder or wait if locked $this->_sync_lock(); @@ -157,7 +159,7 @@ { // delegate to another cache instance if ($foldername && $foldername != $this->folder->name) { - return kolab_storage::get_folder($foldername)->cache->get($msguid, $object); + return kolab_storage::get_folder($foldername)->cache->get($msguid, $type); } // load object if not in memory @@ -238,8 +240,8 @@ $result = $this->db->query( "INSERT INTO kolab_cache ". - " (resource, type, msguid, uid, created, changed, data, xml, dtstart, dtend, tags, words)". - " VALUES (?, ?, ?, ?, " . $this->db->now() . ", ?, ?, ?, ?, ?, ?, ?)", + " (resource, type, msguid, uid, created, changed, data, xml, dtstart, dtend, tags, words, filename)". + " VALUES (?, ?, ?, ?, " . $this->db->now() . ", ?, ?, ?, ?, ?, ?, ?, ?)", $this->resource_uri, $objtype, $msguid, @@ -250,7 +252,8 @@ $sql_data['dtstart'], $sql_data['dtend'], $sql_data['tags'], - $sql_data['words'] + $sql_data['words'], + $sql_data['filename'] ); if (!$this->db->affected_rows($result)) { @@ -274,12 +277,12 @@ * @param string Entry's Object UID * @param string Target IMAP folder to move it to */ - public function move($msguid, $objuid, $target_folder) + public function move($msguid, $uid, $target_folder) { $target = kolab_storage::get_folder($target_folder); // resolve new message UID in target folder - if ($new_msguid = $target->cache->uid2msguid($objuid)) { + if ($new_msguid = $target->cache->uid2msguid($uid)) { $this->db->query( "UPDATE kolab_cache SET resource=?, msguid=? ". "WHERE resource=? AND msguid=? AND type<>?", @@ -363,6 +366,15 @@ // TODO: post-filter result according to query } + // We don't want to cache big results in-memory, however + // if we select only one object here, there's a big chance we will need it later + if (!$uids && count($result) == 1) { + if ($msguid = $result[0]['_msguid']) { + $this->uid2msg[$result[0]['uid']] = $msguid; + $this->objects[$msguid] = $result[0]; + } + } + return $result; } @@ -407,7 +419,17 @@ { $sql_where = ''; foreach ($query as $param) { - if ($param[1] == '=' && is_array($param[2])) { + if (is_array($param[0])) { + $subq = array(); + foreach ($param[0] as $q) { + $subq[] = preg_replace('/^\s*AND\s+/i', '', $this->_sql_where(array($q))); + } + if (!empty($subq)) { + $sql_where .= ' AND (' . implode($param[1] == 'OR' ? ' OR ' : ' AND ', $subq) . ')'; + } + continue; + } + else if ($param[1] == '=' && is_array($param[2])) { $qvalue = '(' . join(',', array_map(array($this->db, 'quote'), $param[2])) . ')'; $param[1] = 'IN'; } @@ -506,9 +528,8 @@ */ private function _serialize($object) { - $bincols = array_flip($this->binary_cols); $sql_data = array('changed' => null, 'dtstart' => null, 'dtend' => null, 'xml' => '', 'tags' => '', 'words' => ''); - $objtype = $object['_type'] ? $object['_type'] : $this->folder->type; + $objtype = $object['_type'] ? $object['_type'] : $this->folder->type; // set type specific values if ($objtype == 'event') { @@ -517,8 +538,8 @@ $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']) { - $recurrence = new kolab_date_recurrence($object); + 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')); } } @@ -528,26 +549,33 @@ 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']; + } + } if ($object['changed']) { $sql_data['changed'] = date('Y-m-d H:i:s', is_object($object['changed']) ? $object['changed']->format('U') : $object['changed']); } if ($object['_formatobj']) { - $sql_data['xml'] = preg_replace('!(</?[a-z0-9:-]+>)[\n\r\t\s]+!ms', '$1', (string)$object['_formatobj']->write()); - $sql_data['tags'] = ' ' . join(' ', $object['_formatobj']->get_tags()) . ' '; // pad with spaces for strict/prefix search + $sql_data['xml'] = preg_replace('!(</?[a-z0-9:-]+>)[\n\r\t\s]+!ms', '$1', (string)$object['_formatobj']->write(3.0)); + $sql_data['tags'] = ' ' . join(' ', $object['_formatobj']->get_tags()) . ' '; // pad with spaces for strict/prefix search $sql_data['words'] = ' ' . join(' ', $object['_formatobj']->get_words()) . ' '; } // extract object data $data = array(); foreach ($object as $key => $val) { + // skip empty properties if ($val === "" || $val === null) { - // skip empty properties continue; } - if (isset($bincols[$key])) { - $data[$key] = base64_encode($val); + // mark binary data to be extracted from xml on unserialize() + if (isset($this->binary_items[$key])) { + $data[$key] = true; } else if ($key[0] != '_') { $data[$key] = $val; @@ -573,16 +601,18 @@ $object = unserialize($sql_arr['data']); // decode binary properties - foreach ($this->binary_cols as $key) { - if (!empty($object[$key])) - $object[$key] = base64_decode($object[$key]); + foreach ($this->binary_items as $key => $regexp) { + if (!empty($object[$key]) && preg_match($regexp, $sql_arr['xml'], $m)) { + $object[$key] = base64_decode($m[1]); + } } // add meta data $object['_type'] = $sql_arr['type']; $object['_msguid'] = $sql_arr['msguid']; $object['_mailbox'] = $this->folder->name; - $object['_formatobj'] = kolab_format::factory($sql_arr['type'], $sql_arr['xml']); + $object['_size'] = strlen($sql_arr['xml']); + $object['_formatobj'] = kolab_format::factory($sql_arr['type'], 3.0, $sql_arr['xml']); return $object; } @@ -615,14 +645,15 @@
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/lib/kolab_storage_folder.php -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/lib/kolab_storage_folder.php
Changed
@@ -5,6 +5,7 @@ * * @version @package_version@ * @author Thomas Bruederli <bruederli@kolabsys.com> + * @author Aleksander Machniak <machniak@kolabsys.com> * * Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com> * @@ -48,11 +49,12 @@ public $cache; private $type_annotation; + private $namespace; private $imap; private $info; + private $idata; private $owner; private $resource_uri; - private $uid2msg = array(); /** @@ -98,6 +100,16 @@ return $this->info; } + /** + * Make IMAP folder data available for this folder + */ + public function get_imap_data() + { + if (!isset($this->idata)) + $this->idata = $this->imap->folder_data($this->name); + + return $this->idata; + } /** * Returns IMAP metadata/annotations (GETMETADATA/GETANNOTATION) @@ -148,7 +160,6 @@ break; default: - $owner = ''; list($prefix, $user) = explode($this->imap->get_hierarchy_delimiter(), $info['name']); if (strpos($user, '@') === false) { $domain = strstr($rcmail->get_user_name(), '@'); @@ -170,7 +181,9 @@ */ public function get_namespace() { - return $this->imap->folder_namespace($this->name); + if (!isset($this->namespace)) + $this->namespace = $this->imap->folder_namespace($this->name); + return $this->namespace; } @@ -191,6 +204,35 @@ /** + * Get the display name value of this folder + * + * @return string Folder name + */ + public function get_name() + { + return kolab_storage::object_name($this->name, $this->namespace); + } + + + /** + * Get the color value stored in metadata + * + * @param string Default color value to return if not set + * @return mixed Color value from IMAP metadata or $default is not set + */ + public function get_color($default = null) + { + // color is defined in folder METADATA + $metadata = $this->get_metadata(array(kolab_storage::COLOR_KEY_PRIVATE, kolab_storage::COLOR_KEY_SHARED)); + if (($color = $metadata[kolab_storage::COLOR_KEY_PRIVATE]) || ($color = $metadata[kolab_storage::COLOR_KEY_SHARED])) { + return $color; + } + + return $default; + } + + + /** * Compose a unique resource URI for this IMAP folder */ public function get_resource_uri() @@ -204,7 +246,7 @@ if (is_array($nsdata[0]) && strlen($nsdata[0][0]) && strpos($this->name, $nsdata[0][0]) === 0) { $subpath = substr($this->name, strlen($nsdata[0][0])); if ($ns == 'other') { - list($user, $suffix) = explode($nsdata[0][1], $subpath); + list($user, $suffix) = explode($nsdata[0][1], $subpath, 2); $subpath = $suffix; } } @@ -218,46 +260,47 @@ } /** - * Check subscription status of this folder + * Check activation status of this folder * - * @param string Subscription type (kolab_storage::SERVERSIDE_SUBSCRIPTION or kolab_storage::CLIENTSIDE_SUBSCRIPTION) - * @return boolean True if subscribed, false if not + * @return boolean True if enabled, false if not */ - public function is_subscribed($type = 0) + public function is_active() { - static $subscribed; // local cache - - if ($type == kolab_storage::SERVERSIDE_SUBSCRIPTION) { - if (!$subscribed) - $subscribed = $this->imap->list_folders_subscribed(); + return kolab_storage::folder_is_active($this->name); + } - return in_array($this->name, $subscribed); - } - else if (kolab_storage::CLIENTSIDE_SUBSCRIPTION) { - // TODO: implement this - return true; - } + /** + * Change activation status of this folder + * + * @param boolean The desired subscription status: true = active, false = not active + * + * @return True on success, false on error + */ + public function activate($active) + { + return $active ? kolab_storage::folder_activate($this->name) : kolab_storage::folder_deactivate($this->name); + } - return false; + /** + * Check subscription status of this folder + * + * @return boolean True if subscribed, false if not + */ + public function is_subscribed() + { + return kolab_storage::folder_is_subscribed($this->name); } /** * Change subscription status of this folder * * @param boolean The desired subscription status: true = subscribed, false = not subscribed - * @param string Subscription type (kolab_storage::SERVERSIDE_SUBSCRIPTION or kolab_storage::CLIENTSIDE_SUBSCRIPTION) + * * @return True on success, false on error */ - public function subscribe($subscribed, $type = 0) + public function subscribe($subscribed) { - if ($type == kolab_storage::SERVERSIDE_SUBSCRIPTION) { - return $subscribed ? $this->imap->subscribe($this->name) : $this->imap->unsubscribe($this->name); - } - else { - // TODO: implement this - } - - return false; + return $subscribed ? kolab_storage::folder_subscribe($this->name) : kolab_storage::folder_unsubscribe($this->name); } @@ -369,17 +412,22 @@ /** * Getter for a single Kolab object, identified by its UID * - * @param string Object UID + * @param string $uid Object UID + * @param string $type Object type (e.g. contact, event, todo, journal, note, configuration) + * Defaults to folder type + * * @return array The Kolab object represented as hash array */ - public function get_object($uid) + public function get_object($uid, $type = null) { // synchronize caches $this->cache->synchronize(); $msguid = $this->cache->uid2msguid($uid); - if ($msguid && ($object = $this->cache->get($msguid))) + + if ($msguid && ($object = $this->cache->get($msguid, $type))) { return $object; + }
View file
kolab-syncroton-2.1.0.tar.gz/lib/plugins/libkolab/libkolab.php -> kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/libkolab.php
Changed
@@ -46,20 +46,9 @@ kolab_format::$timezone = new DateTimeZone($rcmail->config->get('timezone', 'GMT')); } catch (Exception $e) { - raise_error($e, true); + rcube::raise_error($e, true); kolab_format::$timezone = new DateTimeZone('GMT'); } - - // load (old) dependencies if available - if (@include_once('Horde/Util.php')) { - include_once 'Horde/Kolab/Format.php'; - include_once 'Horde/Kolab/Format/XML.php'; - include_once 'Horde/Kolab/Format/XML/contact.php'; - include_once 'Horde/Kolab/Format/XML/event.php'; - include_once 'Horde_Kolab_Format_XML_configuration.php'; - - String::setDefaultCharset('UTF-8'); - } } /** @@ -70,6 +59,4 @@ $p['fetch_headers'] = trim($p['fetch_headers'] .' X-KOLAB-TYPE X-KOLAB-MIME-VERSION'); return $p; } - - }
View file
kolab-syncroton-2.2.0.tar.gz/lib/plugins/libkolab/package.xml
Added
@@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="UTF-8"?> +<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 + http://pear.php.net/dtd/tasks-1.0.xsd + http://pear.php.net/dtd/package-2.0 + http://pear.php.net/dtd/package-2.0.xsd"> + <name>libkolab</name> + <uri>http://git.kolab.org/roundcubemail-plugins-kolab/</uri> + <summary>Kolab core library</summary> + <description>Plugin to setup a basic environment for the interaction with a Kolab server.</description> + <lead> + <name>Thomas Bruederli</name> + <user>bruederli</user> + <email>bruederli@kolabsys.com</email> + <active>yes</active> + </lead> + <developer> + <name>Alensader Machniak</name> + <user>machniak</user> + <email>machniak@kolabsys.com</email> + <active>yes</active> + </developer> + <date>2013-04-19</date> + <version> + <release>0.9</release> + <api>0.9</api> + </version> + <stability> + <release>stable</release> + <api>stable</api> + </stability> + <license uri="http://www.gnu.org/licenses/agpl.html">GNU AGPLv3</license> + <notes>-</notes> + <contents> + <dir baseinstalldir="/" name="/"> + <file name="libkolab.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file name="lib/kolab_format.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file name="lib/kolab_format_configuration.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file name="lib/kolab_format_contact.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file name="lib/kolab_format_distributionlist.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file name="lib/kolab_format_event.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file name="lib/kolab_format_file.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file name="lib/kolab_format_journal.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file name="lib/kolab_format_note.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file name="lib/kolab_format_task.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file name="lib/kolab_format_xcal.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file name="lib/kolab_storage.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file name="lib/kolab_storage_cache.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file name="lib/kolab_storage_folder.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + <file name="lib/kolab_date_recurrence.php" role="php"> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + + <file name="bin/modcache.php" role="php"></file> + + <file name="config.inc.php.dist" role="data"></file> + <file name="LICENSE" role="data"></file> + <file name="README" role="data"></file> + <file name="UPGRADING" role="data"></file> + </dir> + <!-- / --> + </contents> + <dependencies> + <required> + <php> + <min>5.3.1</min> + </php> + <pearinstaller> + <min>1.7.0</min> + </pearinstaller> + </required> + </dependencies> + <phprelease/> +</package>
View file
kolab-syncroton-2.1.0.tar.gz/tests/data.php -> kolab-syncroton-2.2.0.tar.gz/tests/data.php
Changed
@@ -22,7 +22,7 @@ $event = new Syncroton_Model_Event($xml->ApplicationData); $data = new kolab_sync_data_test; - $result = $data->recurrence_to_kolab($event->recurrence); + $result = $data->recurrence_to_kolab($event); $this->assertEquals('DAILY', $result['FREQ']); $this->assertEquals(1, $result['INTERVAL']); @@ -42,7 +42,7 @@ $xml = new SimpleXMLElement($xml); $event = new Syncroton_Model_Event($xml->ApplicationData); - $result = $data->recurrence_to_kolab($event->recurrence); + $result = $data->recurrence_to_kolab($event, null); $this->assertEquals('WEEKLY', $result['FREQ']); $this->assertEquals(1, $result['INTERVAL']); @@ -59,16 +59,16 @@ { } - public function recurrence_to_kolab($data, $timezone = null) + public function recurrence_to_kolab($data) { - return parent::recurrence_to_kolab($data, $timezone); + return parent::recurrence_to_kolab($data, null); } - function toKolab(Syncroton_Model_IEntry $data, $folderId, $entry = null) + function toKolab(Syncroton_Model_IEntry $data, $folderId, $entry = null, $timezone = null) { } - function getEntry(Syncroton_Model_SyncCollection $collection, $serverId) + function getEntry(Syncroton_Model_SyncCollection $collection, $serverId, $as_array = false) { } }
View file
kolab-syncroton-2.1.0.tar.gz/tests/phpunit.xml -> kolab-syncroton-2.2.0.tar.gz/tests/phpunit.xml
Changed
@@ -7,6 +7,7 @@ <file>data.php</file> <file>data_tasks.php</file> <file>message.php</file> + <file>timezone_converter.php</file> </testsuite> </testsuites> </phpunit>
View file
kolab-syncroton-2.2.0.tar.gz/tests/timezone_converter.php
Added
@@ -0,0 +1,28 @@ +<?php + +class timezone_converter extends PHPUnit_Framework_TestCase +{ + function setUp() + { + } + + + function test_list_timezones() + { + $converter = timezone_converter_test::getInstance(); + + $input = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAEAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAFAAEAAAAAAAAAxP///w=='; + $output = $converter->getListOfTimezones($input, 'UTC'); + + $this->assertTrue(is_array($output)); + } +} + +class timezone_converter_test extends kolab_sync_timezone_converter +{ + // disable cache + function getCache() + { + return null; + } +}
View file
kolab-syncroton.dsc
Changed
@@ -2,7 +2,7 @@ Source: kolab-syncroton Binary: kolab-syncroton Architecture: all -Version: 2.1.0-1 +Version: 2.2.0-1 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.1.0.tar.gz + 00000000000000000000000000000000 0 kolab-syncroton-2.2.0.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
.