16 commits - plugins/calendar plugins/kolab_config plugins/libkolab plugins/tasklist

Thomas Brüderli bruederli at kolabsys.com
Fri Nov 9 17:34:04 CET 2012


 plugins/calendar/drivers/kolab/kolab_calendar.php        |   14 +
 plugins/kolab_config/kolab_config.php                    |    2 
 plugins/libkolab/config.inc.php.dist                     |    1 
 plugins/libkolab/lib/kolab_date_recurrence.php           |  109 +++------------
 plugins/libkolab/lib/kolab_format.php                    |  107 +++++++++++++-
 plugins/libkolab/lib/kolab_format_configuration.php      |   16 --
 plugins/libkolab/lib/kolab_format_contact.php            |   13 +
 plugins/libkolab/lib/kolab_format_distributionlist.php   |   12 -
 plugins/libkolab/lib/kolab_format_event.php              |   18 +-
 plugins/libkolab/lib/kolab_format_journal.php            |   12 -
 plugins/libkolab/lib/kolab_format_note.php               |   12 -
 plugins/libkolab/lib/kolab_format_task.php               |   11 -
 plugins/libkolab/lib/kolab_storage.php                   |    2 
 plugins/libkolab/lib/kolab_storage_cache.php             |   11 -
 plugins/libkolab/lib/kolab_storage_folder.php            |   59 +++-----
 plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php |    3 
 16 files changed, 220 insertions(+), 182 deletions(-)

New commits:
commit cf5c88437e60735849c8e990a280db0dc06b1e8c
Merge: fa6b80b 79d71d0
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Fri Nov 9 14:02:34 2012 +0100

    Merge branch 'dev/libcalendaring'

diff --cc plugins/libkolab/lib/kolab_format_event.php
index 90bfea3,6094c6a..d1e6b2e
--- a/plugins/libkolab/lib/kolab_format_event.php
+++ b/plugins/libkolab/lib/kolab_format_event.php
@@@ -143,7 -147,7 +147,7 @@@ class kolab_format_event extends kolab_
              $attach = $vattach->get($i);
  
              // skip cid: attachments which are mime message parts handled by kolab_storage_folder
