Projects
Kolab:3.4
kolab-syncroton
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 27
View file
kolab-syncroton.spec
Changed
@@ -25,8 +25,8 @@ %global _ap_sysconfdir %{_sysconfdir}/%{httpd_name} Name: kolab-syncroton -Version: 2.2.2 -Release: 3%{?dist} +Version: 2.2.3 +Release: 1%{?dist} Summary: ActiveSync for Kolab Groupware Group: Applications/Internet @@ -197,6 +197,16 @@ %attr(0770,%{httpd_user},%{httpd_group}) %{_var}/log/%{name} %changelog +* Tue Nov 12 2013 Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> - 2.2.3-1 +- New upstream release +- Fixes: + 2385 - Do not depend on kolab_cache behavior + 2386 - Improve performance by skipping IMAP SEARCH when checking + mail folder for changes + ???? - Skip SELECT/DELETE ... WHERE id = NULL queries + 2383 - Enable alarms synchronization by default + 2431 - Fix event attendees synchronization from server to the device + * Mon Nov 11 2013 Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> - 2.2.2-3 - Make sure we use the readily available plugins and libraries
View file
debian.changelog
Changed
@@ -1,3 +1,15 @@ +kolab-syncroton (2.2.3-0~kolab1) unstable; urgency=low + + * New upstream release + * 2385 - Do not depend on kolab_cache behavior + * 2386 - Improve performance by skipping IMAP SEARCH when checking + mail folder for changes + * ???? - Skip SELECT/DELETE ... WHERE id = NULL queries + * 2383 - Enable alarms synchronization by default + * 2431 - Fix event attendees synchronization from server to the device + + -- Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Tue, 12 Nov 2013 15:13:40 +0200 + kolab-syncroton (2.2.2-0~kolab2) unstable; urgency=low * Fix bug #2382
View file
kolab-syncroton-2.2.2.tar.gz/lib/ext/Mail/mime.php -> kolab-syncroton-2.2.3.tar.gz/lib/ext/Mail/mime.php
Changed
@@ -48,7 +48,7 @@ * @author Aleksander Machniak <alec@php.net> * @copyright 2003-2006 PEAR <pear-group@php.net> * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version 1.8.5 + * @version CVS: $Id$ * @link http://pear.php.net/package/Mail_mime * * This class is based on HTML Mime Mail class from @@ -89,7 +89,7 @@ * @author Sean Coates <sean@php.net> * @copyright 2003-2006 PEAR <pear-group@php.net> * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.8.5 + * @version Release: @package_version@ * @link http://pear.php.net/package/Mail_mime */ class Mail_mime @@ -245,7 +245,7 @@ } } else { $cont = $this->_file2str($data); - if (PEAR::isError($cont)) { + if ($this->_isError($cont)) { return $cont; } if (!$append) { @@ -254,6 +254,7 @@ $this->_txtbody .= $cont; } } + return true; } @@ -286,7 +287,7 @@ $this->_htmlbody = $data; } else { $cont = $this->_file2str($data); - if (PEAR::isError($cont)) { + if ($this->_isError($cont)) { return $cont; } $this->_htmlbody = $cont; @@ -336,7 +337,7 @@ $filedata = null; $bodyfile = $file; } else { - if (PEAR::isError($filedata = $this->_file2str($file))) { + if ($this->_isError($filedata = $this->_file2str($file))) { return $filedata; } } @@ -347,7 +348,7 @@ } if (!$content_id) { - $content_id = md5(uniqid(time())); + $content_id = preg_replace('/[^0-9a-zA-Z]/', '', uniqid(time(), true)); } $this->_html_images[] = array( @@ -416,12 +417,12 @@ $filedata = null; $bodyfile = $file; } else { - if (PEAR::isError($filedata = $this->_file2str($file))) { + if ($this->_isError($filedata = $this->_file2str($file))) { return $filedata; } } // Force the name the user supplied, otherwise use $file - $filename = ($name ? $name : $file); + $filename = ($name ? $name : $this->_basename($file)); } else { $filedata = $file; $filename = $name; @@ -429,10 +430,8 @@ if (!strlen($filename)) { $msg = "The supplied filename for the attachment can't be empty"; - $err = PEAR::raiseError($msg); - return $err; + return $this->_raiseError($msg); } - $filename = $this->_basename($filename); $this->_parts[] = array( 'body' => $filedata, @@ -462,20 +461,17 @@ * @return string Contents of $file_name * @access private */ - function &_file2str($file_name) + function _file2str($file_name) { // Check state of file and raise an error properly if (!file_exists($file_name)) { - $err = PEAR::raiseError('File not found: ' . $file_name); - return $err; + return $this->_raiseError('File not found: ' . $file_name); } if (!is_file($file_name)) { - $err = PEAR::raiseError('Not a regular file: ' . $file_name); - return $err; + return $this->_raiseError('Not a regular file: ' . $file_name); } if (!is_readable($file_name)) { - $err = PEAR::raiseError('File is not readable: ' . $file_name); - return $err; + return $this->_raiseError('File is not readable: ' . $file_name); } // Temporarily reset magic_quotes_runtime and read file contents @@ -501,7 +497,7 @@ * @return object The text mimePart object * @access private */ - function &_addTextPart(&$obj, $text) + function &_addTextPart(&$obj = null, $text = '') { $params['content_type'] = 'text/plain'; $params['encoding'] = $this->_build_params['text_encoding']; @@ -510,11 +506,11 @@ if (is_object($obj)) { $ret = $obj->addSubpart($text, $params); - return $ret; } else { $ret = new Mail_mimePart($text, $params); - return $ret; } + + return $ret; } /** @@ -527,7 +523,7 @@ * @return object The html mimePart object * @access private */ - function &_addHtmlPart(&$obj) + function &_addHtmlPart(&$obj = null) { $params['content_type'] = 'text/html'; $params['encoding'] = $this->_build_params['html_encoding']; @@ -536,11 +532,11 @@ if (is_object($obj)) { $ret = $obj->addSubpart($this->_htmlbody, $params); - return $ret; } else { $ret = new Mail_mimePart($this->_htmlbody, $params); - return $ret; } + + return $ret; } /** @@ -553,7 +549,6 @@ */ function &_addMixedPart() { - $params = array(); $params['content_type'] = 'multipart/mixed'; $params['eol'] = $this->_build_params['eol']; @@ -573,17 +568,18 @@ * @return object The multipart/mixed mimePart object * @access private */ - function &_addAlternativePart(&$obj) + function &_addAlternativePart(&$obj = null) { $params['content_type'] = 'multipart/alternative'; $params['eol'] = $this->_build_params['eol']; if (is_object($obj)) { - return $obj->addSubpart('', $params); + $ret = $obj->addSubpart('', $params); } else { $ret = new Mail_mimePart('', $params); - return $ret; } + + return $ret; } /** @@ -597,17 +593,18 @@ * @return object The multipart/mixed mimePart object * @access private */ - function &_addRelatedPart(&$obj) + function &_addRelatedPart(&$obj = null) { $params['content_type'] = 'multipart/related'; $params['eol'] = $this->_build_params['eol']; if (is_object($obj)) { - return $obj->addSubpart('', $params); + $ret = $obj->addSubpart('', $params); } else { $ret = new Mail_mimePart('', $params); - return $ret; } + + return $ret; } /** @@ -701,9 +698,9 @@ * * @param string $separation The separation between these two parts. * @param array $params The Build parameters passed to the - * &get() function. See &get for more info. + * get() function. See get() for more info. * @param array $headers The extra headers that should be passed - * to the &headers() function. + * to the headers() method. * See that function for more info. * @param bool $overwrite Overwrite the existing headers with new. * @@ -719,13 +716,11 @@ $body = $this->get($params); - if (PEAR::isError($body)) { + if ($this->_isError($body)) { return $body; } - $head = $this->txtHeaders($headers, $overwrite); - $mail = $head . $separation . $body; - return $mail; + return $this->txtHeaders($headers, $overwrite) . $separation . $body; } /** @@ -733,7 +728,7 @@ * mail delivery method. * * @param array $params The Build parameters passed to the - * &get() function. See &get for more info. + * get() method. See get() for more info. * * @return mixed The e-mail body or PEAR error object * @access public @@ -749,9 +744,9 @@ * * @param string $filename Output file location * @param array $params The Build parameters passed to the - * &get() function. See &get for more info. + * get() method. See get() for more info. * @param array $headers The extra headers that should be passed - * to the &headers() function. + * to the headers() function. * See that function for more info. * @param bool $overwrite Overwrite the existing headers with new. * @@ -763,8 +758,7 @@ { // Check state of file and raise an error properly if (file_exists($filename) && !is_writable($filename)) { - $err = PEAR::raiseError('File is not writable: ' . $filename); - return $err; + return $this->_raiseError('File is not writable: ' . $filename); } // Temporarily reset magic_quotes_runtime and read file contents @@ -773,15 +767,13 @@ } if (!($fh = fopen($filename, 'ab'))) { - $err = PEAR::raiseError('Unable to open file: ' . $filename); - return $err; + return $this->_raiseError('Unable to open file: ' . $filename); } // Write message headers into file (skipping Content-* headers) $head = $this->txtHeaders($headers, $overwrite, true); if (fwrite($fh, $head) === false) { - $err = PEAR::raiseError('Error writing to file: ' . $filename); - return $err; + return $this->_raiseError('Error writing to file: ' . $filename); } fclose($fh); @@ -798,10 +790,10 @@ /** * Writes (appends) the complete e-mail body into file. - * + * * @param string $filename Output file location * @param array $params The Build parameters passed to the - * &get() function. See &get for more info. + * get() method. See get() for more info. * * @return mixed True or PEAR error object * @access public @@ -811,8 +803,7 @@ { // Check state of file and raise an error properly if (file_exists($filename) && !is_writable($filename)) { - $err = PEAR::raiseError('File is not writable: ' . $filename); - return $err; + return $this->_raiseError('File is not writable: ' . $filename); } // Temporarily reset magic_quotes_runtime and read file contents @@ -821,8 +812,7 @@ } if (!($fh = fopen($filename, 'ab'))) { - $err = PEAR::raiseError('Unable to open file: ' . $filename); - return $err; + return $this->_raiseError('Unable to open file: ' . $filename); } // Write the rest of the message into file @@ -845,7 +835,7 @@ * @return mixed The MIME message content string, null or PEAR error object * @access public */ - function &get($params = null, $filename = null, $skip_head = false) + function get($params = null, $filename = null, $skip_head = false) { if (isset($params)) { while (list($key, $value) = each($params)) { @@ -1005,8 +995,7 @@ } if (!isset($message)) { - $ret = null; - return $ret; + return null; } // Use saved boundary @@ -1020,20 +1009,18 @@ if ($filename) { // Append mimePart message headers and body into file $headers = $message->encodeToFile($filename, $boundary, $skip_head); - if (PEAR::isError($headers)) { + if ($this->_isError($headers)) { return $headers; } $this->_headers = array_merge($this->_headers, $headers); - $ret = null; - return $ret; + return null; } else { $output = $message->encode($boundary, $skip_head); - if (PEAR::isError($output)) { + if ($this->_isError($output)) { return $output; } $this->_headers = array_merge($this->_headers, $output['headers']); - $body = $output['body']; - return $body; + return $output['body']; } } @@ -1051,7 +1038,7 @@ * @return array Assoc array with the mime headers * @access public */ - function &headers($xtra_headers = null, $overwrite = false, $skip_content = false) + function headers($xtra_headers = null, $overwrite = false, $skip_content = false) { // Add mime version header $headers['MIME-Version'] = '1.0'; @@ -1090,7 +1077,7 @@ /** * Get the text version of the headers - * (useful if you want to use the PHP mail() function) + * (usefull if you want to use the PHP mail() function) * * @param array $xtra_headers Assoc array with any extra headers (optional) * (Don't set Content-Type for multipart messages here!) @@ -1473,4 +1460,36 @@ } } + /** + * PEAR::isError implementation + * + * @param mixed $data Object + * + * @return bool True if object is an instance of PEAR_Error + * @access private + */ + function _isError($data) + { + // PEAR::isError() is not PHP 5.4 compatible (see Bug #19473) + if (is_object($data) && is_a($data, 'PEAR_Error')) { + return true; + } + + return false; + } + + /** + * PEAR::raiseError implementation + * + * @param $message A text error message + * + * @return PEAR_Error Instance of PEAR_Error + * @access private + */ + function _raiseError($message) + { + // PEAR::raiseError() is not PHP 5.4 compatible + return new PEAR_Error($message); + } + } // End of class
View file
kolab-syncroton-2.2.2.tar.gz/lib/ext/Mail/mimeDecode.php -> kolab-syncroton-2.2.3.tar.gz/lib/ext/Mail/mimeDecode.php
Changed
@@ -52,7 +52,7 @@ * @author Sean Coates <sean@php.net> * @copyright 2003-2006 PEAR <pear-group@php.net> * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version CVS: $Id$ + * @version CVS: $Id: mimeDecode.php 305875 2010-12-01 07:17:10Z alan_k $ * @link http://pear.php.net/package/Mail_mime */ @@ -85,7 +85,7 @@ * @author Sean Coates <sean@php.net> * @copyright 2003-2006 PEAR <pear-group@php.net> * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: @package_version@ + * @version Release: 1.5.5 * @link http://pear.php.net/package/Mail_mime */ class Mail_mimeDecode extends PEAR
View file
kolab-syncroton-2.2.2.tar.gz/lib/ext/Mail/mimePart.php -> kolab-syncroton-2.2.3.tar.gz/lib/ext/Mail/mimePart.php
Changed
@@ -48,7 +48,7 @@ * @author Aleksander Machniak <alec@php.net> * @copyright 2003-2006 PEAR <pear-group@php.net> * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version 1.8.5 + * @version CVS: $Id$ * @link http://pear.php.net/package/Mail_mime */ @@ -70,7 +70,7 @@ * @author Aleksander Machniak <alec@php.net> * @copyright 2003-2006 PEAR <pear-group@php.net> * @license http://www.opensource.org/licenses/bsd-license.php BSD License - * @version Release: 1.8.5 + * @version Release: @package_version@ * @link http://pear.php.net/package/Mail_mime */ class Mail_mimePart @@ -315,7 +315,7 @@ for ($i = 0; $i < count($this->_subparts); $i++) { $encoded['body'] .= '--' . $boundary . $eol; $tmp = $this->_subparts[$i]->encode(); - if (PEAR::isError($tmp)) { + if ($this->_isError($tmp)) { return $tmp; } foreach ($tmp['headers'] as $key => $value) { @@ -338,7 +338,7 @@ @ini_set('magic_quotes_runtime', $magic_quote_setting); } - if (PEAR::isError($body)) { + if ($this->_isError($body)) { return $body; } $encoded['body'] = $body; @@ -368,12 +368,12 @@ function encodeToFile($filename, $boundary=null, $skip_head=false) { if (file_exists($filename) && !is_writable($filename)) { - $err = PEAR::raiseError('File is not writeable: ' . $filename); + $err = $this->_raiseError('File is not writeable: ' . $filename); return $err; } if (!($fh = fopen($filename, 'ab'))) { - $err = PEAR::raiseError('Unable to open file: ' . $filename); + $err = $this->_raiseError('Unable to open file: ' . $filename); return $err; } @@ -390,7 +390,7 @@ @ini_set('magic_quotes_runtime', $magic_quote_setting); } - return PEAR::isError($res) ? $res : $this->_headers; + return $this->_isError($res) ? $res : $this->_headers; } /** @@ -425,7 +425,7 @@ for ($i = 0; $i < count($this->_subparts); $i++) { fwrite($fh, $f_eol . '--' . $boundary . $eol); $res = $this->_subparts[$i]->_encodePartToFile($fh); - if (PEAR::isError($res)) { + if ($this->_isError($res)) { return $res; } $f_eol = $eol; @@ -440,7 +440,7 @@ $res = $this->_getEncodedDataFromFile( $this->_body_file, $this->_encoding, $fh ); - if (PEAR::isError($res)) { + if ($this->_isError($res)) { return $res; } } @@ -456,7 +456,7 @@ * @param array $params The parameters for the subpart, same * as the $params argument for constructor. * - * @return Mail_mimePart A reference to the part you just added. It is + * @return Mail_mimePart A reference to the part you just added. In PHP4, it is * crucial if using multipart/* in your subparts that * you use =& in your script when calling this function, * otherwise you will not be able to add further subparts. @@ -464,8 +464,8 @@ */ function &addSubpart($body, $params) { - $this->_subparts[] = new Mail_mimePart($body, $params); - return $this->_subparts[count($this->_subparts) - 1]; + $this->_subparts[] = $part = new Mail_mimePart($body, $params); + return $part; } /** @@ -511,12 +511,12 @@ function _getEncodedDataFromFile($filename, $encoding, $fh=null) { if (!is_readable($filename)) { - $err = PEAR::raiseError('Unable to read file: ' . $filename); + $err = $this->_raiseError('Unable to read file: ' . $filename); return $err; } if (!($fd = fopen($filename, 'rb'))) { - $err = PEAR::raiseError('Could not open file: ' . $filename); + $err = $this->_raiseError('Could not open file: ' . $filename); return $err; } @@ -815,6 +815,7 @@ 'from', 'to', 'cc', 'bcc', 'sender', 'reply-to', 'resent-from', 'resent-to', 'resent-cc', 'resent-bcc', 'resent-sender', 'resent-reply-to', + 'mail-reply-to', 'mail-followup-to', 'return-receipt-to', 'disposition-notification-to', ); $other_headers = array( @@ -1012,7 +1013,7 @@ $value = substr($value, $cutpoint); $cutpoint = $maxLength; // RFC 2047 specifies that any split header should - // be seperated by a CRLF SPACE. + // be separated by a CRLF SPACE. if ($output) { $output .= $eol . ' '; } @@ -1054,7 +1055,7 @@ } // RFC 2047 specifies that any split header should - // be seperated by a CRLF SPACE + // be separated by a CRLF SPACE if ($output) { $output .= $eol . ' '; } @@ -1225,4 +1226,36 @@ return sprintf('%%%02X', ord($matches[1])); } + /** + * PEAR::isError implementation + * + * @param mixed $data Object + * + * @return bool True if object is an instance of PEAR_Error + * @access private + */ + function _isError($data) + { + // PEAR::isError() is not PHP 5.4 compatible (see Bug #19473) + if (is_object($data) && is_a($data, 'PEAR_Error')) { + return true; + } + + return false; + } + + /** + * PEAR::raiseError implementation + * + * @param $message A text error message + * + * @return PEAR_Error Instance of PEAR_Error + * @access private + */ + function _raiseError($message) + { + // PEAR::raiseError() is not PHP 5.4 compatible + return new PEAR_Error($message); + } + } // End of class
View file
kolab-syncroton-2.2.3.tar.gz/lib/ext/Roundcube/rcube_spellcheck_atd.php
Added
@@ -0,0 +1,192 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | This file is part of the Roundcube Webmail client | + | | + | Copyright (C) 2013, 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. | + | | + | PURPOSE: | + | Spellchecking backend implementation for afterthedeadline services | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ +*/ + +/** + * Spellchecking backend implementation to work with an After the Deadline service + * See http://www.afterthedeadline.com/ for more information + * + * @package Framework + * @subpackage Utils + */ +class rcube_spellcheck_atd extends rcube_spellcheck_engine +{ + const SERVICE_HOST = 'service.afterthedeadline.com'; + const SERVICE_PORT = 80; + + private $matches = array(); + private $content; + private $langhosts = array( + 'fr' => 'fr.', + 'de' => 'de.', + 'pt' => 'pt.', + 'es' => 'es.', + ); + + /** + * Set content and check spelling + * + * @see rcube_spellcheck_engine::check() + */ + function check($text) + { + $this->content = $text; + + // spell check uri is configured + $rcube = rcube::get_instance(); + $url = $rcube->config->get('spellcheck_uri'); + $key = $rcube->config->get('spellcheck_atd_key'); + + if ($url) { + $a_uri = parse_url($url); + $ssl = ($a_uri['scheme'] == 'https' || $a_uri['scheme'] == 'ssl'); + $port = $a_uri['port'] ? $a_uri['port'] : ($ssl ? 443 : 80); + $host = ($ssl ? 'ssl://' : '') . $a_uri['host']; + $path = $a_uri['path'] . ($a_uri['query'] ? '?'.$a_uri['query'] : '') . $this->lang; + } + else { + $host = self::SERVICE_HOST; + $port = self::SERVICE_PORT; + $path = '/checkDocument'; + + // prefix host for other languages than 'en' + $lang = substr($this->lang, 0, 2); + if ($this->langhosts[$lang]) + $host = $this->langhosts[$lang] . $host; + } + + $postdata = 'data=' . urlencode($text); + + if (!empty($key)) + $postdata .= '&key=' . urlencode($key); + + $response = $headers = ''; + $in_header = true; + if ($fp = fsockopen($host, $port, $errno, $errstr, 30)) { + $out = "POST $path HTTP/1.0\r\n"; + $out .= "Host: " . str_replace('ssl://', '', $host) . "\r\n"; + $out .= "Content-Length: " . strlen($postdata) . "\r\n"; + $out .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $out .= "Connection: Close\r\n\r\n"; + $out .= $postdata; + fwrite($fp, $out); + + while (!feof($fp)) { + if ($in_header) { + $line = fgets($fp, 512); + $headers .= $line; + if (trim($line) == '') + $in_header = false; + } + else { + $response .= fgets($fp, 1024); + } + } + fclose($fp); + } + + // parse HTTP response headers + if (preg_match('!^HTTP/1.\d (\d+)(.+)!', $headers, $m)) { + $http_status = $m[1]; + if ($http_status != '200') + $this->error = 'HTTP ' . $m[1] . $m[2]; + } + + if (!$response) { + $this->error = "Empty result from spelling engine"; + } + + try { + $result = new SimpleXMLElement($response); + } + catch (Exception $e) { + $thid->error = "Unexpected response from server: " . $store; + return array(); + } + + foreach ($result->error as $error) { + if (strval($error->type) == 'spelling') { + $word = strval($error->string); + + // skip exceptions + if ($this->dictionary->is_exception($word)) { + continue; + } + + $prefix = strval($error->precontext); + $start = $prefix ? mb_strpos($text, $prefix) : 0; + $pos = mb_strpos($text, $word, $start); + $len = mb_strlen($word); + $num = 0; + + $match = array($word, $pos, $len, null, array()); + foreach ($error->suggestions->option as $option) { + $match[4][] = strval($option); + if (++$num == self::MAX_SUGGESTIONS) + break; + } + $matches[] = $match; + } + } + + $this->matches = $matches; + return $matches; + } + + /** + * Returns suggestions for the specified word + * + * @see rcube_spellcheck_engine::get_words() + */ + function get_suggestions($word) + { + $matches = $word ? $this->check($word) : $this->matches; + + if ($matches[0][4]) { + return $matches[0][4]; + } + + return array(); + } + + /** + * Returns misspelled words + * + * @see rcube_spellcheck_engine::get_suggestions() + */ + function get_words($text = null) + { + if ($text) { + $matches = $this->check($text); + } + else { + $matches = $this->matches; + $text = $this->content; + } + + $result = array(); + + foreach ($matches as $m) { + $result[] = mb_substr($text, $m[1], $m[2], RCUBE_CHARSET); + } + + return $result; + } + +} +
View file
kolab-syncroton-2.2.3.tar.gz/lib/ext/Roundcube/rcube_spellcheck_enchant.php
Added
@@ -0,0 +1,164 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | This file is part of the Roundcube Webmail client | + | | + | Copyright (C) 2011-2013, Kolab Systems AG | + | Copyright (C) 20011-2013, 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. | + | | + | PURPOSE: | + | Spellchecking backend implementation to work with Enchant | + +-----------------------------------------------------------------------+ + | Author: Aleksander Machniak <machniak@kolabsys.com> | + +-----------------------------------------------------------------------+ +*/ + +/** + * Spellchecking backend implementation to work with Pspell + * + * @package Framework + * @subpackage Utils + */ +class rcube_spellcheck_enchant extends rcube_spellcheck_engine +{ + private $enchant_broker; + private $enchant_dictionary; + private $matches = array(); + + /** + * Initializes Enchant dictionary + */ + private function 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); + } + + /** + * Set content and check spelling + * + * @see rcube_spellcheck_engine::check() + */ + function check($text) + { + $this->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->dictionary->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); + } + + $this->matches = $matches; + return $matches; + } + + /** + * Returns suggestions for the specified word + * + * @see rcube_spellcheck_engine::get_words() + */ + function get_suggestions($word) + { + $this->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(); + } + + /** + * Returns misspelled words + * + * @see rcube_spellcheck_engine::get_suggestions() + */ + function get_words($text = null) + { + $result = array(); + + if ($text) { + // init spellchecker + $this->init(); + + if (!$this->enchant_dictionary) { + return array(); + } + + // With Enchant we don't need to get suggestions to return misspelled words + $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->dictionary->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; + } + +} +
View file
kolab-syncroton-2.2.3.tar.gz/lib/ext/Roundcube/rcube_spellcheck_engine.php
Added
@@ -0,0 +1,84 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | This file is part of the Roundcube Webmail client | + | | + | Copyright (C) 2011-2013, Kolab Systems AG | + | Copyright (C) 2008-2013, The Roundcube Dev Team | + | | + | Licensed under the GNU General Public License version 3 or | + | any later version with exceptions for skins & plugins. | + | See the README file for a full license statement. | + | | + | PURPOSE: | + | Interface class for a spell-checking backend | + +-----------------------------------------------------------------------+ + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ +*/ + +/** + * Interface class for a spell-checking backend + * + * @package Framework + * @subpackage Utils + */ +abstract class rcube_spellcheck_engine +{ + const MAX_SUGGESTIONS = 10; + + protected $lang; + protected $error; + protected $dictionary; + protected $separator = '/[\s\r\n\t\(\)\/\[\]{}<>\\"]+|[:;?!,\.](?=\W|$)/'; + + /** + * Default constructor + */ + public function __construct($dict, $lang) + { + $this->dictionary = $dict; + $this->lang = $lang; + } + + /** + * Set content and check spelling + * + * @param string $text Text content for spellchecking + * + * @return bool True when no mispelling found, otherwise false + */ + abstract function check($text); + + /** + * Returns suggestions for the specified word + * + * @param string $word The word + * + * @return array Suggestions list + */ + abstract function get_suggestions($word); + + /** + * Returns misspelled words + * + * @param string $text The content for spellchecking. If empty content + * used for check() method will be used. + * + * @return array List of misspelled words + */ + abstract function get_words($text = null); + + /** + * Returns error message + * + * @return string Error message + */ + public function error() + { + return $this->error; + } + +} +
View file
kolab-syncroton-2.2.3.tar.gz/lib/ext/Roundcube/rcube_spellcheck_googie.php
Added
@@ -0,0 +1,158 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | This file is part of the Roundcube Webmail client | + | | + | Copyright (C) 2008-2013, The Roundcube Dev Team | + | | + | Licensed under the GNU General Public License version 3 or | + | any later version with exceptions for skins & plugins. | + | See the README file for a full license statement. | + | | + | PURPOSE: | + | Spellchecking backend implementation to work with Googiespell | + +-----------------------------------------------------------------------+ + | Author: Aleksander Machniak <machniak@kolabsys.com> | + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ +*/ + +/** + * Spellchecking backend implementation to work with a Googiespell service + * + * @package Framework + * @subpackage Utils + */ +class rcube_spellcheck_googie extends rcube_spellcheck_engine +{ + const GOOGLE_HOST = 'ssl://www.google.com'; + const GOOGLE_PORT = 443; + + private $matches = array(); + private $content; + + /** + * Set content and check spelling + * + * @see rcube_spellcheck_engine::check() + */ + function check($text) + { + $this->content = $text; + + // spell check uri is configured + $url = rcube::get_instance()->config->get('spellcheck_uri'); + + if ($url) { + $a_uri = parse_url($url); + $ssl = ($a_uri['scheme'] == 'https' || $a_uri['scheme'] == 'ssl'); + $port = $a_uri['port'] ? $a_uri['port'] : ($ssl ? 443 : 80); + $host = ($ssl ? 'ssl://' : '') . $a_uri['host']; + $path = $a_uri['path'] . ($a_uri['query'] ? '?'.$a_uri['query'] : '') . $this->lang; + } + else { + $host = self::GOOGLE_HOST; + $port = self::GOOGLE_PORT; + $path = '/tbproxy/spell?lang=' . $this->lang; + } + + // Google has some problem with spaces, use \n instead + $gtext = str_replace(' ', "\n", $text); + + $gtext = '<?xml version="1.0" encoding="utf-8" ?>' + .'<spellrequest textalreadyclipped="0" ignoredups="0" ignoredigits="1" ignoreallcaps="1">' + .'<text>' . $gtext . '</text>' + .'</spellrequest>'; + + $store = ''; + if ($fp = fsockopen($host, $port, $errno, $errstr, 30)) { + $out = "POST $path HTTP/1.0\r\n"; + $out .= "Host: " . str_replace('ssl://', '', $host) . "\r\n"; + $out .= "Content-Length: " . strlen($gtext) . "\r\n"; + $out .= "Content-Type: application/x-www-form-urlencoded\r\n"; + $out .= "Connection: Close\r\n\r\n"; + $out .= $gtext; + fwrite($fp, $out); + + while (!feof($fp)) + $store .= fgets($fp, 128); + fclose($fp); + } + + // parse HTTP response + if (preg_match('!^HTTP/1.\d (\d+)(.+)!', $store, $m)) { + $http_status = $m[1]; + if ($http_status != '200') + $this->error = 'HTTP ' . $m[1] . $m[2]; + } + + if (!$store) { + $this->error = "Empty result from spelling engine"; + } + else if (preg_match('/<spellresult error="([^"]+)"/', $store, $m) && $m[1]) { + $this->error = "Error code $m[1] returned"; + } + + preg_match_all('/<c o="([^"]*)" l="([^"]*)" s="([^"]*)">([^<]*)<\/c>/', $store, $matches, PREG_SET_ORDER); + + // skip exceptions (if appropriate options are enabled) + foreach ($matches as $idx => $m) { + $word = mb_substr($text, $m[1], $m[2], RCUBE_CHARSET); + // skip exceptions + if ($this->dictionary->is_exception($word)) { + unset($matches[$idx]); + } + } + + $this->matches = $matches; + return $matches; + } + + /** + * Returns suggestions for the specified word + * + * @see rcube_spellcheck_engine::get_words() + */ + function get_suggestions($word) + { + $matches = $word ? $this->check($word) : $this->matches; + + if ($matches[0][4]) { + $suggestions = explode("\t", $matches[0][4]); + if (sizeof($suggestions) > self::MAX_SUGGESTIONS) { + $suggestions = array_slice($suggestions, 0, self::MAX_SUGGESTIONS); + } + + return $suggestions; + } + + return array(); + } + + /** + * Returns misspelled words + * + * @see rcube_spellcheck_engine::get_suggestions() + */ + function get_words($text = null) + { + if ($text) { + $matches = $this->check($text); + } + else { + $matches = $this->matches; + $text = $this->content; + } + + $result = array(); + + foreach ($matches as $m) { + $result[] = mb_substr($text, $m[1], $m[2], RCUBE_CHARSET); + } + + return $result; + } + +} +
View file
kolab-syncroton-2.2.3.tar.gz/lib/ext/Roundcube/rcube_spellcheck_pspell.php
Added
@@ -0,0 +1,160 @@ +<?php + +/* + +-----------------------------------------------------------------------+ + | This file is part of the Roundcube Webmail client | + | | + | Copyright (C) 2008-2013, The Roundcube Dev Team | + | | + | Licensed under the GNU General Public License version 3 or | + | any later version with exceptions for skins & plugins. | + | See the README file for a full license statement. | + | | + | PURPOSE: | + | Spellchecking backend implementation to work with Pspell | + +-----------------------------------------------------------------------+ + | Author: Aleksander Machniak <machniak@kolabsys.com> | + | Author: Thomas Bruederli <roundcube@gmail.com> | + +-----------------------------------------------------------------------+ +*/ + +/** + * Spellchecking backend implementation to work with Pspell + * + * @package Framework + * @subpackage Utils + */ +class rcube_spellcheck_pspell extends rcube_spellcheck_engine +{ + private $plink; + private $matches = array(); + + /** + * Initializes PSpell dictionary + */ + private function init() + { + if (!$this->plink) { + if (!extension_loaded('pspell')) { + $this->error = "Pspell extension not available"; + return; + } + + $this->plink = pspell_new($this->lang, null, null, RCUBE_CHARSET, PSPELL_FAST); + } + + if (!$this->plink) { + $this->error = "Unable to load Pspell engine for selected language"; + } + } + + /** + * Set content and check spelling + * + * @see rcube_spellcheck_engine::check() + */ + function check($text) + { + $this->init(); + + if (!$this->plink) { + return array(); + } + + // tokenize + $text = preg_split($this->separator, $text, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_OFFSET_CAPTURE); + + $diff = 0; + $matches = array(); + + foreach ($text as $w) { + $word = trim($w[0]); + $pos = $w[1] - $diff; + $len = mb_strlen($word); + + // skip exceptions + if ($this->dictionary->is_exception($word)) { + } + else if (!pspell_check($this->plink, $word)) { + $suggestions = pspell_suggest($this->plink, $word); + + if (sizeof($suggestions) > self::MAX_SUGGESTIONS) { + $suggestions = array_slice($suggestions, 0, self::MAX_SUGGESTIONS); + } + + $matches[] = array($word, $pos, $len, null, $suggestions); + } + + $diff += (strlen($word) - $len); + } + + $this->matches = $matches; + return $matches; + } + + /** + * Returns suggestions for the specified word + * + * @see rcube_spellcheck_engine::get_words() + */ + function get_suggestions($word) + { + $this->init(); + + if (!$this->plink) { + return array(); + } + + $suggestions = pspell_suggest($this->plink, $word); + + if (sizeof($suggestions) > self::MAX_SUGGESTIONS) + $suggestions = array_slice($suggestions, 0, self::MAX_SUGGESTIONS); + + return is_array($suggestions) ? $suggestions : array(); + } + + /** + * Returns misspelled words + * + * @see rcube_spellcheck_engine::get_suggestions() + */ + function get_words($text = null) + { + $result = array(); + + if ($text) { + // init spellchecker + $this->init(); + + if (!$this->plink) { + return array(); + } + + // With PSpell we don't need to get suggestions to return misspelled words + $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->dictionary->is_exception($word)) { + continue; + } + + if (!pspell_check($this->plink, $word)) { + $result[] = $word; + } + } + + return $result; + } + + foreach ($this->matches as $m) { + $result[] = $m[0]; + } + + return $result; + } + +} +
View file
kolab-syncroton-2.2.2.tar.gz/lib/kolab_sync.php -> kolab-syncroton-2.2.3.tar.gz/lib/kolab_sync.php
Changed
@@ -43,7 +43,7 @@ public $user; const CHARSET = 'UTF-8'; - const VERSION = "2.2.2"; + const VERSION = "2.2.3"; /**
View file
kolab-syncroton-2.2.2.tar.gz/lib/kolab_sync_backend.php -> kolab-syncroton-2.2.3.tar.gz/lib/kolab_sync_backend.php
Changed
@@ -421,7 +421,8 @@ foreach ($foldertypes as $folder => $type) { // only personal folders if ($this->storage->folder_namespace($folder) == 'personal') { - $this->folder_set($folder, $id, 1); + $flag = preg_match('/^(event|task)/', $type) ? 2 : 1; + $this->folder_set($folder, $id, $flag); } } }
View file
kolab-syncroton-2.2.2.tar.gz/lib/kolab_sync_backend_common.php -> kolab-syncroton-2.2.3.tar.gz/lib/kolab_sync_backend_common.php
Changed
@@ -120,8 +120,10 @@ { $id = $id instanceof $this->interface_name ? $id->id : $id; - $select = $this->db->query('SELECT * FROM ' . $this->table_name . ' WHERE id = ?', array($id)); - $data = $this->db->fetch_assoc($select); + if ($id) { + $select = $this->db->query('SELECT * FROM ' . $this->table_name . ' WHERE id = ?', array($id)); + $data = $this->db->fetch_assoc($select); + } if (empty($data)) { throw new Syncroton_Exception_NotFound('Object not found'); @@ -141,6 +143,10 @@ { $id = $id instanceof $this->interface_name ? $id->id : $id; + if (!$id) { + return false; + } + $result = $this->db->query('DELETE FROM ' . $this->table_name .' WHERE id = ?', array($id)); return (bool) $this->db->affected_rows($result);
View file
kolab-syncroton-2.2.2.tar.gz/lib/kolab_sync_data_calendar.php -> kolab-syncroton-2.2.3.tar.gz/lib/kolab_sync_data_calendar.php
Changed
@@ -286,10 +286,10 @@ foreach ($event['attendees'] as $idx => $attendee) { $att = array(); - if ($attendee['name']) { + if ($name = $attendee['name']) { $att['name'] = $name; } - if ($attendee['email']) { + if ($email = $attendee['email']) { $att['email'] = $email; } @@ -501,7 +501,7 @@ */ protected function filter($filter_type = 0) { - $filter = array(); + $filter = array(array('type', '=', $this->modelName)); switch ($filter_type) { case Syncroton_Command_Sync::FILTER_2_WEEKS_BACK:
View file
kolab-syncroton-2.2.2.tar.gz/lib/kolab_sync_data_contacts.php -> kolab-syncroton-2.2.3.tar.gz/lib/kolab_sync_data_contacts.php
Changed
@@ -254,4 +254,18 @@ return $contact; } + /** + * Returns filter query array according to specified ActiveSync FilterType + * + * @param int $filter_type Filter type + * + * @param array Filter query + */ + protected function filter($filter_type = 0) + { + // specify object type, contact folders in Kolab might + // contain also ditribution-list objects, we'll skip them + return array(array('type', '=', $this->modelName)); + } + }
View file
kolab-syncroton-2.2.2.tar.gz/lib/kolab_sync_data_email.php -> kolab-syncroton-2.2.3.tar.gz/lib/kolab_sync_data_email.php
Changed
@@ -802,22 +802,39 @@ // We're in "get changes" mode if (isset($modseq_data)) { $folder_data = $this->storage->folder_data($foldername); + $got_changes = true; + if ($folder_data['HIGHESTMODSEQ']) { $modseq_data[$foldername] = $folder_data['HIGHESTMODSEQ']; if ($modseq_data[$foldername] != $modseq[$foldername]) { $modseq_update = true; } + else { + $got_changes = false; + } } // If previous HIGHESTMODSEQ doesn't exist we can't get changes // We can only get folder's HIGHESTMODSEQ value and store it for the next try - if (empty($modseq) || empty($modseq[$foldername])) { + // Skip search if HIGHESTMODSEQ didn't change + if (!$got_changes || empty($modseq) || empty($modseq[$foldername])) { continue; } $filter_str .= " MODSEQ " . ($modseq[$foldername] + 1); } + // We could use messages cache by replacing search() with index() + // in some cases. This however is possible only if user has skip_deleted=true, + // in his Roundcube preferences, otherwise we'd make often cache re-initialization, + // because Roundcube message cache can work only with one skip_deleted + // setting at a time. We'd also need to make sure folder_sync() was called + // before (see above). + // + // if ($filter_str == 'ALL UNDELETED') + // $search = $this->storage->index($foldername, null, null, true, true); + // else + $search = $this->storage->search_once($foldername, $filter_str); if (!($search instanceof rcube_result_index)) { @@ -887,8 +904,8 @@ continue; } -// $this->storage->set_folder($foldername); - $this->storage->folder_sync($foldername); +// $this->storage->set_folder($foldername); +// $this->storage->folder_sync($foldername); $search = $this->storage->search_once($foldername, $search_str);
View file
kolab-syncroton-2.2.2.tar.gz/lib/kolab_sync_data_tasks.php -> kolab-syncroton-2.2.3.tar.gz/lib/kolab_sync_data_tasks.php
Changed
@@ -220,7 +220,7 @@ */ protected function filter($filter_type = 0) { - $filter = array(); + $filter = array(array('type', '=', $this->modelName)); if ($filter_type == Syncroton_Command_Sync::FILTER_INCOMPLETE) { $filter[] = array('tags', '!~', 'x-complete');
View file
kolab-syncroton-2.2.2.tar.gz/lib/plugins/kolab_auth/config.inc.php.dist -> kolab-syncroton-2.2.3.tar.gz/lib/plugins/kolab_auth/config.inc.php.dist
Changed
@@ -72,5 +72,9 @@ ), ); +// List of LDAP addressbooks (keys of ldap_public configuration array) +// for which base_dn variables (%dc, etc.) will be replaced according to authenticated user DN +// Note: special name '*' for all LDAP addressbooks +$rcmail_config['kolab_auth_ldap_addressbooks'] = array('*'); ?>
View file
kolab-syncroton-2.2.2.tar.gz/lib/plugins/kolab_auth/kolab_auth.php -> kolab-syncroton-2.2.3.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-2012, Kolab Systems AG <contact@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 @@ -41,11 +41,18 @@ $this->add_hook('startup', array($this, 'startup')); $this->add_hook('user_create', array($this, 'user_create')); + // Hook for password change + $this->add_hook('password_ldap_bind', array($this, 'password_ldap_bind')); + // Hooks related to "Login As" feature $this->add_hook('template_object_loginform', array($this, 'login_form')); $this->add_hook('storage_connect', array($this, 'imap_connect')); $this->add_hook('managesieve_connect', array($this, 'imap_connect')); $this->add_hook('smtp_connect', array($this, 'smtp_connect')); + $this->add_hook('identity_form', array($this, 'identity_form')); + + // Hook to modify some configuration, e.g. ldap + $this->add_hook('config_get', array($this, 'config_get')); $this->add_hook('write_log', array($this, 'write_log')); @@ -62,25 +69,57 @@ $rcmail->config->set('ldap_debug', true); $rcmail->config->set('smtp_debug', true); } - } 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']); + $this->load_user_role_plugins_and_settings(); + + return $args; + } + + /** + * Modify some configuration according to LDAP user record + */ + public function config_get($args) + { + // Replaces ldap_vars (%dc, etc) in public kolab ldap addressbooks + // config based on the users base_dn. (for multi domain support) + if ($args['name'] == 'ldap_public' && !empty($args['result'])) { + $this->load_config(); + + $rcmail = rcube::get_instance(); + $kolab_books = (array) $rcmail->config->get('kolab_auth_ldap_addressbooks'); + + foreach ($args['result'] as $name => $config) { + if (in_array($name, $kolab_books) || in_array('*', $kolab_books)) { + $args['result'][$name]['base_dn'] = self::parse_ldap_vars($config['base_dn']); + $args['result'][$name]['search_base_dn'] = self::parse_ldap_vars($config['search_base_dn']); + $args['result'][$name]['bind_dn'] = str_replace('%dn', $_SESSION['kolab_dn'], $config['bind_dn']); + + if (!empty($config['groups'])) { + $args['result'][$name]['groups']['base_dn'] = self::parse_ldap_vars($config['groups']['base_dn']); + } + } + } } return $args; } - public function load_user_role_plugins_and_settings($role_dns) + /** + * Modifies list of plugins and settings according to + * specified LDAP roles + */ + public function load_user_role_plugins_and_settings() { + if (empty($_SESSION['user_roledns'])) { + return; + } + $rcmail = rcube::get_instance(); $this->load_config(); - // Check role dependent plugins to enable and settings to modify // Example 'kolab_auth_role_plugins' = // @@ -108,25 +147,19 @@ $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; + $role_plugins[self::parse_ldap_vars($role_dn)] = $plugins; } } if (!empty($role_settings)) { foreach ($role_settings as $role_dn => $settings) { - $role_settings[$ldap->parse_vars($role_dn)] = $settings; + $role_settings[self::parse_ldap_vars($role_dn)] = $settings; } } - foreach ($role_dns as $role_dn) { + foreach ($_SESSION['user_roledns'] as $role_dn) { if (isset($role_plugins[$role_dn]) && is_array($role_plugins[$role_dn])) { foreach ($role_plugins[$role_dn] as $plugin) { $this->require_plugin($plugin); @@ -306,6 +339,16 @@ $ldap = self::ldap(); if (!$ldap || !$ldap->ready) { $args['abort'] = true; + $message = sprintf( + 'Login failure for user %s from %s in session %s (error %s)', + $user, + rcube_utils::remote_ip(), + session_id(), + "LDAP not ready" + ); + + rcube::write_log('userlogins', $message); + return $args; } @@ -314,6 +357,16 @@ if (empty($record)) { $args['abort'] = true; + $message = sprintf( + 'Login failure for user %s from %s in session %s (error %s)', + $user, + rcube_utils::remote_ip(), + session_id(), + "No user record found" + ); + + rcube::write_log('userlogins', $message); + return $args; } @@ -325,11 +378,21 @@ $email_attr = $rcmail->config->get('kolab_auth_email'); $org_attr = $rcmail->config->get('kolab_auth_organization'); $role_attr = $rcmail->config->get('kolab_auth_role'); + $imap_attr = $rcmail->config->get('kolab_auth_mailhost'); if (!empty($role_attr) && !empty($record[$role_attr])) { $_SESSION['user_roledns'] = (array)($record[$role_attr]); } + if (!empty($imap_attr) && !empty($record[$role_attr])) { + $default_host = $rcmail->config->get('default_host'); + if (!empty($default_host)) { + rcube::write_log("errors", "Both default host and kolab_auth_mailhost set. Incompatible."); + } else { + $args['host'] = "tls://" . $record[$role_attr]; + } + } + // Login As... if (!empty($loginas) && $admin_login) { // Authenticate to LDAP @@ -337,6 +400,16 @@ if (!$result) { $args['abort'] = true; + $message = sprintf( + 'Login failure for user %s from %s in session %s (error %s)', + $user, + rcube_utils::remote_ip(), + session_id(), + "Unable to bind with '" . $record['dn'] . "'" + ); + + rcube::write_log('userlogins', $message); + return $args; } @@ -378,6 +451,17 @@ if (empty($record)) { $args['abort'] = true; + $message = sprintf( + 'Login failure for user %s (as user %s) from %s in session %s (error %s)', + $user, + $loginas, + rcube_utils::remote_ip(), + session_id(), + "No user record found for '" . $loginas . "'" + ); + + rcube::write_log('userlogins', $message); + return $args; } @@ -393,6 +477,12 @@ $_SESSION['kolab_uid'] = is_array($record['uid']) ? $record['uid'][0] : $record['uid']; $_SESSION['kolab_dn'] = $record['dn']; + // Store LDAP replacement variables used for current user + // This improves performance of load_user_role_plugins_and_settings() + // which is executed on every request (via startup hook) and where + // we don't like to use LDAP (connection + bind + search) + $_SESSION['kolab_auth_vars'] = $ldap->get_parse_vars(); + // Set user login if ($login_attr) { $this->data['user_login'] = is_array($record[$login_attr]) ? $record[$login_attr][0] : $record[$login_attr]; @@ -435,6 +525,20 @@ } /** + * Set user DN for password change (password plugin with ldap_simple driver) + */ + public function password_ldap_bind($args) + { + $args['user_dn'] = $_SESSION['kolab_dn']; + + $rcmail = rcube::get_instance(); + + $rcmail->config->set('password_ldap_method', 'user'); + + return $args; + } + + /** * Sets SASL Proxy login/password for IMAP and Managesieve auth */ public function imap_connect($args) @@ -469,6 +573,48 @@ } /** + * Hook to replace the plain text input field for email address by a drop-down list + * with all email addresses (including aliases) from this user's LDAP record. + */ + public function identity_form($args) + { + $rcmail = rcube::get_instance(); + $ident_level = intval($rcmail->config->get('identities_level', 0)); + + // do nothing if email address modification is disabled + if ($ident_level == 1 || $ident_level == 3) { + return $args; + } + + $ldap = self::ldap(); + if (!$ldap || !$ldap->ready || empty($_SESSION['kolab_dn'])) { + return $args; + } + + $emails = array(); + $user_record = $ldap->get_record($_SESSION['kolab_dn']); + + foreach ((array)$rcmail->config->get('kolab_auth_email', array()) as $col) { + $values = rcube_addressbook::get_col_values($col, $user_record, true); + if (!empty($values)) + $emails = array_merge($emails, array_filter($values)); + } + + // kolab_delegation might want to modify this addresses list + $plugin = $rcmail->plugins->exec_hook('kolab_auth_emails', array('emails' => $emails)); + $emails = $plugin['emails']; + + if (!empty($emails)) { + $args['form']['addressing']['content']['email'] = array( + 'type' => 'select', + 'options' => array_combine($emails, $emails), + ); + } + + return $args; + } + + /** * Initializes LDAP object and connects to LDAP server */ public static function ldap() @@ -506,4 +652,21 @@ return self::$ldap; } + + /** + * Parses LDAP DN string with replacing supported variables. + * See kolab_auth_ldap::parse_vars() + * + * @param string $str LDAP DN string + * + * @return string Parsed DN string + */ + public static function parse_ldap_vars($str) + { + if (!empty($_SESSION['kolab_auth_vars'])) { + $str = strtr($str, $_SESSION['kolab_auth_vars']); + } + + return $str; + } }
View file
kolab-syncroton-2.2.2.tar.gz/lib/plugins/kolab_auth/kolab_auth_ldap.php -> kolab-syncroton-2.2.3.tar.gz/lib/plugins/kolab_auth/kolab_auth_ldap.php
Changed
@@ -287,7 +287,8 @@ if ($limit && $limit <= $i) { break; } - $dn = $result->get_dn(); + $dn = $result->get_dn(); + $entry = rcube_ldap_generic::normalize_entry($entry); $list[$dn] = $this->field_mapping($dn, $entry); $i++; } @@ -369,7 +370,8 @@ if (!$user) { $user = $_SESSION['username']; } - else if (isset($this->icache[$user])) { + + if (isset($this->icache[$user])) { list($user, $dc) = $this->icache[$user]; } else { @@ -412,6 +414,8 @@ $replaces = array('%dc' => $dc, '%d' => $d, '%fu' => $user, '%u' => $u); + $this->parse_replaces = $replaces; + return strtr($str, $replaces); } @@ -459,6 +463,16 @@ } /** + * Returns variables used for replacement in (last) parse_vars() call + * + * @return array Variable-value hash array + */ + public function get_parse_vars() + { + return $this->parse_replaces; + } + + /** * HTML-safe DN string encoding * * @param string $str DN string
View file
kolab-syncroton-2.2.2.tar.gz/lib/plugins/kolab_auth/package.xml -> kolab-syncroton-2.2.3.tar.gz/lib/plugins/kolab_auth/package.xml
Changed
@@ -18,10 +18,10 @@ <email>machniak@kolabsys.com</email> <active>yes</active> </lead> - <date>2013-06-25</date> + <date>2013-10-04</date> <version> - <release>0.7</release> - <api>0.2</api> + <release>1.0</release> + <api>1.0</api> </version> <stability> <release>stable</release>
View file
kolab-syncroton-2.2.2.tar.gz/lib/plugins/kolab_folders/kolab_folders.php -> kolab-syncroton-2.2.3.tar.gz/lib/plugins/kolab_folders/kolab_folders.php
Changed
@@ -534,7 +534,7 @@ // create folder if (!$exists && !$storage->folder_exists($foldername)) { - $storage->create_folder($foldername, $type1 == 'mail'); + $storage->create_folder($foldername); $storage->subscribe($foldername); }
View file
kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/SQL/mysql.initial.sql -> kolab-syncroton-2.2.3.tar.gz/lib/plugins/libkolab/SQL/mysql.initial.sql
Changed
@@ -30,7 +30,7 @@ `created` DATETIME DEFAULT NULL, `changed` DATETIME DEFAULT NULL, `data` TEXT NOT NULL, - `xml` TEXT NOT NULL, + `xml` LONGTEXT NOT NULL, `tags` VARCHAR(255) NOT NULL, `words` TEXT NOT NULL, `type` VARCHAR(32) CHARACTER SET ascii NOT NULL, @@ -172,4 +172,4 @@ ) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; -INSERT INTO `system` (`name`, `value`) VALUES ('libkolab-version', '2013100400'); +INSERT INTO `system` (`name`, `value`) VALUES ('libkolab-version', '2013110400');
View file
kolab-syncroton-2.2.3.tar.gz/lib/plugins/libkolab/SQL/mysql/2013100400.sql
Added
@@ -0,0 +1,174 @@ +CREATE TABLE `kolab_folders` ( + `folder_id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, + `resource` VARCHAR(255) NOT NULL, + `type` VARCHAR(32) NOT NULL, + `synclock` INT(10) NOT NULL DEFAULT '0', + `ctag` VARCHAR(40) DEFAULT NULL, + PRIMARY KEY(`folder_id`), + INDEX `resource_type` (`resource`, `type`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +CREATE TABLE `kolab_cache_contact` ( + `folder_id` BIGINT UNSIGNED NOT NULL, + `msguid` BIGINT UNSIGNED NOT NULL, + `uid` VARCHAR(128) CHARACTER SET ascii NOT NULL, + `created` DATETIME DEFAULT NULL, + `changed` DATETIME DEFAULT NULL, + `data` TEXT NOT NULL, + `xml` TEXT NOT NULL, + `tags` VARCHAR(255) NOT NULL, + `words` TEXT NOT NULL, + `type` VARCHAR(32) CHARACTER SET ascii NOT NULL, + CONSTRAINT `fk_kolab_cache_contact_folder` FOREIGN KEY (`folder_id`) + REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE, + PRIMARY KEY(`folder_id`,`msguid`), + INDEX `contact_type` (`folder_id`,`type`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +CREATE TABLE `kolab_cache_event` ( + `folder_id` BIGINT UNSIGNED NOT NULL, + `msguid` BIGINT UNSIGNED NOT NULL, + `uid` VARCHAR(128) CHARACTER SET ascii NOT NULL, + `created` DATETIME DEFAULT NULL, + `changed` DATETIME DEFAULT NULL, + `data` TEXT NOT NULL, + `xml` TEXT NOT NULL, + `tags` VARCHAR(255) NOT NULL, + `words` TEXT NOT NULL, + `dtstart` DATETIME, + `dtend` DATETIME, + CONSTRAINT `fk_kolab_cache_event_folder` FOREIGN KEY (`folder_id`) + REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE, + PRIMARY KEY(`folder_id`,`msguid`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +CREATE TABLE `kolab_cache_task` ( + `folder_id` BIGINT UNSIGNED NOT NULL, + `msguid` BIGINT UNSIGNED NOT NULL, + `uid` VARCHAR(128) CHARACTER SET ascii NOT NULL, + `created` DATETIME DEFAULT NULL, + `changed` DATETIME DEFAULT NULL, + `data` TEXT NOT NULL, + `xml` TEXT NOT NULL, + `tags` VARCHAR(255) NOT NULL, + `words` TEXT NOT NULL, + `dtstart` DATETIME, + `dtend` DATETIME, + CONSTRAINT `fk_kolab_cache_task_folder` FOREIGN KEY (`folder_id`) + REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE, + PRIMARY KEY(`folder_id`,`msguid`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +CREATE TABLE `kolab_cache_journal` ( + `folder_id` BIGINT UNSIGNED NOT NULL, + `msguid` BIGINT UNSIGNED NOT NULL, + `uid` VARCHAR(128) CHARACTER SET ascii NOT NULL, + `created` DATETIME DEFAULT NULL, + `changed` DATETIME DEFAULT NULL, + `data` TEXT NOT NULL, + `xml` TEXT NOT NULL, + `tags` VARCHAR(255) NOT NULL, + `words` TEXT NOT NULL, + `dtstart` DATETIME, + `dtend` DATETIME, + CONSTRAINT `fk_kolab_cache_journal_folder` FOREIGN KEY (`folder_id`) + REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE, + PRIMARY KEY(`folder_id`,`msguid`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +CREATE TABLE `kolab_cache_note` ( + `folder_id` BIGINT UNSIGNED NOT NULL, + `msguid` BIGINT UNSIGNED NOT NULL, + `uid` VARCHAR(128) CHARACTER SET ascii NOT NULL, + `created` DATETIME DEFAULT NULL, + `changed` DATETIME DEFAULT NULL, + `data` TEXT NOT NULL, + `xml` TEXT NOT NULL, + `tags` VARCHAR(255) NOT NULL, + `words` TEXT NOT NULL, + CONSTRAINT `fk_kolab_cache_note_folder` FOREIGN KEY (`folder_id`) + REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE, + PRIMARY KEY(`folder_id`,`msguid`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +CREATE TABLE `kolab_cache_file` ( + `folder_id` BIGINT UNSIGNED NOT NULL, + `msguid` BIGINT UNSIGNED NOT NULL, + `uid` VARCHAR(128) CHARACTER SET ascii NOT NULL, + `created` DATETIME DEFAULT NULL, + `changed` DATETIME DEFAULT NULL, + `data` TEXT NOT NULL, + `xml` TEXT NOT NULL, + `tags` VARCHAR(255) NOT NULL, + `words` TEXT NOT NULL, + `filename` varchar(255) DEFAULT NULL, + CONSTRAINT `fk_kolab_cache_file_folder` FOREIGN KEY (`folder_id`) + REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE, + PRIMARY KEY(`folder_id`,`msguid`), + INDEX `folder_filename` (`folder_id`, `filename`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +CREATE TABLE `kolab_cache_configuration` ( + `folder_id` BIGINT UNSIGNED NOT NULL, + `msguid` BIGINT UNSIGNED NOT NULL, + `uid` VARCHAR(128) CHARACTER SET ascii NOT NULL, + `created` DATETIME DEFAULT NULL, + `changed` DATETIME DEFAULT NULL, + `data` TEXT NOT NULL, + `xml` TEXT NOT NULL, + `tags` VARCHAR(255) NOT NULL, + `words` TEXT NOT NULL, + `type` VARCHAR(32) CHARACTER SET ascii NOT NULL, + CONSTRAINT `fk_kolab_cache_configuration_folder` FOREIGN KEY (`folder_id`) + REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE, + PRIMARY KEY(`folder_id`,`msguid`), + INDEX `configuration_type` (`folder_id`,`type`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + +CREATE TABLE `kolab_cache_freebusy` ( + `folder_id` BIGINT UNSIGNED NOT NULL, + `msguid` BIGINT UNSIGNED NOT NULL, + `uid` VARCHAR(128) CHARACTER SET ascii NOT NULL, + `created` DATETIME DEFAULT NULL, + `changed` DATETIME DEFAULT NULL, + `data` TEXT NOT NULL, + `xml` TEXT NOT NULL, + `tags` VARCHAR(255) NOT NULL, + `words` TEXT NOT NULL, + `dtstart` DATETIME, + `dtend` DATETIME, + CONSTRAINT `fk_kolab_cache_freebusy_folder` FOREIGN KEY (`folder_id`) + REFERENCES `kolab_folders`(`folder_id`) ON DELETE CASCADE ON UPDATE CASCADE, + PRIMARY KEY(`folder_id`,`msguid`) +) /*!40000 ENGINE=INNODB */ /*!40101 CHARACTER SET utf8 COLLATE utf8_general_ci */; + + +-- Migrate data from old kolab_cache table + +INSERT INTO kolab_folders (resource, type) + SELECT DISTINCT resource, type + FROM kolab_cache WHERE type IN ('event','contact','task','file'); + +INSERT INTO kolab_cache_event (folder_id, msguid, uid, created, changed, data, xml, tags, words, dtstart, dtend) + SELECT kolab_folders.folder_id, msguid, uid, created, changed, data, xml, tags, words, dtstart, dtend + FROM kolab_cache LEFT JOIN kolab_folders ON (kolab_folders.resource = kolab_cache.resource) + WHERE kolab_cache.type = 'event' AND kolab_folders.folder_id IS NOT NULL; + +INSERT INTO kolab_cache_task (folder_id, msguid, uid, created, changed, data, xml, tags, words, dtstart, dtend) + SELECT kolab_folders.folder_id, msguid, uid, created, changed, data, xml, tags, words, dtstart, dtend + FROM kolab_cache LEFT JOIN kolab_folders ON (kolab_folders.resource = kolab_cache.resource) + WHERE kolab_cache.type = 'task' AND kolab_folders.folder_id IS NOT NULL; + +INSERT INTO kolab_cache_contact (folder_id, msguid, uid, created, changed, data, xml, tags, words, type) + SELECT kolab_folders.folder_id, msguid, uid, created, changed, data, xml, tags, words, kolab_cache.type + FROM kolab_cache LEFT JOIN kolab_folders ON (kolab_folders.resource = kolab_cache.resource) + WHERE kolab_cache.type IN ('contact','distribution-list') AND kolab_folders.folder_id IS NOT NULL; + +INSERT INTO kolab_cache_file (folder_id, msguid, uid, created, changed, data, xml, tags, words, filename) + SELECT kolab_folders.folder_id, msguid, uid, created, changed, data, xml, tags, words, filename + FROM kolab_cache LEFT JOIN kolab_folders ON (kolab_folders.resource = kolab_cache.resource) + WHERE kolab_cache.type = 'file' AND kolab_folders.folder_id IS NOT NULL; + + +DROP TABLE IF EXISTS `kolab_cache`; +
View file
kolab-syncroton-2.2.3.tar.gz/lib/plugins/libkolab/SQL/mysql/2013110400.sql
Added
@@ -0,0 +1,1 @@ +ALTER TABLE `kolab_cache_contact` CHANGE `xml` `xml` LONGTEXT NOT NULL;
View file
kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/lib/kolab_date_recurrence.php -> kolab-syncroton-2.2.3.tar.gz/lib/plugins/libkolab/lib/kolab_date_recurrence.php
Changed
@@ -101,16 +101,35 @@ /** * Get the end date of the occurence of this recurrence cycle * - * @param string Date limit (where infinite recurrences should abort) * @return mixed Timestamp with end date of the last event or False if recurrence exceeds limit */ - public function end($limit = 'now +1 year') + public function end() { - $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) { + $event = $this->object->to_array(); + + // recurrence end date is given + if ($event['recurrence']['UNTIL'] instanceof DateTime) { + return $event['recurrence']['UNTIL']->format('U'); + } + + // let libkolab do the work + if ($this->engine && ($cend = $this->engine->getLastOccurrence()) && ($end_dt = kolab_format::php_datetime(new cDateTime($cend)))) { return $end_dt->format('U'); } + // determine a reasonable end date if none given + if (!$event['recurrence']['COUNT']) { + switch ($event['recurrence']['FREQ']) { + case 'YEARLY': $intvl = 'P100Y'; break; + case 'MONTHLY': $intvl = 'P20Y'; break; + default: $intvl = 'P10Y'; break; + } + + $end_dt = clone $event['start']; + $end_dt->add(new DateInterval($intvl)); + return $end_dt->format('U'); + } + return false; } }
View file
kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/lib/kolab_format.php -> kolab-syncroton-2.2.3.tar.gz/lib/plugins/libkolab/lib/kolab_format.php
Changed
@@ -123,7 +123,7 @@ if (!$dateonly) $result->setTime($datetime->format('G'), $datetime->format('i'), $datetime->format('s')); - if ($tz && $tz->getName() == 'UTC') + if ($tz && in_array($tz->getName(), array('UTC','GMT','+00:00'))) $result->setUTC(true); else if ($tz !== false) $result->setTimezone($tz->getName()); @@ -401,13 +401,13 @@ // set some automatic values if missing if (empty($object['created']) && method_exists($this->obj, 'setCreated')) { $cdt = $this->obj->created(); - $object['created'] = $cdt && $cdt->isValid() ? self::php_datetime($cdt) : new DateTime('now', self::$timezone); + $object['created'] = $cdt && $cdt->isValid() ? self::php_datetime($cdt) : new DateTime('now', new DateTimeZone('UTC')); if (!$cdt || !$cdt->isValid()) $this->obj->setCreated(self::get_datetime($object['created'])); } - $object['changed'] = new DateTime('now', self::$timezone); - $this->obj->setLastModified(self::get_datetime($object['changed'], new DateTimeZone('UTC'))); + $object['changed'] = new DateTime('now', new DateTimeZone('UTC')); + $this->obj->setLastModified(self::get_datetime($object['changed'])); // Save custom properties of the given object if (isset($object['x-custom'])) {
View file
kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/lib/kolab_format_event.php -> kolab-syncroton-2.2.3.tar.gz/lib/plugins/libkolab/lib/kolab_format_event.php
Changed
@@ -163,20 +163,22 @@ else if ($status == kolabformat::StatusCancelled) $object['cancelled'] = true; + // this is an exception object + if ($this->obj->recurrenceID()->isValid()) { + $object['thisandfuture'] = $this->obj->thisAndFuture(); + } // read exception event objects - if (($exceptions = $this->obj->exceptions()) && is_object($exceptions) && $exceptions->size()) { + else if (($exceptions = $this->obj->exceptions()) && is_object($exceptions) && $exceptions->size()) { + $recurrence_exceptions = array(); 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); + $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(); + $object['recurrence']['EXCEPTIONS'] = $recurrence_exceptions; } return $this->data = $object;
View file
kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/lib/kolab_format_xcal.php -> kolab-syncroton-2.2.3.tar.gz/lib/plugins/libkolab/lib/kolab_format_xcal.php
Changed
@@ -373,7 +373,7 @@ if ($object['alarms']) { list($offset, $type) = explode(":", $object['alarms']); - if ($type == 'EMAIL') { // email alarms implicitly go to event owner + if ($type == 'EMAIL' && !empty($object['_owner'])) { // email alarms implicitly go to event owner $recipients = new vectorcontactref; $recipients->push(new ContactReference(ContactReference::EmailReference, $object['_owner'])); $alarm = new Alarm($object['title'], strval($object['description']), $recipients);
View file
kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/lib/kolab_storage_cache.php -> kolab-syncroton-2.2.3.tar.gz/lib/plugins/libkolab/lib/kolab_storage_cache.php
Changed
@@ -318,8 +318,8 @@ } // keep a copy in memory for fast access - $this->objects[$msguid] = $object; - $this->uid2msg[$object['uid']] = $msguid; + $this->objects = array($msguid => $object); + $this->uid2msg = array($object['uid'] => $msguid); } @@ -364,7 +364,7 @@ $this->_read_folder_data(); $result = $this->db->query( - "DELETE FROM $this->cache_table WHERE folder_id=?". + "DELETE FROM $this->cache_table WHERE folder_id=?", $this->folder_id ); return $this->db->affected_rows($result);
View file
kolab-syncroton-2.2.3.tar.gz/lib/plugins/libkolab/lib/kolab_storage_cache_configuration.php
Added
@@ -0,0 +1,40 @@ +<?php + +/** + * Kolab storage cache class for configuration objects + * + * @author Thomas Bruederli <bruederli@kolabsys.com> + * + * Copyright (C) 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/>. + */ + +class kolab_storage_cache_configuration extends kolab_storage_cache +{ + protected $extra_cols = array('type'); + + /** + * Helper method to convert the given Kolab object into a dataset to be written to cache + * + * @override + */ + protected function _serialize($object) + { + $sql_data = parent::_serialize($object); + $sql_data['type'] = $object['type']; + + return $sql_data; + } +} \ No newline at end of file
View file
kolab-syncroton-2.2.3.tar.gz/lib/plugins/libkolab/lib/kolab_storage_cache_contact.php
Added
@@ -0,0 +1,45 @@ +<?php + +/** + * Kolab storage cache class for contact objects + * + * @author Thomas Bruederli <bruederli@kolabsys.com> + * + * Copyright (C) 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/>. + */ + +class kolab_storage_cache_contact extends kolab_storage_cache +{ + protected $extra_cols = array('type'); + protected $binary_items = array( + 'photo' => '|<photo><uri>[^;]+;base64,([^<]+)</uri></photo>|i', + 'pgppublickey' => '|<key><uri>date:application/pgp-keys;base64,([^<]+)</uri></key>|i', + 'pkcs7publickey' => '|<key><uri>date:application/pkcs7-mime;base64,([^<]+)</uri></key>|i', + ); + + /** + * Helper method to convert the given Kolab object into a dataset to be written to cache + * + * @override + */ + protected function _serialize($object) + { + $sql_data = parent::_serialize($object); + $sql_data['type'] = $object['_type']; + + return $sql_data; + } +} \ No newline at end of file
View file
kolab-syncroton-2.2.3.tar.gz/lib/plugins/libkolab/lib/kolab_storage_cache_event.php
Added
@@ -0,0 +1,49 @@ +<?php + +/** + * Kolab storage cache class for calendar event objects + * + * @author Thomas Bruederli <bruederli@kolabsys.com> + * + * Copyright (C) 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/>. + */ + +class kolab_storage_cache_event extends kolab_storage_cache +{ + protected $extra_cols = array('dtstart','dtend'); + + /** + * Helper method to convert the given Kolab object into a dataset to be written to cache + * + * @override + */ + protected function _serialize($object) + { + $sql_data = parent::_serialize($object); + + // database runs in server's timezone so using date() is what we want + $sql_data['dtstart'] = date('Y-m-d H:i:s', is_object($object['start']) ? $object['start']->format('U') : $object['start']); + $sql_data['dtend'] = date('Y-m-d H:i:s', is_object($object['end']) ? $object['end']->format('U') : $object['end']); + + // extend date range for recurring events + if ($object['recurrence'] && $object['_formatobj']) { + $recurrence = new kolab_date_recurrence($object['_formatobj']); + $sql_data['dtend'] = date('Y-m-d 23:59:59', $recurrence->end() ?: strtotime('now +10 years')); + } + + return $sql_data; + } +} \ No newline at end of file
View file
kolab-syncroton-2.2.3.tar.gz/lib/plugins/libkolab/lib/kolab_storage_cache_file.php
Added
@@ -0,0 +1,44 @@ +<?php + +/** + * Kolab storage cache class for file objects + * + * @author Thomas Bruederli <bruederli@kolabsys.com> + * + * Copyright (C) 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/>. + */ + +class kolab_storage_cache_file extends kolab_storage_cache +{ + protected $extra_cols = array('filename'); + + /** + * Helper method to convert the given Kolab object into a dataset to be written to cache + * + * @override + */ + protected function _serialize($object) + { + $sql_data = parent::_serialize($object); + + if (!empty($object['_attachments'])) { + reset($object['_attachments']); + $sql_data['filename'] = $object['_attachments'][key($object['_attachments'])]['name']; + } + + return $sql_data; + } +} \ No newline at end of file
View file
kolab-syncroton-2.2.3.tar.gz/lib/plugins/libkolab/lib/kolab_storage_cache_freebusy.php
Added
@@ -0,0 +1,27 @@ +<?php + +/** + * Kolab storage cache class for freebusy objects + * + * @author Thomas Bruederli <bruederli@kolabsys.com> + * + * Copyright (C) 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/>. + */ + +class kolab_storage_cache_freebusy extends kolab_storage_cache +{ + protected $extra_cols = array('dtstart','dtend'); +} \ No newline at end of file
View file
kolab-syncroton-2.2.3.tar.gz/lib/plugins/libkolab/lib/kolab_storage_cache_journal.php
Added
@@ -0,0 +1,28 @@ +<?php + +/** + * Kolab storage cache class for journal objects + * + * @author Thomas Bruederli <bruederli@kolabsys.com> + * + * Copyright (C) 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/>. + */ + +class kolab_storage_cache_journal extends kolab_storage_cache +{ + protected $extra_cols = array('dtstart','dtend'); + +} \ No newline at end of file
View file
kolab-syncroton-2.2.3.tar.gz/lib/plugins/libkolab/lib/kolab_storage_cache_mongodb.php
Added
@@ -0,0 +1,561 @@ +<?php + +/** + * Kolab storage cache class providing a local caching layer for Kolab groupware objects. + * + * @version @package_version@ + * @author Thomas Bruederli <bruederli@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_storage_cache_mongodb +{ + private $db; + private $imap; + private $folder; + private $uid2msg; + private $objects; + private $index = array(); + private $resource_uri; + private $enabled = true; + private $synched = false; + private $synclock = false; + private $ready = false; + private $max_sql_packet = 1046576; // 1 MB - 2000 bytes + private $binary_cols = array('photo','pgppublickey','pkcs7publickey'); + + + /** + * Default constructor + */ + public function __construct(kolab_storage_folder $storage_folder = null) + { + $rcmail = rcube::get_instance(); + $mongo = new Mongo(); + $this->db = $mongo->kolab_cache; + $this->imap = $rcmail->get_storage(); + $this->enabled = $rcmail->config->get('kolab_cache', false); + + if ($this->enabled) { + // remove sync-lock on script termination + $rcmail->add_shutdown_function(array($this, '_sync_unlock')); + } + + if ($storage_folder) + $this->set_folder($storage_folder); + } + + + /** + * Connect cache with a storage folder + * + * @param kolab_storage_folder The storage folder instance to connect with + */ + public function set_folder(kolab_storage_folder $storage_folder) + { + $this->folder = $storage_folder; + + if (empty($this->folder->name)) { + $this->ready = false; + return; + } + + // compose fully qualified ressource uri for this instance + $this->resource_uri = $this->folder->get_resource_uri(); + $this->ready = $this->enabled; + } + + + /** + * Synchronize local cache data with remote + */ + public function synchronize() + { + // only sync once per request cycle + if ($this->synched) + return; + + // increase time limit + @set_time_limit(500); + + // lock synchronization for this folder or wait if locked + $this->_sync_lock(); + + // synchronize IMAP mailbox cache + $this->imap->folder_sync($this->folder->name); + + // compare IMAP index with object cache index + $imap_index = $this->imap->index($this->folder->name); + $this->index = $imap_index->get(); + + // determine objects to fetch or to invalidate + if ($this->ready) { + // read cache index + $old_index = array(); + $cursor = $this->db->cache->find(array('resource' => $this->resource_uri), array('msguid' => 1, 'uid' => 1)); + foreach ($cursor as $doc) { + $old_index[] = $doc['msguid']; + $this->uid2msg[$doc['uid']] = $doc['msguid']; + } + + // fetch new objects from imap + foreach (array_diff($this->index, $old_index) as $msguid) { + if ($object = $this->folder->read_object($msguid, '*')) { + try { + $this->db->cache->insert($this->_serialize($object, $msguid)); + } + catch (Exception $e) { + rcmail::raise_error(array( + 'code' => 900, 'type' => 'php', + 'message' => "Failed to write to mongodb cache: " . $e->getMessage(), + ), true); + } + } + } + + // delete invalid entries from local DB + $del_index = array_diff($old_index, $this->index); + if (!empty($del_index)) { + $this->db->cache->remove(array('resource' => $this->resource_uri, 'msguid' => array('$in' => $del_index))); + } + } + + // remove lock + $this->_sync_unlock(); + + $this->synched = time(); + } + + + /** + * Read a single entry from cache or from IMAP directly + * + * @param string Related IMAP message UID + * @param string Object type to read + * @param string IMAP folder name the entry relates to + * @param array Hash array with object properties or null if not found + */ + public function get($msguid, $type = null, $foldername = null) + { + // delegate to another cache instance + if ($foldername && $foldername != $this->folder->name) { + return kolab_storage::get_folder($foldername)->cache->get($msguid, $object); + } + + // load object if not in memory + if (!isset($this->objects[$msguid])) { + if ($this->ready && ($doc = $this->db->cache->findOne(array('resource' => $this->resource_uri, 'msguid' => $msguid)))) + $this->objects[$msguid] = $this->_unserialize($doc); + + // fetch from IMAP if not present in cache + if (empty($this->objects[$msguid])) { + $result = $this->_fetch(array($msguid), $type, $foldername); + $this->objects[$msguid] = $result[0]; + } + } + + return $this->objects[$msguid]; + } + + + /** + * Insert/Update a cache entry + * + * @param string Related IMAP message UID + * @param mixed Hash array with object properties to save or false to delete the cache entry + * @param string IMAP folder name the entry relates to + */ + public function set($msguid, $object, $foldername = null) + { + // delegate to another cache instance + if ($foldername && $foldername != $this->folder->name) { + kolab_storage::get_folder($foldername)->cache->set($msguid, $object); + return; + } + + // write to cache + if ($this->ready) { + // remove old entry + $this->db->cache->remove(array('resource' => $this->resource_uri, 'msguid' => $msguid)); + + // write new object data if not false (wich means deleted) + if ($object) { + try { + $this->db->cache->insert($this->_serialize($object, $msguid)); + } + catch (Exception $e) { + rcmail::raise_error(array( + 'code' => 900, 'type' => 'php', + 'message' => "Failed to write to mongodb cache: " . $e->getMessage(), + ), true); + } + } + } + + // keep a copy in memory for fast access + $this->objects[$msguid] = $object; + + if ($object) + $this->uid2msg[$object['uid']] = $msguid; + } + + /** + * Move an existing cache entry to a new resource + * + * @param string Entry's IMAP message UID + * @param string Entry's Object UID + * @param string Target IMAP folder to move it to + */ + public function move($msguid, $objuid, $target_folder) + { + $target = kolab_storage::get_folder($target_folder); + + // resolve new message UID in target folder + if ($new_msguid = $target->cache->uid2msguid($objuid)) { +/* + $this->db->query( + "UPDATE kolab_cache SET resource=?, msguid=? ". + "WHERE resource=? AND msguid=? AND type<>?", + $target->get_resource_uri(), + $new_msguid, + $this->resource_uri, + $msguid, + 'lock' + ); +*/ + } + else { + // just clear cache entry + $this->set($msguid, false); + } + + unset($this->uid2msg[$uid]); + } + + + /** + * Remove all objects from local cache + */ + public function purge($type = null) + { + return $this->db->cache->remove(array(), array('safe' => true)); + } + + + /** + * Select Kolab objects filtered by the given query + * + * @param array Pseudo-SQL query as list of filter parameter triplets + * triplet: array('<colname>', '<comparator>', '<value>') + * @return array List of Kolab data objects (each represented as hash array) + */ + public function select($query = array()) + { + $result = array(); + + // read from local cache DB (assume it to be synchronized) + if ($this->ready) { + $cursor = $this->db->cache->find(array('resource' => $this->resource_uri) + $this->_mongo_filter($query)); + foreach ($cursor as $doc) { + if ($object = $this->_unserialize($doc)) + $result[] = $object; + } + } + else { + // extract object type from query parameter + $filter = $this->_query2assoc($query); + + // use 'list' for folder's default objects + if ($filter['type'] == $this->type) { + $index = $this->index; + } + else { // search by object type + $search = 'UNDELETED HEADER X-Kolab-Type ' . kolab_storage_folder::KTYPE_PREFIX . $filter['type']; + $index = $this->imap->search_once($this->folder->name, $search)->get(); + } + + // fetch all messages in $index from IMAP + $result = $this->_fetch($index, $filter['type']); + + // TODO: post-filter result according to query + } + + return $result; + } + + + /** + * Get number of objects mathing the given query + * + * @param array $query Pseudo-SQL query as list of filter parameter triplets + * @return integer The number of objects of the given type + */ + public function count($query = array()) + { + $count = 0; + + // cache is in sync, we can count records in local DB + if ($this->synched) { + $cursor = $this->db->cache->find(array('resource' => $this->resource_uri) + $this->_mongo_filter($query)); + $count = $cursor->valid() ? $cursor->count() : 0; + } + else { + // search IMAP by object type + $filter = $this->_query2assoc($query); + $ctype = kolab_storage_folder::KTYPE_PREFIX . $filter['type']; + $index = $this->imap->search_once($this->folder->name, 'UNDELETED HEADER X-Kolab-Type ' . $ctype); + $count = $index->count(); + } + + return $count; + } + + /** + * Helper method to convert the pseudo-SQL query into a valid mongodb filter + */ + private function _mongo_filter($query) + { + $filters = array(); + foreach ($query as $param) { + $filter = array(); + if ($param[1] == '=' && is_array($param[2])) { + $filter[$param[0]] = array('$in' => $param[2]); + $filters[] = $filter; + } + else if ($param[1] == '=') { + $filters[] = array($param[0] => $param[2]); + } + else if ($param[1] == 'LIKE' || $param[1] == '~') { + $filter[$param[0]] = array('$regex' => preg_quote($param[2]), '$options' => 'i'); + $filters[] = $filter; + } + else if ($param[1] == '!~' || $param[1] == '!LIKE') { + $filter[$param[0]] = array('$not' => '/' . preg_quote($param[2]) . '/i'); + $filters[] = $filter; + } + else { + $op = ''; + switch ($param[1]) { + case '>': $op = '$gt'; break; + case '>=': $op = '$gte'; break; + case '<': $op = '$lt'; break; + case '<=': $op = '$lte'; break; + case '!=': + case '<>': $op = '$gte'; break; + } + if ($op) { + $filter[$param[0]] = array($op => $param[2]); + $filters[] = $filter; + } + } + } + + return array('$and' => $filters); + } + + /** + * Helper method to convert the given pseudo-query triplets into + * an associative filter array with 'equals' values only + */ + private function _query2assoc($query) + { + // extract object type from query parameter + $filter = array(); + foreach ($query as $param) { + if ($param[1] == '=') + $filter[$param[0]] = $param[2]; + } + return $filter; + } + + /** + * Fetch messages from IMAP + * + * @param array List of message UIDs to fetch + * @return array List of parsed Kolab objects + */ + private function _fetch($index, $type = null, $folder = null) + { + $results = array(); + foreach ((array)$index as $msguid) { + if ($object = $this->folder->read_object($msguid, $type, $folder)) { + $results[] = $object; + $this->set($msguid, $object); + } + } + + return $results; + } + + + /** + * Helper method to convert the given Kolab object into a dataset to be written to cache + */ + private function _serialize($object, $msguid) + { + $bincols = array_flip($this->binary_cols); + $doc = array( + 'resource' => $this->resource_uri, + 'type' => $object['_type'] ? $object['_type'] : $this->folder->type, + 'msguid' => $msguid, + 'uid' => $object['uid'], + 'xml' => '', + 'tags' => array(), + 'words' => array(), + 'objcols' => array(), + ); + + // set type specific values + if ($this->folder->type == 'event') { + // database runs in server's timezone so using date() is what we want + $doc['dtstart'] = date('Y-m-d H:i:s', is_object($object['start']) ? $object['start']->format('U') : $object['start']); + $doc['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']) { + $doc['dtend'] = date('Y-m-d H:i:s', $object['recurrence']['UNTIL'] ?: strtotime('now + 2 years')); + } + } + + if ($object['_formatobj']) { + $doc['xml'] = preg_replace('!(</?[a-z0-9:-]+>)[\n\r\t\s]+!ms', '$1', (string)$object['_formatobj']->write()); + $doc['tags'] = $object['_formatobj']->get_tags(); + $doc['words'] = $object['_formatobj']->get_words(); + } + + // extract object data + $data = array(); + foreach ($object as $key => $val) { + if ($val === "" || $val === null) { + // skip empty properties + continue; + } + if (isset($bincols[$key])) { + $data[$key] = base64_encode($val); + } + else if (is_object($val)) { + if (is_a($val, 'DateTime')) { + $data[$key] = array('_class' => 'DateTime', 'date' => $val->format('Y-m-d H:i:s'), 'timezone' => $val->getTimezone()->getName()); + $doc['objcols'][] = $key; + } + } + else if ($key[0] != '_') { + $data[$key] = $val; + } + else if ($key == '_attachments') { + foreach ($val as $k => $att) { + unset($att['content'], $att['path']); + if ($att['id']) + $data[$key][$k] = $att; + } + } + } + + $doc['data'] = $data; + return $doc; + } + + /** + * Helper method to turn stored cache data into a valid storage object + */ + private function _unserialize($doc) + { + $object = $doc['data']; + + // decode binary properties + foreach ($this->binary_cols as $key) { + if (!empty($object[$key])) + $object[$key] = base64_decode($object[$key]); + } + + // restore serialized objects + foreach ((array)$doc['objcols'] as $key) { + switch ($object[$key]['_class']) { + case 'DateTime': + $val = new DateTime($object[$key]['date'], new DateTimeZone($object[$key]['timezone'])); + $object[$key] = $val; + break; + } + } + + // add meta data + $object['_type'] = $doc['type']; + $object['_msguid'] = $doc['msguid']; + $object['_mailbox'] = $this->folder->name; + $object['_formatobj'] = kolab_format::factory($doc['type'], $doc['xml']); + + return $object; + } + + /** + * Check lock record for this folder and wait if locked or set lock + */ + private function _sync_lock() + { + if (!$this->ready) + return; + + $this->synclock = true; + $lock = $this->db->locks->findOne(array('resource' => $this->resource_uri)); + + // create lock record if not exists + if (!$lock) { + $this->db->locks->insert(array('resource' => $this->resource_uri, 'created' => time())); + } + // wait if locked (expire locks after 10 minutes) + else if ((time() - $lock['created']) < 600) { + usleep(500000); + return $this->_sync_lock(); + } + // set lock + else { + $lock['created'] = time(); + $this->db->locks->update(array('_id' => $lock['_id']), $lock, array('safe' => true)); + } + } + + /** + * Remove lock for this folder + */ + public function _sync_unlock() + { + if (!$this->ready || !$this->synclock) + return; + + $this->db->locks->remove(array('resource' => $this->resource_uri)); + } + + /** + * Resolve an object UID into an IMAP message UID + * + * @param string Kolab object UID + * @param boolean Include deleted objects + * @return int The resolved IMAP message UID + */ + public function uid2msguid($uid, $deleted = false) + { + if (!isset($this->uid2msg[$uid])) { + // use IMAP SEARCH to get the right message + $index = $this->imap->search_once($this->folder->name, ($deleted ? '' : 'UNDELETED ') . 'HEADER SUBJECT ' . $uid); + $results = $index->get(); + $this->uid2msg[$uid] = $results[0]; + } + + return $this->uid2msg[$uid]; + } + +}
View file
kolab-syncroton-2.2.3.tar.gz/lib/plugins/libkolab/lib/kolab_storage_cache_note.php
Added
@@ -0,0 +1,27 @@ +<?php + +/** + * Kolab storage cache class for note objects + * + * @author Thomas Bruederli <bruederli@kolabsys.com> + * + * Copyright (C) 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/>. + */ + +class kolab_storage_cache_note extends kolab_storage_cache +{ + +} \ No newline at end of file
View file
kolab-syncroton-2.2.3.tar.gz/lib/plugins/libkolab/lib/kolab_storage_cache_task.php
Added
@@ -0,0 +1,44 @@ +<?php + +/** + * Kolab storage cache class for task objects + * + * @author Thomas Bruederli <bruederli@kolabsys.com> + * + * Copyright (C) 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/>. + */ + +class kolab_storage_cache_task extends kolab_storage_cache +{ + protected $extra_cols = array('dtstart','dtend'); + + /** + * Helper method to convert the given Kolab object into a dataset to be written to cache + * + * @override + */ + protected function _serialize($object) + { + $sql_data = parent::_serialize($object) + array('dtstart' => null, 'dtend' => null); + + if ($object['start']) + $sql_data['dtstart'] = date('Y-m-d H:i:s', is_object($object['start']) ? $object['start']->format('U') : $object['start']); + if ($object['due']) + $sql_data['dtend'] = date('Y-m-d H:i:s', is_object($object['due']) ? $object['due']->format('U') : $object['due']); + + return $sql_data; + } +} \ No newline at end of file
View file
kolab-syncroton-2.2.2.tar.gz/lib/plugins/libkolab/lib/kolab_storage_folder.php -> kolab-syncroton-2.2.3.tar.gz/lib/plugins/libkolab/lib/kolab_storage_folder.php
Changed
@@ -764,6 +764,7 @@ // update cache with new UID if ($result) { $object['_msguid'] = $result; + $object['_mailbox'] = $this->name; $this->cache->insert($result, $object); // remove temp file
View file
kolab-syncroton.dsc
Changed
@@ -2,7 +2,7 @@ Source: kolab-syncroton Binary: kolab-syncroton Architecture: all -Version: 2.2.2-0~kolab2 +Version: 2.2.3-0~kolab1 Maintainer: Jeroen van Meeuwen (Kolab Systems) <vanmeeuwen@kolabsys.com> Uploaders: Paul Klos <kolab@klos2day.nl> Homepage: http://www.kolab.org/ @@ -12,5 +12,5 @@ Package-List: kolab-syncroton deb utils extra Files: - 00000000000000000000000000000000 0 kolab-syncroton-2.2.2.tar.gz + 00000000000000000000000000000000 0 kolab-syncroton-2.2.3.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
.