-             if (substr($attach->uri(), 0, 4) != 'cid:') {
 -            if (substr($attach->uri(), 0, 4) != 'cid' && $attach->label()) {
++            if (substr($attach->uri(), 0, 4) != 'cid:' && $attach->label()) {
                  $name = $attach->label();
                  $data = $attach->data();
                  $object['_attachments'][$name] = array(
diff --cc plugins/libkolab/lib/kolab_storage_folder.php
index 7c28eff,682ca7e..302efd6
--- a/plugins/libkolab/lib/kolab_storage_folder.php
+++ b/plugins/libkolab/lib/kolab_storage_folder.php
@@@ -570,7 -548,16 +559,16 @@@ class kolab_storage_folde
              }
          }
  
-         // Parse attachments
+         // save contact photo to attachment for Kolab2 format
+         if (kolab_storage::$version == 2.0 && $object['photo'] && !$existing_photo) {
+             $attkey = 'kolab-picture.png';  // this file name is hard-coded in libkolab/kolabformatV2/contact.cpp
+             $object['_attachments'][$attkey] = array(
+                 'mimetype'=> rc_image_content_type($object['photo']),
+                 'content' => preg_match('![^a-z0-9/=+-]!i', $object['photo']) ? $object['photo'] : base64_decode($object['photo']),
+             );
+         }
+ 
 -        // generate unique keys (used as content-id) for attachments
++        // process attachments
          if (is_array($object['_attachments'])) {
              $numatt = count($object['_attachments']);
              foreach ($object['_attachments'] as $key => $attachment) {


commit 79d71d07310fe88346cb22c33afebc04f336f33e
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Fri Nov 9 13:57:54 2012 +0100

    Always use Kolab format v3 for caching

diff --git a/plugins/libkolab/lib/kolab_storage_cache.php b/plugins/libkolab/lib/kolab_storage_cache.php
index 6d50bf9..9eac164 100644
--- a/plugins/libkolab/lib/kolab_storage_cache.php
+++ b/plugins/libkolab/lib/kolab_storage_cache.php
@@ -534,7 +534,7 @@ class kolab_storage_cache
         }
 
         if ($object['_formatobj']) {
-            $sql_data['xml'] = preg_replace('!(</?[a-z0-9:-]+>)[\n\r\t\s]+!ms', '$1', (string)$object['_formatobj']->write());
+            $sql_data['xml'] = preg_replace('!(</?[a-z0-9:-]+>)[\n\r\t\s]+!ms', '$1', (string)$object['_formatobj']->write(3.0));
             $sql_data['tags'] = ' ' . join(' ', $object['_formatobj']->get_tags()) . ' ';  // pad with spaces for strict/prefix search
             $sql_data['words'] = ' ' . join(' ', $object['_formatobj']->get_words()) . ' ';
         }


commit 826f3f8ea39299a9c8e8cf105ec112ef32960c38
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Fri Nov 9 13:55:57 2012 +0100

    Avoid endless loops in recurrence computation

diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index a62eb54..c1c8626 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -409,6 +409,10 @@ class kolab_calendar
       }
       else if ($next_event['start'] > $end)  // stop loop if out of range
         break;
+
+	  // avoid endless recursion loops
+	  if ($i > 1000)
+		  break;
     }
     
     return $events;


commit 3cff4556329849bb8939c1c2de03362e42d13bad
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Nov 8 16:18:54 2012 +0100

    Ignore empty attachments from format v2

diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php
index 838f37d..6094c6a 100644
--- a/plugins/libkolab/lib/kolab_format_event.php
+++ b/plugins/libkolab/lib/kolab_format_event.php
@@ -147,7 +147,7 @@ class kolab_format_event extends kolab_format_xcal
             $attach = $vattach->get($i);
 
             // skip cid: attachments which are mime message parts handled by kolab_storage_folder
-            if (substr($attach->uri(), 0, 4) != 'cid') {
+            if (substr($attach->uri(), 0, 4) != 'cid' && $attach->label()) {
                 $name = $attach->label();
                 $data = $attach->data();
                 $object['_attachments'][$name] = array(


commit 0c418da7908806a4ee89fad56a814fa7939c1175
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Nov 8 16:18:28 2012 +0100

    Don't set due time if dateonly

diff --git a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
index 0f9d35d..f6ebe14 100644
--- a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
+++ b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
@@ -548,7 +548,8 @@ class tasklist_kolab_driver extends tasklist_driver
         // convert from DateTime to internal date format
         if (is_a($record['due'], 'DateTime')) {
             $task['date'] = $record['due']->format('Y-m-d');
-            $task['time'] = $record['due']->format('h:i');
+            if (!$record['due']->_dateonly)
+            	$task['time'] = $record['due']->format('h:i');
         }
         // convert from DateTime to internal date format
         if (is_a($record['start'], 'DateTime')) {


commit fa497d2dbbb4e87635c10651196f0a504e1a4f02
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Nov 8 15:41:23 2012 +0100

    Fix recurrence computation using libkolab bindings

diff --git a/plugins/libkolab/lib/kolab_date_recurrence.php b/plugins/libkolab/lib/kolab_date_recurrence.php
index 0df96e0..3aaa399 100644
--- a/plugins/libkolab/lib/kolab_date_recurrence.php
+++ b/plugins/libkolab/lib/kolab_date_recurrence.php
@@ -30,6 +30,7 @@ class kolab_date_recurrence
     private /* kolab_format_xcal */ $object;
     private /* DateTime */ $start;
     private /* DateTime */ $next;
+    private /* cDateTime */ $cnext;
     private /* DateInterval */ $duration;
 
     /**
@@ -44,6 +45,7 @@ class kolab_date_recurrence
         $this->object = $object;
         $this->engine = $object->to_libcal();
         $this->start = $this->next = $data['start'];
+        $this->cnext = kolab_format::get_datetime($this->next);
 
         if (is_object($data['start']) && is_object($data['end']))
             $this->duration = $data['start']->diff($data['end']);
@@ -62,9 +64,10 @@ class kolab_date_recurrence
         $time = false;
 
         if ($this->engine && $this->next) {
-            $cstart = kolab_format::get_datetime($this->next);
-            if ($next = kolab_format::php_datetime(new cDateTime($this->engine->getNextOccurence($cstart)))) {
+            if (($cnext = new cDateTime($this->engine->getNextOccurence($this->cnext))) && $cnext->isValid()) {
+                $next = kolab_format::php_datetime($cnext);
                 $time = $timestamp ? $next->format('U') : $next;
+                $this->cnext = $cnext;
                 $this->next = $next;
             }
         }


commit f91e6ed831da8cb1ac9c4d121ece42147b5ea87b
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Nov 8 14:41:37 2012 +0100

    Use generic escape() function to quote UIDs

diff --git a/plugins/libkolab/lib/kolab_storage_cache.php b/plugins/libkolab/lib/kolab_storage_cache.php
index cdb373f..6d50bf9 100644
--- a/plugins/libkolab/lib/kolab_storage_cache.php
+++ b/plugins/libkolab/lib/kolab_storage_cache.php
@@ -717,7 +717,8 @@ class kolab_storage_cache
     {
         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. '"');
+            $index = $this->imap->search_once($this->folder->name, ($deleted ? '' : 'UNDELETED ') .
+				'HEADER SUBJECT ' . rcube_imap_generic::escape($uid));
             $results = $index->get();
             $this->uid2msg[$uid] = $results[0];
         }


commit dfb7fae98367bf59540aff43979539cfc1ff27d1
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Nov 8 14:07:01 2012 +0100

    Fix contact picture handling with Kolab format v2

diff --git a/plugins/libkolab/lib/kolab_format_contact.php b/plugins/libkolab/lib/kolab_format_contact.php
index 9dedcb1..b147c38 100644
--- a/plugins/libkolab/lib/kolab_format_contact.php
+++ b/plugins/libkolab/lib/kolab_format_contact.php
@@ -379,6 +379,8 @@ class kolab_format_contact extends kolab_format
 
         if ($this->obj->photoMimetype())
             $object['photo'] = $this->obj->photo();
+        else if ($this->xmlobject && ($photo_name = $this->xmlobject->pictureAttachmentName()))
+        	$object['photo'] = $photo_name;
 
         // relateds -> spouse, children
         $this->read_relateds($this->obj->relateds(), $object);
diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php
index 54aed7a..682ca7e 100644
--- a/plugins/libkolab/lib/kolab_storage_folder.php
+++ b/plugins/libkolab/lib/kolab_storage_folder.php
@@ -540,13 +540,23 @@ class kolab_storage_folder
                     unset($object['_attachments'][$key]);
                 }
                 // load photo.attachment from old Kolab2 format to be directly embedded in xcard block
-                else if ($key == 'photo.attachment' && !isset($object['photo']) && !$object['_attachments'][$key]['content'] && $att['id']) {
-                    $object['photo'] = $this->get_attachment($object['_msguid'], $att['id'], $object['_mailbox']);
+                else if ($type == 'contact' && ($key == 'photo.attachment' || $key == 'kolab-picture.png') && $att['id']) {
+                    if (!isset($object['photo']))
+                        $object['photo'] = $this->get_attachment($object['_msguid'], $att['id'], $object['_mailbox']);
                     unset($object['_attachments'][$key]);
                 }
             }
         }
 
+        // save contact photo to attachment for Kolab2 format
+        if (kolab_storage::$version == 2.0 && $object['photo'] && !$existing_photo) {
+            $attkey = 'kolab-picture.png';  // this file name is hard-coded in libkolab/kolabformatV2/contact.cpp
+            $object['_attachments'][$attkey] = array(
+                'mimetype'=> rc_image_content_type($object['photo']),
+                'content' => preg_match('![^a-z0-9/=+-]!i', $object['photo']) ? $object['photo'] : base64_decode($object['photo']),
+            );
+        }
+
         // generate unique keys (used as content-id) for attachments
         if (is_array($object['_attachments'])) {
             $numatt = count($object['_attachments']);


commit 6d75fdd5f317d33d4f42208397de21e7fce47aed
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Nov 8 14:03:06 2012 +0100

    Quote object UID for IMAP SEARCH command; read generated UID from XMLObject if we're working with libkolab bindings

diff --git a/plugins/libkolab/lib/kolab_format.php b/plugins/libkolab/lib/kolab_format.php
index c54558e..a414781 100644
--- a/plugins/libkolab/lib/kolab_format.php
+++ b/plugins/libkolab/lib/kolab_format.php
@@ -265,7 +265,7 @@ abstract class kolab_format
     {
         // get generated UID
         if (!$this->data['uid']) {
-            $this->data['uid'] = kolabformat::getSerializedUID();
+            $this->data['uid'] = $this->xmlobject ? $this->xmlobject->getSerializedUID() : kolabformat::getSerializedUID();
             $this->obj->setUid($this->data['uid']);
         }
     }
diff --git a/plugins/libkolab/lib/kolab_storage_cache.php b/plugins/libkolab/lib/kolab_storage_cache.php
index a800e58..cdb373f 100644
--- a/plugins/libkolab/lib/kolab_storage_cache.php
+++ b/plugins/libkolab/lib/kolab_storage_cache.php
@@ -717,7 +717,7 @@ class kolab_storage_cache
     {
         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);
+            $index = $this->imap->search_once($this->folder->name, ($deleted ? '' : 'UNDELETED ') . 'HEADER SUBJECT "' . $uid. '"');
             $results = $index->get();
             $this->uid2msg[$uid] = $results[0];
         }


commit d98e8787c6b74bf26e9eec8df807435d455af990
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Tue Nov 6 19:03:12 2012 +0100

    Fix kolab_format_* classes to read v2 format; perpare kolab_storage to write out v2 format

diff --git a/plugins/libkolab/config.inc.php.dist b/plugins/libkolab/config.inc.php.dist
index fedf793..cb44652 100644
--- a/plugins/libkolab/config.inc.php.dist
+++ b/plugins/libkolab/config.inc.php.dist
@@ -2,6 +2,7 @@
     /* Configuration for libkolab */
 
     $rcmail_config['kolab_cache'] = true;
+	$rcmail_config['kolab_format_version']  = 3.0;
 
     $rcmail_config['kolab_freebusy_server'] = 'https://' . $_SESSION['imap_host'] . '/freebusy';
     $rcmail_config['kolab_ssl_verify_peer'] = true;
diff --git a/plugins/libkolab/lib/kolab_format.php b/plugins/libkolab/lib/kolab_format.php
index 08dc962..c54558e 100644
--- a/plugins/libkolab/lib/kolab_format.php
+++ b/plugins/libkolab/lib/kolab_format.php
@@ -30,6 +30,7 @@ abstract class kolab_format
     public static $timezone;
 
     public /*abstract*/ $CTYPE;
+    public /*abstract*/ $CTYPEv2;
 
     protected /*abstract*/ $objclass;
     protected /*abstract*/ $read_func;
@@ -49,11 +50,11 @@ abstract class kolab_format
      * Factory method to instantiate a kolab_format object of the given type and version
      *
      * @param string Object type to instantiate
-     * @param string Cached xml data to initialize with
      * @param float  Format version
+     * @param string Cached xml data to initialize with
      * @return object kolab_format
      */
-    public static function factory($type, $xmldata = null, $version = 3.0)
+    public static function factory($type, $version = 3.0, $xmldata = null)
     {
         if (!isset(self::$timezone))
             self::$timezone = new DateTimeZone('UTC');
@@ -249,7 +250,7 @@ abstract class kolab_format
                 'type' => 'php',
                 'file' => __FILE__,
                 'line' => __LINE__,
-                'message' => "kolabformat write $log: " . kolabformat::errorMessage(),
+                'message' => "kolabformat $log: " . kolabformat::errorMessage(),
             ), true);
         }
 
@@ -332,11 +333,12 @@ abstract class kolab_format
      */
     public function load($xml)
     {
-		$read_func = $this->libfunc($this->read_func);
-		if (is_array($read_func))
-			$r = call_user_func($read_func, $xml, $this->libversion());
-		else
-			$r = call_user_func($read_func, $xml, false);
+        $read_func = $this->libfunc($this->read_func);
+
+        if (is_array($read_func))
+            $r = call_user_func($read_func, $xml, $this->libversion());
+        else
+            $r = call_user_func($read_func, $xml, false);
 
         if (is_resource($r))
             $this->obj = new $this->objclass($r);
@@ -355,11 +357,11 @@ abstract class kolab_format
     public function write($version = null)
     {
         $this->init();
-		$write_func = $this->libfunc($this->write_func);
-		if (is_array($write_func))
-			$this->xmldata = call_user_func($write_func, $this->obj, $this->libversion($version), self::PRODUCT_ID);
-		else
-			$this->xmldata = call_user_func($write_func, $this->obj, self::PRODUCT_ID);
+        $write_func = $this->libfunc($this->write_func);
+        if (is_array($write_func))
+            $this->xmldata = call_user_func($write_func, $this->obj, $this->libversion($version), self::PRODUCT_ID);
+        else
+            $this->xmldata = call_user_func($write_func, $this->obj, self::PRODUCT_ID);
 
         if (!$this->format_errors())
             $this->update_uid();
diff --git a/plugins/libkolab/lib/kolab_format_configuration.php b/plugins/libkolab/lib/kolab_format_configuration.php
index 1bb919d..918928b 100644
--- a/plugins/libkolab/lib/kolab_format_configuration.php
+++ b/plugins/libkolab/lib/kolab_format_configuration.php
@@ -25,10 +25,11 @@
 class kolab_format_configuration extends kolab_format
 {
     public $CTYPE = 'application/x-vnd.kolab.configuration';
+    public $CTYPEv2 = 'application/x-vnd.kolab.configuration';
 
     protected $objclass = 'Configuration';
-    protected $read_func = 'kolabformat::readConfiguration';
-    protected $write_func = 'kolabformat::writeConfiguration';
+    protected $read_func = 'readConfiguration';
+    protected $write_func = 'writeConfiguration';
 
     private $type_map = array(
         'dictionary' => Configuration::TypeDictionary,
@@ -69,7 +70,7 @@ class kolab_format_configuration extends kolab_format
             $this->obj->setCreated(self::get_datetime($object['created']));
 
         // adjust content-type string
-        $this->CTYPE = 'application/x-vnd.kolab.configuration.' . $object['type'];
+        $this->CTYPE = $this->CTYPEv2 = 'application/x-vnd.kolab.configuration.' . $object['type'];
 
         // cache this data
         $this->data = $object;
@@ -121,7 +122,7 @@ class kolab_format_configuration extends kolab_format
 
         // adjust content-type string
         if ($object['type'])
-            $this->CTYPE = 'application/x-vnd.kolab.configuration.' . $object['type'];
+            $this->CTYPE = $this->CTYPEv2 = 'application/x-vnd.kolab.configuration.' . $object['type'];
 
         $this->data = $object;
         return $this->data;
diff --git a/plugins/libkolab/lib/kolab_format_contact.php b/plugins/libkolab/lib/kolab_format_contact.php
index bae5fa3..9dedcb1 100644
--- a/plugins/libkolab/lib/kolab_format_contact.php
+++ b/plugins/libkolab/lib/kolab_format_contact.php
@@ -25,6 +25,7 @@
 class kolab_format_contact extends kolab_format
 {
     public $CTYPE = 'application/vcard+xml';
+    public $CTYPEv2 = 'application/x-vnd.kolab.contact';
 
     protected $objclass = 'Contact';
     protected $read_func = 'readContact';
diff --git a/plugins/libkolab/lib/kolab_format_distributionlist.php b/plugins/libkolab/lib/kolab_format_distributionlist.php
index 6def48f..ba54742 100644
--- a/plugins/libkolab/lib/kolab_format_distributionlist.php
+++ b/plugins/libkolab/lib/kolab_format_distributionlist.php
@@ -25,6 +25,7 @@
 class kolab_format_distributionlist extends kolab_format
 {
     public $CTYPE = 'application/vcard+xml';
+    public $CTYPEv2 = 'application/x-vnd.kolab.distribution-list';
 
     protected $objclass = 'DistList';
     protected $read_func = 'readDistlist';
diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php
index b2cb87b..838f37d 100644
--- a/plugins/libkolab/lib/kolab_format_event.php
+++ b/plugins/libkolab/lib/kolab_format_event.php
@@ -24,6 +24,8 @@
 
 class kolab_format_event extends kolab_format_xcal
 {
+    public $CTYPEv2 = 'application/x-vnd.kolab.event';
+
     protected $objclass = 'Event';
     protected $read_func = 'readEvent';
     protected $write_func = 'writeEvent';
diff --git a/plugins/libkolab/lib/kolab_format_journal.php b/plugins/libkolab/lib/kolab_format_journal.php
index 0e1b474..9144ea2 100644
--- a/plugins/libkolab/lib/kolab_format_journal.php
+++ b/plugins/libkolab/lib/kolab_format_journal.php
@@ -25,6 +25,7 @@
 class kolab_format_journal extends kolab_format
 {
     public $CTYPE = 'application/calendar+xml';
+    public $CTYPEv2 = 'application/x-vnd.kolab.journal';
 
     protected $objclass = 'Journal';
     protected $read_func = 'readJournal';
diff --git a/plugins/libkolab/lib/kolab_format_note.php b/plugins/libkolab/lib/kolab_format_note.php
index 482a3e8..48e963e 100644
--- a/plugins/libkolab/lib/kolab_format_note.php
+++ b/plugins/libkolab/lib/kolab_format_note.php
@@ -25,6 +25,7 @@
 class kolab_format_note extends kolab_format
 {
     public $CTYPE = 'application/x-vnd.kolab.note';
+    public $CTYPEv2 = 'application/x-vnd.kolab.note';
 
     protected $objclass = 'Note';
     protected $read_func = 'readNote';
diff --git a/plugins/libkolab/lib/kolab_format_task.php b/plugins/libkolab/lib/kolab_format_task.php
index 425f5e0..0bfac3d 100644
--- a/plugins/libkolab/lib/kolab_format_task.php
+++ b/plugins/libkolab/lib/kolab_format_task.php
@@ -24,6 +24,8 @@
 
 class kolab_format_task extends kolab_format_xcal
 {
+    public $CTYPEv2 = 'application/x-vnd.kolab.task';
+
     protected $objclass = 'Todo';
     protected $read_func = 'readTodo';
     protected $write_func = 'writeTodo';
diff --git a/plugins/libkolab/lib/kolab_storage_cache.php b/plugins/libkolab/lib/kolab_storage_cache.php
index 866a795..a800e58 100644
--- a/plugins/libkolab/lib/kolab_storage_cache.php
+++ b/plugins/libkolab/lib/kolab_storage_cache.php
@@ -582,7 +582,7 @@ class kolab_storage_cache
         $object['_type'] = $sql_arr['type'];
         $object['_msguid'] = $sql_arr['msguid'];
         $object['_mailbox'] = $this->folder->name;
-        $object['_formatobj'] = kolab_format::factory($sql_arr['type'], $sql_arr['xml']);
+        $object['_formatobj'] = kolab_format::factory($sql_arr['type'], 3.0, $sql_arr['xml']);
 
         return $object;
     }
diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php
index e92a6fa..54aed7a 100644
--- a/plugins/libkolab/lib/kolab_storage_folder.php
+++ b/plugins/libkolab/lib/kolab_storage_folder.php
@@ -719,8 +719,9 @@ class kolab_storage_folder
             . 'To view this object you will need an email client that understands the Kolab Groupware format. '
             . "For a list of such email clients please visit http://www.kolab.org/\n\n");
 
+        $ctype = kolab_storage::$version == 2.0 ? $format->CTYPEv2 : $format->CTYPE;
         $mime->addAttachment($xml,  // file
-            $format->CTYPE,         // content-type
+            $ctype,                 // content-type
             'kolab.xml',            // filename
             false,                  // is_file
             '8bit',                 // encoding


commit dd2bf15adf7e39612df9548095ac1ed8cafb33a1
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Fri Nov 2 15:04:59 2012 +0100

    Also use different signatures when calling libkolab(xml) read functions

diff --git a/plugins/libkolab/lib/kolab_format.php b/plugins/libkolab/lib/kolab_format.php
index 760133b..08dc962 100644
--- a/plugins/libkolab/lib/kolab_format.php
+++ b/plugins/libkolab/lib/kolab_format.php
@@ -332,7 +332,12 @@ abstract class kolab_format
      */
     public function load($xml)
     {
-        $r = call_user_func($this->libfunc($this->read_func), $xml, $this->libversion());
+		$read_func = $this->libfunc($this->read_func);
+		if (is_array($read_func))
+			$r = call_user_func($read_func, $xml, $this->libversion());
+		else
+			$r = call_user_func($read_func, $xml, false);
+
         if (is_resource($r))
             $this->obj = new $this->objclass($r);
         else if (is_a($r, $this->objclass))


commit dbe7352ce100eb6c8d6619d68fe9b6a81b029112
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Fri Nov 2 13:46:00 2012 +0100

    Fix default config folder selection (list is not indexed anymore)

diff --git a/plugins/kolab_config/kolab_config.php b/plugins/kolab_config/kolab_config.php
index 4fe8a7f..23188cf 100644
--- a/plugins/kolab_config/kolab_config.php
+++ b/plugins/kolab_config/kolab_config.php
@@ -77,7 +77,7 @@ class kolab_config extends rcube_plugin
 
         // if no folder is set as default, choose the first one
         if (!$this->default)
-            $this->default = $this->folders[0];
+            $this->default = reset($this->folders);
 
         // check if configuration folder exist
         if ($this->default && $this->default->name) {


commit 50b3b8e766ca081f492521a4f502284e139c8915
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Fri Nov 2 13:45:05 2012 +0100

    Use different signatures for libkolab/libkolabxml function calls

diff --git a/plugins/libkolab/lib/kolab_format.php b/plugins/libkolab/lib/kolab_format.php
index bedee44..760133b 100644
--- a/plugins/libkolab/lib/kolab_format.php
+++ b/plugins/libkolab/lib/kolab_format.php
@@ -350,7 +350,11 @@ abstract class kolab_format
     public function write($version = null)
     {
         $this->init();
-        $this->xmldata = call_user_func($this->libfunc($this->write_func), $this->obj, $this->libversion($version), self::PRODUCT_ID);
+		$write_func = $this->libfunc($this->write_func);
+		if (is_array($write_func))
+			$this->xmldata = call_user_func($write_func, $this->obj, $this->libversion($version), self::PRODUCT_ID);
+		else
+			$this->xmldata = call_user_func($write_func, $this->obj, self::PRODUCT_ID);
 
         if (!$this->format_errors())
             $this->update_uid();


commit d55e56c07ca04bbe642e9b60c095c7938a16d424
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Thu Nov 1 20:06:09 2012 +0100

    Use new libkolab XML object reading/writing functions in preparation of adding Kolab format v2 capabilities

diff --git a/plugins/libkolab/lib/kolab_format.php b/plugins/libkolab/lib/kolab_format.php
index 23246d3..bedee44 100644
--- a/plugins/libkolab/lib/kolab_format.php
+++ b/plugins/libkolab/lib/kolab_format.php
@@ -31,39 +31,60 @@ abstract class kolab_format
 
     public /*abstract*/ $CTYPE;
 
+    protected /*abstract*/ $objclass;
     protected /*abstract*/ $read_func;
     protected /*abstract*/ $write_func;
 
     protected $obj;
     protected $data;
     protected $xmldata;
+    protected $xmlobject;
     protected $loaded = false;
+    protected $version = 3.0;
 
-    const VERSION = '3.0';
     const KTYPE_PREFIX = 'application/x-vnd.kolab.';
+    const PRODUCT_ID = 'Roundcube-libkolab-0.9';
 
     /**
-     * Factory method to instantiate a kolab_format object of the given type
+     * Factory method to instantiate a kolab_format object of the given type and version
      *
      * @param string Object type to instantiate
      * @param string Cached xml data to initialize with
+     * @param float  Format version
      * @return object kolab_format
      */
-    public static function factory($type, $xmldata = null)
+    public static function factory($type, $xmldata = null, $version = 3.0)
     {
         if (!isset(self::$timezone))
             self::$timezone = new DateTimeZone('UTC');
 
+        if (!self::supports($version))
+            return PEAR::raiseError("No support for Kolab format version " . $version);
+
         $type = preg_replace('/configuration\.[a-z.]+$/', 'configuration', $type);
         $suffix = preg_replace('/[^a-z]+/', '', $type);
         $classname = 'kolab_format_' . $suffix;
         if (class_exists($classname))
-            return new $classname($xmldata);
+            return new $classname($xmldata, $version);
 
         return PEAR::raiseError("Failed to load Kolab Format wrapper for type " . $type);
     }
 
     /**
+     * Determine support for the given format version
+     *
+     * @param float Format version to check
+     * @return boolean True if supported, False otherwise
+     */
+    public static function supports($version)
+    {
+        if ($version == 2.0)
+            return class_exists('kolabobject');
+        // default is version 3
+        return class_exists('kolabformat');
+    }
+
+    /**
      * Convert the given date/time value into a cDateTime object
      *
      * @param mixed         Date/Time value either as unix timestamp, date string or PHP DateTime object
@@ -184,6 +205,23 @@ abstract class kolab_format
         return preg_replace('/dictionary.[a-z.]+$/', 'dictionary', substr($x_kolab_type, strlen(self::KTYPE_PREFIX)));
     }
 
+
+    /**
+     * Default constructor of all kolab_format_* objects
+     */
+    public function __construct($xmldata = null, $version = null)
+    {
+        $this->obj = new $this->objclass;
+        $this->xmldata = $xmldata;
+
+        if ($version)
+            $this->version = $version;
+
+        // use libkolab module if available
+        if (class_exists('kolabobject'))
+            $this->xmlobject = new XMLObject();
+    }
+
     /**
      * Check for format errors after calling kolabformat::write*()
      *
@@ -246,6 +284,39 @@ abstract class kolab_format
     }
 
     /**
+     * Get constant value for libkolab's version parameter
+     *
+     * @param float Version value to convert
+     * @return int Constant value of either kolabobject::KolabV2 or kolabobject::KolabV3 or false if kolabobject module isn't available
+     */
+    protected function libversion($v = null)
+    {
+        if (class_exists('kolabobject')) {
+            $version = $v ?: $this->version;
+            if ($version <= 2.0)
+                return kolabobject::KolabV2;
+            else
+                return kolabobject::KolabV3;
+        }
+
+        return false;
+    }
+
+    /**
+     * Determine the correct libkolab(xml) wrapper function for the given call
+     * depending on the available PHP modules
+     */
+    protected function libfunc($func)
+    {
+        if (is_array($func) || strpos($func, '::'))
+            return $func;
+        else if (class_exists('kolabobject'))
+            return array($this->xmlobject, $func);
+        else
+            return 'kolabformat::' . $func;
+    }
+
+    /**
      * Direct getter for object properties
      */
     public function __get($var)
@@ -257,22 +328,29 @@ abstract class kolab_format
      * Load Kolab object data from the given XML block
      *
      * @param string XML data
+     * @return boolean True on success, False on failure
      */
     public function load($xml)
     {
-        $this->obj = call_user_func($this->read_func, $xml, false);
+        $r = call_user_func($this->libfunc($this->read_func), $xml, $this->libversion());
+        if (is_resource($r))
+            $this->obj = new $this->objclass($r);
+        else if (is_a($r, $this->objclass))
+            $this->obj = $r;
+
         $this->loaded = !$this->format_errors();
     }
 
     /**
      * Write object data to XML format
      *
+     * @param float Format version to write
      * @return string XML data
      */
-    public function write()
+    public function write($version = null)
     {
         $this->init();
-        $this->xmldata = call_user_func($this->write_func, $this->obj);
+        $this->xmldata = call_user_func($this->libfunc($this->write_func), $this->obj, $this->libversion($version), self::PRODUCT_ID);
 
         if (!$this->format_errors())
             $this->update_uid();
diff --git a/plugins/libkolab/lib/kolab_format_configuration.php b/plugins/libkolab/lib/kolab_format_configuration.php
index 974fc45..1bb919d 100644
--- a/plugins/libkolab/lib/kolab_format_configuration.php
+++ b/plugins/libkolab/lib/kolab_format_configuration.php
@@ -26,6 +26,7 @@ class kolab_format_configuration extends kolab_format
 {
     public $CTYPE = 'application/x-vnd.kolab.configuration';
 
+    protected $objclass = 'Configuration';
     protected $read_func = 'kolabformat::readConfiguration';
     protected $write_func = 'kolabformat::writeConfiguration';
 
@@ -35,12 +36,6 @@ class kolab_format_configuration extends kolab_format
     );
 
 
-    function __construct($xmldata = null)
-    {
-        $this->obj = new Configuration;
-        $this->xmldata = $xmldata;
-    }
-
     /**
      * Set properties to the kolabformat object
      *
diff --git a/plugins/libkolab/lib/kolab_format_contact.php b/plugins/libkolab/lib/kolab_format_contact.php
index ffef059..bae5fa3 100644
--- a/plugins/libkolab/lib/kolab_format_contact.php
+++ b/plugins/libkolab/lib/kolab_format_contact.php
@@ -26,8 +26,9 @@ class kolab_format_contact extends kolab_format
 {
     public $CTYPE = 'application/vcard+xml';
 
-    protected $read_func = 'kolabformat::readContact';
-    protected $write_func = 'kolabformat::writeContact';
+    protected $objclass = 'Contact';
+    protected $read_func = 'readContact';
+    protected $write_func = 'writeContact';
 
     public static $fulltext_cols = array('name', 'firstname', 'surname', 'middlename', 'email');
 
@@ -106,10 +107,9 @@ class kolab_format_contact extends kolab_format
     /**
      * Default constructor
      */
-    function __construct($xmldata = null)
+    function __construct($xmldata = null, $version = 3.0)
     {
-        $this->obj = new Contact;
-        $this->xmldata = $xmldata;
+        parent::__construct($xmldata, $version);
 
         // complete phone types
         $this->phonetypes['homefax'] |= Telephone::Home;
diff --git a/plugins/libkolab/lib/kolab_format_distributionlist.php b/plugins/libkolab/lib/kolab_format_distributionlist.php
index fcb94c1..6def48f 100644
--- a/plugins/libkolab/lib/kolab_format_distributionlist.php
+++ b/plugins/libkolab/lib/kolab_format_distributionlist.php
@@ -26,16 +26,11 @@ class kolab_format_distributionlist extends kolab_format
 {
     public $CTYPE = 'application/vcard+xml';
 
-    protected $read_func = 'kolabformat::readDistlist';
-    protected $write_func = 'kolabformat::writeDistlist';
+    protected $objclass = 'DistList';
+    protected $read_func = 'readDistlist';
+    protected $write_func = 'writeDistlist';
 
 
-    function __construct($xmldata = null)
-    {
-        $this->obj = new DistList;
-        $this->xmldata = $xmldata;
-    }
-
     /**
      * Set properties to the kolabformat object
      *
diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php
index 4e790f2..b2cb87b 100644
--- a/plugins/libkolab/lib/kolab_format_event.php
+++ b/plugins/libkolab/lib/kolab_format_event.php
@@ -24,8 +24,9 @@
 
 class kolab_format_event extends kolab_format_xcal
 {
-    protected $read_func = 'kolabformat::readEvent';
-    protected $write_func = 'kolabformat::writeEvent';
+    protected $objclass = 'Event';
+    protected $read_func = 'readEvent';
+    protected $write_func = 'writeEvent';
 
     private $kolab2_rolemap = array(
         'required' => 'REQ-PARTICIPANT',
@@ -43,24 +44,15 @@ class kolab_format_event extends kolab_format_xcal
 
 
     /**
-     * Default constructor
+     * Clones into an instance of libcalendaring's extended EventCal class
+     *
+     * @return mixed EventCal object or false on failure
      */
-    function __construct($xmldata = null)
+    public function to_libcal()
     {
-        $this->obj = new Event;
-        $this->xmldata = $xmldata;
+        return class_exists('kolabcalendaring') ? new EventCal($this->obj) : false;
     }
 
-	/**
-	 * Clones into an instance of libcalendaring's extended EventCal class
-	 *
-	 * @return mixed EventCal object or false on failure
-	 */
-	public function to_libcal()
-	{
-		return class_exists('kolabcalendaring') ? new EventCal($this->obj) : false;
-	}
-
     /**
      * Set event properties to the kolabformat object
      *
diff --git a/plugins/libkolab/lib/kolab_format_journal.php b/plugins/libkolab/lib/kolab_format_journal.php
index 5869af0..0e1b474 100644
--- a/plugins/libkolab/lib/kolab_format_journal.php
+++ b/plugins/libkolab/lib/kolab_format_journal.php
@@ -26,16 +26,11 @@ class kolab_format_journal extends kolab_format
 {
     public $CTYPE = 'application/calendar+xml';
 
-    protected $read_func = 'kolabformat::readJournal';
-    protected $write_func = 'kolabformat::writeJournal';
+    protected $objclass = 'Journal';
+    protected $read_func = 'readJournal';
+    protected $write_func = 'writeJournal';
 
 
-    function __construct($xmldata = null)
-    {
-        $this->obj = new Journal;
-        $this->xmldata = $xmldata;
-    }
-
     /**
      * Set properties to the kolabformat object
      *
diff --git a/plugins/libkolab/lib/kolab_format_note.php b/plugins/libkolab/lib/kolab_format_note.php
index 1c88a8b..482a3e8 100644
--- a/plugins/libkolab/lib/kolab_format_note.php
+++ b/plugins/libkolab/lib/kolab_format_note.php
@@ -26,16 +26,11 @@ class kolab_format_note extends kolab_format
 {
     public $CTYPE = 'application/x-vnd.kolab.note';
 
-    protected $read_func = 'kolabformat::readNote';
-    protected $write_func = 'kolabformat::writeNote';
+    protected $objclass = 'Note';
+    protected $read_func = 'readNote';
+    protected $write_func = 'writeNote';
 
 
-    function __construct($xmldata = null)
-    {
-        $this->obj = new Note;
-        $this->xmldata = $xmldata;
-    }
-
     /**
      * Set properties to the kolabformat object
      *
diff --git a/plugins/libkolab/lib/kolab_format_task.php b/plugins/libkolab/lib/kolab_format_task.php
index 2a7a629..425f5e0 100644
--- a/plugins/libkolab/lib/kolab_format_task.php
+++ b/plugins/libkolab/lib/kolab_format_task.php
@@ -24,16 +24,11 @@
 
 class kolab_format_task extends kolab_format_xcal
 {
-    protected $read_func = 'kolabformat::readTodo';
-    protected $write_func = 'kolabformat::writeTodo';
+    protected $objclass = 'Todo';
+    protected $read_func = 'readTodo';
+    protected $write_func = 'writeTodo';
 
 
-    function __construct($xmldata = null)
-    {
-        $this->obj = new Todo;
-        $this->xmldata = $xmldata;
-    }
-
     /**
      * Set properties to the kolabformat object
      *
diff --git a/plugins/libkolab/lib/kolab_storage.php b/plugins/libkolab/lib/kolab_storage.php
index 1241934..ac7de34 100644
--- a/plugins/libkolab/lib/kolab_storage.php
+++ b/plugins/libkolab/lib/kolab_storage.php
@@ -31,6 +31,7 @@ class kolab_storage
     const SERVERSIDE_SUBSCRIPTION = 0;
     const CLIENTSIDE_SUBSCRIPTION = 1;
 
+    public static $version = 3.0;
     public static $last_error;
 
     private static $ready = false;
@@ -49,6 +50,7 @@ class kolab_storage
 
         $rcmail = rcube::get_instance();
         self::$config = $rcmail->config;
+        self::$version = $rcmail->config->get('kolab_format_version', self::$version);
         self::$imap = $rcmail->get_storage();
         self::$ready = class_exists('kolabformat') &&
             (self::$imap->get_capability('METADATA') || self::$imap->get_capability('ANNOTATEMORE') || self::$imap->get_capability('ANNOTATEMORE2'));
diff --git a/plugins/libkolab/lib/kolab_storage_folder.php b/plugins/libkolab/lib/kolab_storage_folder.php
index 08bf669..e92a6fa 100644
--- a/plugins/libkolab/lib/kolab_storage_folder.php
+++ b/plugins/libkolab/lib/kolab_storage_folder.php
@@ -466,39 +466,27 @@ class kolab_storage_folder
             return false;
         }
 
-        $format = kolab_format::factory($object_type);
-
-        if (is_a($format, 'PEAR_Error'))
-            return false;
-
         // check kolab format version
-        $mime_version = $headers->others['x-kolab-mime-version'];
-        if (empty($mime_version)) {
+        $format_version = $headers->others['x-kolab-mime-version'];
+        if (empty($format_version)) {
             list($xmltype, $subtype) = explode('.', $object_type);
             $xmlhead = substr($xml, 0, 512);
 
             // detect old Kolab 2.0 format
             if (strpos($xmlhead, '<' . $xmltype) !== false && strpos($xmlhead, 'xmlns=') === false)
-                $mime_version = 2.0;
+                $format_version = 2.0;
             else
-                $mime_version = 3.0; // assume 3.0
+                $format_version = 3.0; // assume 3.0
         }
 
-        if ($mime_version <= 2.0) {
-            // read Kolab 2.0 format
-            $handler = class_exists('Horde_Kolab_Format') ? Horde_Kolab_Format::factory('XML', $xmltype, array('subtype' => $subtype)) : null;
-            if (!is_object($handler) || is_a($handler, 'PEAR_Error')) {
-                return false;
-            }
+        // get Kolab format handler for the given type
+        $format = kolab_format::factory($object_type, $format_version);
 
-            // XML-to-array
-            $object = $handler->load($xml);
-            $format->fromkolab2($object);
-        }
-        else {
-            // load Kolab 3 format using libkolabxml
-            $format->load($xml);
-        }
+        if (is_a($format, 'PEAR_Error'))
+            return false;
+
+        // load Kolab object from XML part
+        $format->load($xml);
 
         if ($format->is_valid()) {
             $object = $format->to_array();
@@ -696,13 +684,13 @@ class kolab_storage_folder
 
         // create new kolab_format instance
         if (!$format)
-            $format = kolab_format::factory($type);
+            $format = kolab_format::factory($type, kolab_storage::$version);
 
         if (PEAR::isError($format))
             return false;
 
         $format->set($object);
-        $xml = $format->write();
+        $xml = $format->write(kolab_storage::$version);
         $object['uid'] = $format->uid;  // read UID from format
         $object['_formatobj'] = $format;
 
@@ -721,7 +709,7 @@ class kolab_storage_folder
         }
         $headers['Date'] = date('r');
         $headers['X-Kolab-Type'] = kolab_format::KTYPE_PREFIX . $type;
-        $headers['X-Kolab-Mime-Version'] = kolab_format::VERSION;
+        $headers['X-Kolab-Mime-Version'] = kolab_storage::$version;
         $headers['Subject'] = $object['uid'];
 //        $headers['Message-ID'] = $rcmail->gen_message_id();
         $headers['User-Agent'] = $rcmail->config->get('useragent');


commit 06e6c43db027cb6539d5eb417322aff556a5f3a7
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date:   Wed Oct 31 14:01:54 2012 +0100

    Use the right library function to get the last occurence of a recurrence chain

diff --git a/plugins/libkolab/lib/kolab_date_recurrence.php b/plugins/libkolab/lib/kolab_date_recurrence.php
index 2e883d0..0df96e0 100644
--- a/plugins/libkolab/lib/kolab_date_recurrence.php
+++ b/plugins/libkolab/lib/kolab_date_recurrence.php
@@ -104,8 +104,7 @@ class kolab_date_recurrence
     public function end($limit = 'now +1 year')
     {
         $limit_dt = new DateTime($limit);
-        $cstart = kolab_format::get_datetime($this->start);
-        if ($this->engine && ($cend = $this->engine->getOccurenceEndDate($cstart)) && ($end_dt = kolab_format::php_datetime(new cDateTime($cend))) && $end_dt < $limit_dt) {
+        if ($this->engine && ($cend = $this->engine->getLastOccurrence()) && ($end_dt = kolab_format::php_datetime(new cDateTime($cend))) && $end_dt < $limit_dt) {
             return $end_dt->format('U');
         }
 


commit b4ced09d2e91a7be0649aa4c7866b4f817fcffbc
Author: Thomas Brüderli <bruederli at kolabsys.com>
Date:   Tue Oct 23 15:05:38 2012 +0200

    Make use of libkolab/libcalendaring PHP bindings for computing recurring events

diff --git a/plugins/calendar/drivers/kolab/kolab_calendar.php b/plugins/calendar/drivers/kolab/kolab_calendar.php
index e20133e..a62eb54 100644
--- a/plugins/calendar/drivers/kolab/kolab_calendar.php
+++ b/plugins/calendar/drivers/kolab/kolab_calendar.php
@@ -376,7 +376,15 @@ class kolab_calendar
    */
   public function _get_recurring_events($event, $start, $end, $event_id = null)
   {
-    $recurrence = new kolab_date_recurrence($event);
+    $object = $event['_formatobj'];
+    if (!$object) {
+      $rec = $this->storage->get_object($event['id']);
+      $object = $rec['_formatobj'];
+    }
+    if (!is_object($object))
+      return array();
+
+    $recurrence = new kolab_date_recurrence($object);
 
     $i = 0;
     $events = array();
diff --git a/plugins/libkolab/lib/kolab_date_recurrence.php b/plugins/libkolab/lib/kolab_date_recurrence.php
index 427f62a..2e883d0 100644
--- a/plugins/libkolab/lib/kolab_date_recurrence.php
+++ b/plugins/libkolab/lib/kolab_date_recurrence.php
@@ -3,7 +3,8 @@
 /**
  * Recurrence computation class for xcal-based Kolab format objects
  *
- * Uitility class to compute instances of recurring events.
+ * Utility class to compute instances of recurring events.
+ * It requires the libcalendaring PHP module to be installed and loaded.
  *
  * @version @package_version@
  * @author Thomas Bruederli <bruederli at kolabsys.com>
@@ -25,14 +26,11 @@
  */
 class kolab_date_recurrence
 {
-    private $engine;
-    private $object;
-    private $next;
-    private $duration;
-    private $tz_offset = 0;
-    private $dst_start = 0;
-    private $allday = false;
-    private $hour = 0;
+    private /* EventCal */ $engine;
+    private /* kolab_format_xcal */ $object;
+    private /* DateTime */ $start;
+    private /* DateTime */ $next;
+    private /* DateInterval */ $duration;
 
     /**
      * Default constructor
@@ -41,27 +39,16 @@ class kolab_date_recurrence
      */
     function __construct($object)
     {
+        $data = $object->to_array();
+
         $this->object = $object;
-        $this->next = new Horde_Date($object['start'], kolab_format::$timezone->getName());
+        $this->engine = $object->to_libcal();
+        $this->start = $this->next = $data['start'];
 
-        if (is_object($object['start']) && is_object($object['end']))
-            $this->duration = $object['start']->diff($object['end']);
+        if (is_object($data['start']) && is_object($data['end']))
+            $this->duration = $data['start']->diff($data['end']);
         else
-            $this->duration = new DateInterval('PT' . ($object['end'] - $object['start']) . 'S');
-
-        // use (copied) Horde classes to compute recurring instances
-        // TODO: replace with something that has less than 6'000 lines of code
-        $this->engine = new Horde_Date_Recurrence($this->next);
-        $this->engine->fromRRule20($this->to_rrule($object['recurrence']));  // TODO: get that string directly from libkolabxml
-
-        foreach ((array)$object['recurrence']['EXDATE'] as $exdate)
-            $this->engine->addException($exdate->format('Y'), $exdate->format('n'), $exdate->format('j'));
-
-        $now = new DateTime('now', kolab_format::$timezone);
-        $this->tz_offset = $object['allday'] ? $now->getOffset() - date('Z') : 0;
-        $this->dst_start = $this->next->format('I');
-        $this->allday = $object['allday'];
-        $this->hour = $this->next->hour;
+            $this->duration = new DateInterval('PT' . ($data['end'] - $data['start']) . 'S');
     }
 
     /**
@@ -73,20 +60,13 @@ class kolab_date_recurrence
     public function next_start($timestamp = false)
     {
         $time = false;
-        if ($this->next && ($next = $this->engine->nextActiveRecurrence(array('year' => $this->next->year, 'month' => $this->next->month, 'mday' => $this->next->mday + 1, 'hour' => $this->next->hour, 'min' => $this->next->min, 'sec' => $this->next->sec)))) {
-            if ($this->allday) {
-                $next->hour = $this->hour;  # fix time for all-day events
-                $next->min = 0;
-            }
-            if ($timestamp) {
-                # consider difference in daylight saving between base event and recurring instance
-                $dst_diff = ($this->dst_start - $next->format('I')) * 3600;
-                $time = $next->timestamp() - $this->tz_offset - $dst_diff;
-            }
-            else {
-                $time = $next->toDateTime();
+
+        if ($this->engine && $this->next) {
+            $cstart = kolab_format::get_datetime($this->next);
+            if ($next = kolab_format::php_datetime(new cDateTime($this->engine->getNextOccurence($cstart)))) {
+                $time = $timestamp ? $next->format('U') : $next;
+                $this->next = $next;
             }
-            $this->next = $next;
         }
 
         return $time;
@@ -103,7 +83,7 @@ class kolab_date_recurrence
             $next_end = clone $next_start;
             $next_end->add($this->duration);
 
-            $next = $this->object;
+            $next = $this->object->to_array();
             $next['recurrence_id'] = $next_start->format('Y-m-d');
             $next['start'] = $next_start;
             $next['end'] = $next_end;
@@ -123,49 +103,12 @@ class kolab_date_recurrence
      */
     public function end($limit = 'now +1 year')
     {
-        if ($this->object['recurrence']['UNTIL'])
-            return $this->object['recurrence']['UNTIL']->format('U');
-
-        $limit_time = strtotime($limit);
-        while ($next_start = $this->next_start(true)) {
-            if ($next_start > $limit_time)
-                break;
-        }
-
-        if ($this->next) {
-            $next_end = $this->next->toDateTime();
-            $next_end->add($this->duration);
-            return $next_end->format('U');
+        $limit_dt = new DateTime($limit);
+        $cstart = kolab_format::get_datetime($this->start);
+        if ($this->engine && ($cend = $this->engine->getOccurenceEndDate($cstart)) && ($end_dt = kolab_format::php_datetime(new cDateTime($cend))) && $end_dt < $limit_dt) {
+            return $end_dt->format('U');
         }
 
         return false;
     }
-
-    /**
-     * Convert the internal structured data into a vcalendar RRULE 2.0 string
-     */
-    private function to_rrule($recurrence)
-    {
-      if (is_string($recurrence))
-          return $recurrence;
-
-        $rrule = '';
-        foreach ((array)$recurrence as $k => $val) {
-            $k = strtoupper($k);
-            switch ($k) {
-            case 'UNTIL':
-                $val = $val->format('Ymd\THis');
-                break;
-            case 'EXDATE':
-                foreach ((array)$val as $i => $ex)
-                    $val[$i] = $ex->format('Ymd\THis');
-                $val = join(',', (array)$val);
-                break;
-            }
-            $rrule .= $k . '=' . $val . ';';
-        }
-
-      return $rrule;
-    }
-
 }
diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php
index 33ed5af..4e790f2 100644
--- a/plugins/libkolab/lib/kolab_format_event.php
+++ b/plugins/libkolab/lib/kolab_format_event.php
@@ -51,6 +51,16 @@ class kolab_format_event extends kolab_format_xcal
         $this->xmldata = $xmldata;
     }
 
+	/**
+	 * Clones into an instance of libcalendaring's extended EventCal class
+	 *
+	 * @return mixed EventCal object or false on failure
+	 */
+	public function to_libcal()
+	{
+		return class_exists('kolabcalendaring') ? new EventCal($this->obj) : false;
+	}
+
     /**
      * Set event properties to the kolabformat object
      *
diff --git a/plugins/libkolab/lib/kolab_storage_cache.php b/plugins/libkolab/lib/kolab_storage_cache.php
index c3e88da..866a795 100644
--- a/plugins/libkolab/lib/kolab_storage_cache.php
+++ b/plugins/libkolab/lib/kolab_storage_cache.php
@@ -517,8 +517,8 @@ class kolab_storage_cache
             $sql_data['dtend']   = date('Y-m-d H:i:s', is_object($object['end'])   ? $object['end']->format('U')   : $object['end']);
 
             // extend date range for recurring events
-            if ($object['recurrence']) {
-                $recurrence = new kolab_date_recurrence($object);
+            if ($object['recurrence'] && $object['_formatobj']) {
+                $recurrence = new kolab_date_recurrence($object['_formatobj']);
                 $sql_data['dtend'] = date('Y-m-d 23:59:59', $recurrence->end() ?: strtotime('now +1 year'));
             }
         }





More information about the commits mailing list