7 commits - lib/ext lib/kolab_sync_backend_device.php lib/kolab_sync_backend.php lib/kolab_sync_data_calendar.php lib/kolab_sync_data_contacts.php lib/kolab_sync_data_email.php lib/kolab_sync_data.php lib/kolab_sync_data_tasks.php lib/kolab_sync.php

Aleksander Machniak machniak at kolabsys.com
Mon Sep 3 12:44:10 CEST 2012


 lib/ext/Syncroton/Command/GetAttachment.php |    2 
 lib/ext/Syncroton/Command/Wbxml.php         |   23 +++--
 lib/ext/Syncroton/Model/AEntry.php          |   28 +++++-
 lib/ext/Syncroton/Model/Email.php           |   16 +--
 lib/ext/Syncroton/Model/Event.php           |   45 ++++++++++
 lib/ext/Syncroton/Model/EventException.php  |    2 
 lib/ext/Syncroton/Model/StoreRequest.php    |   18 +++-
 lib/ext/Syncroton/Server.php                |   10 --
 lib/ext/Syncroton/Wbxml/Abstract.php        |   29 ++++++
 lib/ext/Syncroton/Wbxml/Encoder.php         |   21 +++-
 lib/kolab_sync.php                          |   15 +++
 lib/kolab_sync_backend.php                  |   44 ++++++++--
 lib/kolab_sync_backend_device.php           |    5 -
 lib/kolab_sync_data.php                     |    9 --
 lib/kolab_sync_data_calendar.php            |    2 
 lib/kolab_sync_data_contacts.php            |    2 
 lib/kolab_sync_data_email.php               |  121 +++++++++++++++++++++-------
 lib/kolab_sync_data_tasks.php               |    2 
 18 files changed, 305 insertions(+), 89 deletions(-)

New commits:
commit 43cd411c0028193c704ca08c75c4bd873f055797
Author: Aleksander Machniak <alec at alec.pl>
Date:   Mon Sep 3 12:43:37 2012 +0200

    Fixed email folders handling, fixed some minor issues

diff --git a/lib/kolab_sync_data_calendar.php b/lib/kolab_sync_data_calendar.php
index 1c756d9..d441fde 100644
--- a/lib/kolab_sync_data_calendar.php
+++ b/lib/kolab_sync_data_calendar.php
@@ -227,7 +227,7 @@ class kolab_sync_data_calendar extends kolab_sync_data
                 break;
 
             case 'description':
-                $value = $this->setBody($value, Syncroton_Model_EmailBody::TYPE_PLAINTEXT);
+                $value = $this->setBody($value);
                 break;
             }
 
diff --git a/lib/kolab_sync_data_contacts.php b/lib/kolab_sync_data_contacts.php
index 5a71da4..7a60c0e 100644
--- a/lib/kolab_sync_data_contacts.php
+++ b/lib/kolab_sync_data_contacts.php
@@ -156,7 +156,7 @@ class kolab_sync_data_contacts extends kolab_sync_data
                 break;
 
             case 'notes':
-                $value = $this->setBody($value, Syncroton_Model_EmailBody::TYPE_PLAINTEXT);
+                $value = $this->setBody($value);
                 break;
             }
 
diff --git a/lib/kolab_sync_data_email.php b/lib/kolab_sync_data_email.php
index 3e97dae..22207a0 100644
--- a/lib/kolab_sync_data_email.php
+++ b/lib/kolab_sync_data_email.php
@@ -51,6 +51,19 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
     );
 
     /**
+     * Special folder type/name map
+     *
+     * @var array
+     */
+    protected $folder_types = array(
+        2  => 'Inbox',
+        3  => 'Drafts',
+        4  => 'Deleted Items',
+        5  => 'Sent Items',
+        6  => 'Outbox',
+    );
+
+    /**
      * Kolab object type
      *
      * @var string
@@ -388,21 +401,72 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
     }
 
     /**
-      * Returns default folder for current class type.
-      */
-    protected function getDefaultFolder()
+     * Return list of supported folders for this backend
+     *
+     * @return array
+     */
+    public function getAllFolders()
     {
-        // There's always INBOX
-        $real_id   = $this->backend->folder_id('INBOX', 'mail.inbox');
-        $folder_id = $this->defaultRootFolder;
+        $list = $this->backend->folders_list($this->device->deviceid, $this->modelName);
+
+        // device doesn't support multiple folders
+        if (!in_array(strtolower($this->device->devicetype), array('iphone', 'ipad', 'thundertine', 'windowsphone'))) {
+            // We'll return max. one folder of supported type
+            $result = array();
+            $types  = $this->folder_types;
+
+            foreach ($list as $idx => $folder) {
+                if (isset($types[$folder['type']])) {
+                    $folder_id = $types[$folder['type']];
+                    $result[$folder_id] = array(
+                        'displayName' => $folder_id,
+                        'serverId'    => $folder_id,
+                        'parentId'    => 0,
+                        'type'        => $folder['type'],
+                    );
+                    unset($types[$folder['type']]);
+                }
+            }
 
-        return array(
-            'displayName' => 'Inbox',
-            'realid'      => $real_id,
-            'serverId'    => $folder_id,
-            'parentId'    => 0,
-            'type'        => $this->defaultFolderType,
-        );
+            $list = $result;
+        }
+
+        foreach ($list as $idx => $folder) {
+            $list[$idx] = new Syncroton_Model_Folder($folder);
+        }
+
+        return $list;
+    }
+
+    /**
+     * Return list of folders for specified folder ID
+     *
+     * @return array Folder identifiers list
+     */
+    protected function extractFolders($folder_id)
+    {
+        $list   = $this->backend->folders_list($this->device->deviceid, $this->modelName);
+        $result = array();
+
+        // device supports multiple folders?
+        if (in_array(strtolower($this->device->devicetype), array('iphone', 'ipad', 'thundertine', 'windowsphone'))) {
+            if ($list[$folder_id]) {
+                $result[] = $folder_id;
+            }
+        }
+        else if ($type = array_search($folder_id, $this->folder_types)) {
+            foreach ($list as $id => $folder) {
+                if ($folder['type'] == $type || ($folder_id == 'Inbox' && $folder['type'] == 12)) {
+                    $result[] = $id;
+                }
+            }
+        }
+
+        if (empty($result)) {
+            throw new Syncroton_Exception_NotFound('Folder not found');
+        }
+
+        return $result;
     }
 
     /**
@@ -418,7 +482,8 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
     public function moveItem($srcFolderId, $serverId, $dstFolderId)
     {
         $msg     = $this->parseMessageId($serverId);
-        $dstname = $this->backend->folder_id2name($dstFolderId, $this->device->deviceid);
+        $dest    = $this->extractFolders($dstFolderId);
+        $dstname = $this->backend->folder_id2name(array_shift($dest), $this->device->deviceid);
 
         if (empty($msg)) {
             throw new Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_SOURCE);
@@ -652,14 +717,7 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
      */
     protected function searchEntries($folderid, $filter = array(), $result_type = self::RESULT_UID)
     {
-        if ($folderid == $this->defaultRootFolder) {
-            $folders = $this->backend->folders_list($this->device->deviceid, $this->modelName);
-            $folders = array_keys($folders);
-        }
-        else {
-            $folders = array($folderid);
-        }
-
+        $folders    = $this->extractFolders($folderid);
         $filter_str = 'ALL UNDELETED';
 
         // convert filter into one IMAP search string
@@ -799,7 +857,9 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
             }
 
             if (!empty($query['and']['collections'])) {
-                $folders[] = $query['and']['collections'];
+                foreach ($query['and']['collections'] as $collection) {
+                    $folders = array_merge($folders, $this->extractFolders($collection));
+                }
             }
 
             if (!empty($query['and']['greaterThan'])
diff --git a/lib/kolab_sync_data_tasks.php b/lib/kolab_sync_data_tasks.php
index 318dc5b..d94caf0 100644
--- a/lib/kolab_sync_data_tasks.php
+++ b/lib/kolab_sync_data_tasks.php
@@ -188,7 +188,7 @@ class kolab_sync_data_tasks extends kolab_sync_data
                 break;
 
             case 'description':
-                $value = $this->getBody($value);
+                $value = $this->getBody($value, Syncroton_Model_EmailBody::TYPE_PLAINTEXT);
                 // If description isn't specified keep old description
                 if ($value === null) {
                     continue 2;


commit 748290b81482bbd540de25a1f6c2584179ff1ab3
Author: Aleksander Machniak <alec at alec.pl>
Date:   Mon Sep 3 09:25:42 2012 +0200

    Fix searching email by Received date

diff --git a/lib/ext/Syncroton/Model/StoreRequest.php b/lib/ext/Syncroton/Model/StoreRequest.php
index e78f41c..170c3cb 100644
--- a/lib/ext/Syncroton/Model/StoreRequest.php
+++ b/lib/ext/Syncroton/Model/StoreRequest.php
@@ -88,24 +88,34 @@ class Syncroton_Model_StoreRequest
                 if (isset($xmlStore->Query->And->ConversationId)) {
                     $this->_store['query']['and']['conversationId'] = (string) $xmlStore->Query->And->ConversationId;
                 }
+
+                // Protocol specification defines Value as string and DateReceived as datetime, but
+                // PocketPC device I tested sends XML as follows:
+                // <GreaterThan>
+                //    <DateReceived>
+                //    <Value>2012-08-02T16:54:11.000Z</Value>
+                // </GreaterThan>
+
                 if (isset($xmlStore->Query->And->GreaterThan)) {
                     if (isset($xmlStore->Query->And->GreaterThan->Value)) {
-                        $this->_store['query']['and']['greaterThan']['value'] = (string) $xmlStore->Query->And->GreaterThan->Value;
+                        $value = (string) $xmlStore->Query->And->GreaterThan->Value;
+                        $this->_store['query']['and']['greaterThan']['value'] = new DateTime($value, new DateTimeZone('UTC'));
                     }
 
                     $email = $xmlStore->Query->And->GreaterThan->children('uri:Email');
                     if (isset($email->DateReceived)) {
-                        $this->_store['query']['and']['greaterThan']['dateReceived'] = new DateTime((string) $email->DateReceived, new DateTimeZone('UTC'));
+                        $this->_store['query']['and']['greaterThan']['dateReceived'] = true;
                     }
                 }
                 if (isset($xmlStore->Query->And->LessThan)) {
                     if (isset($xmlStore->Query->And->LessThan->Value)) {
-                        $this->_store['query']['and']['lessThan']['value'] = (string) $xmlStore->Query->And->LessThan->Value;
+                        $value = (string) $xmlStore->Query->And->LessThan->Value;
+                        $this->_store['query']['and']['lessThan']['value'] = new DateTime($value, new DateTimeZone('UTC'));
                     }
 
                     $email = $xmlStore->Query->And->LessThan->children('uri:Email');
                     if (isset($email->DateReceived)) {
-                        $this->_store['query']['and']['leasThan']['dateReceived'] = new DateTime((string) $email->DateReceived, new DateTimeZone('UTC'));
+                        $this->_store['query']['and']['leasThan']['dateReceived'] = true;
                     }
                 }
 
diff --git a/lib/kolab_sync_data_email.php b/lib/kolab_sync_data_email.php
index f36c42d..3e97dae 100644
--- a/lib/kolab_sync_data_email.php
+++ b/lib/kolab_sync_data_email.php
@@ -806,21 +806,20 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
                 && !empty($query['and']['greaterThan']['dateReceived'])
                 && !empty($query['and']['greaterThan']['value'])
             ) {
-                // @TODO: date format ?
-                $search_str .= ' SINCE ' . trim($query['and']['greaterThan']['value']);
+                $search_str .= ' SINCE ' . $query['and']['greaterThan']['value']->format('d-M-Y');
             }
 
             if (!empty($query['and']['lessThan'])
                 && !empty($query['and']['lessThan']['dateReceived'])
                 && !empty($query['and']['lessThan']['value'])
             ) {
-                // @TODO: date format ?
-                $search_str .= ' BEFORE ' . trim($query['and']['lessThan']['value']);
+                $search_str .= ' BEFORE ' . $query['and']['lessThan']['value']->format('d-M-Y');
             }
         }
 
         if ($search !== null) {
             // @FIXME: should we use TEXT/BODY search?
+            // ActiveSync protocol specification says "indexed fields"
             $search_keys = array('SUBJECT', 'TO', 'FROM', 'CC');
             $search_str .= str_repeat(' OR', count($search_keys)-1);
             foreach ($search_keys as $key) {


commit a461edbb8f456f3252cf1f9b416f23fb1f8ea7cd
Author: Aleksander Machniak <alec at alec.pl>
Date:   Sun Sep 2 19:53:01 2012 +0200

    Syncroton update (with byteArray support)

diff --git a/lib/ext/Syncroton/Command/GetAttachment.php b/lib/ext/Syncroton/Command/GetAttachment.php
index 5bf745d..28c0deb 100644
--- a/lib/ext/Syncroton/Command/GetAttachment.php
+++ b/lib/ext/Syncroton/Command/GetAttachment.php
@@ -23,6 +23,8 @@ class Syncroton_Command_GetAttachment extends Syncroton_Command_Wbxml
      */
     protected $_attachmentName;
     
+    protected $_skipValidatePolicyKey = true;
+    
     /**
      * process the XML file and add, change, delete or fetches data 
      *
diff --git a/lib/ext/Syncroton/Command/Wbxml.php b/lib/ext/Syncroton/Command/Wbxml.php
index 537900a..f87a71e 100644
--- a/lib/ext/Syncroton/Command/Wbxml.php
+++ b/lib/ext/Syncroton/Command/Wbxml.php
@@ -145,16 +145,19 @@ abstract class Syncroton_Command_Wbxml implements Syncroton_Command_ICommand
         if ($this->_logger instanceof Zend_Log) 
             $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " sync timestamp: " . $this->_syncTimeStamp->format('Y-m-d H:i:s'));
         
-        // Creates an instance of the DOMImplementation class
-        $imp = new DOMImplementation();
-        
-        // Creates a DOMDocumentType instance
-        $dtd = $imp->createDocumentType('AirSync', "-//AIRSYNC//DTD AirSync//EN", "http://www.microsoft.com/");
-
-        // Creates a DOMDocument instance
-        $this->_outputDom = $imp->createDocument($this->_defaultNameSpace, $this->_documentElement, $dtd);
-        $this->_outputDom->formatOutput = false;
-        $this->_outputDom->encoding     = 'utf-8';
+        if (isset($this->_defaultNameSpace) && isset($this->_documentElement)) {
+            // Creates an instance of the DOMImplementation class
+            $imp = new DOMImplementation();
+            
+            // Creates a DOMDocumentType instance
+            $dtd = $imp->createDocumentType('AirSync', "-//AIRSYNC//DTD AirSync//EN", "http://www.microsoft.com/");
+            
+            // Creates a DOMDocument instance
+            $this->_outputDom = $imp->createDocument($this->_defaultNameSpace, $this->_documentElement, $dtd);
+            $this->_outputDom->documentElement->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:Syncroton', 'uri:Syncroton');
+            $this->_outputDom->formatOutput = false;
+            $this->_outputDom->encoding     = 'utf-8';
+        }
         
         if ($this->_skipValidatePolicyKey != true) {
             if (!empty($this->_device->policyId)) {
diff --git a/lib/ext/Syncroton/Model/AEntry.php b/lib/ext/Syncroton/Model/AEntry.php
index 79cfb9a..21e2e3c 100644
--- a/lib/ext/Syncroton/Model/AEntry.php
+++ b/lib/ext/Syncroton/Model/AEntry.php
@@ -57,11 +57,6 @@ abstract class Syncroton_Model_AEntry implements Syncroton_Model_IEntry, Iterato
             
             $nameSpace = 'uri:' . $nameSpace;
             
-            // strip off any non printable control characters
-            if (!ctype_print($value)) {
-                #$value = $this->removeControlChars($value);
-            }
-            
             if (isset($elementProperties['childElement'])) {
                 $element = $_domParrent->ownerDocument->createElementNS($nameSpace, ucfirst($elementName));
                 foreach($value as $subValue) {
@@ -172,7 +167,7 @@ abstract class Syncroton_Model_AEntry implements Syncroton_Model_IEntry, Iterato
         }
     }
     
-    protected function _appendXMLElement($element, $elementProperties, $value)
+    protected function _appendXMLElement(DOMElement $element, $elementProperties, $value)
     {
         if ($value instanceof Syncroton_Model_IEntry) {
             $value->appendXML($element);
@@ -189,9 +184,30 @@ abstract class Syncroton_Model_AEntry implements Syncroton_Model_IEntry, Iterato
                 }
             }
             
+            // strip off any non printable control characters
+            if (!ctype_print($value)) {
+                $value = $this->_removeControlChars($value);
+            }
+            
+            if ($elementProperties['type'] == 'byteArray') {
+                $element->setAttributeNS('uri:Syncroton', 'Syncroton:encoding', 'oqaque');
+            }
+            
             $element->appendChild($element->ownerDocument->createTextNode($value));
         }
     }
+    
+    /**
+     * removed control chars from string which are not allowd in XML values
+     *
+     * @param  string|array $_dirty
+     * @return string
+     */
+    protected function _removeControlChars($dirty)
+    {
+        return preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F]/', null, $dirty);
+    }
+    
     
     /**
      * 
diff --git a/lib/ext/Syncroton/Model/Email.php b/lib/ext/Syncroton/Model/Email.php
index 2ef7da2..1e74a70 100644
--- a/lib/ext/Syncroton/Model/Email.php
+++ b/lib/ext/Syncroton/Model/Email.php
@@ -9,16 +9,16 @@
  */
 
 /**
- * class to handle ActiveSync event
+ * class to handle ActiveSync email
  *
  * @package     Model
- * @property    array    Attachments
- * @property    string   ContentType
- * @property    Syncroton_Model_EmailFlag  Flag
- * @property    Syncroton_Model_EmailBody  Body
- * @property    array    Cc
- * @property    array    To
- * @property    int      Read
+ * @property    array    attachments
+ * @property    string   contentType
+ * @property    array    flag
+ * @property    Syncroton_Model_EmailBody    body
+ * @property    array    cc
+ * @property    array    to
+ * @property    int      read
  */
 class Syncroton_Model_Email extends Syncroton_Model_AEntry
 {
diff --git a/lib/ext/Syncroton/Model/Event.php b/lib/ext/Syncroton/Model/Event.php
index 02307e0..cf0de60 100644
--- a/lib/ext/Syncroton/Model/Event.php
+++ b/lib/ext/Syncroton/Model/Event.php
@@ -64,4 +64,49 @@ class Syncroton_Model_Event extends Syncroton_Model_AEntry
             'uID'                       => array('type' => 'string'),
         )
     );
+    
+    public function setFromArray(array $properties)
+    {
+        parent::setFromArray($properties);
+        
+        $this->_copyFieldsFromParent();
+    }
+    
+    /**
+     * set properties from SimpleXMLElement object
+     *
+     * @param SimpleXMLElement $xmlCollection
+     * @throws InvalidArgumentException
+     */
+    public function setFromSimpleXMLElement(SimpleXMLElement $properties)
+    {
+        parent::setFromSimpleXMLElement($properties);
+        
+        $this->_copyFieldsFromParent();
+    }
+    
+    /**
+     * copy some fileds of the main event to the exception if they are missing
+     * these fields can be left out, if they have the same value in the main event
+     * and the exception 
+     */
+    protected function _copyFieldsFromParent()
+    {
+        if (isset($this->_elements['exceptions']) && is_array($this->_elements['exceptions'])) {
+            foreach ($this->_elements['exceptions'] as $exception) {
+                // no need to update deleted exceptions
+                if ($exception->deleted == 1) {
+                    continue;
+                }
+        
+                $parentFields = array('allDayEvent', 'attendees', 'busyStatus', 'meetingStatus', 'sensitivity', 'subject');
+        
+                foreach ($parentFields as $field) {
+                    if (!isset($exception->$field) && isset($this->_elements[$field])) {
+                        $exception->$field = $this->_elements[$field];
+                    }
+                }
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/lib/ext/Syncroton/Model/EventException.php b/lib/ext/Syncroton/Model/EventException.php
index 400c397..7d7224b 100644
--- a/lib/ext/Syncroton/Model/EventException.php
+++ b/lib/ext/Syncroton/Model/EventException.php
@@ -20,7 +20,7 @@
  * @property    int     windowSize
  */
 
-class Syncroton_Model_EventException extends Syncroton_Model_Event
+class Syncroton_Model_EventException extends Syncroton_Model_AEntry
 {    
     protected $_xmlBaseElement = 'Exception';
     
diff --git a/lib/ext/Syncroton/Server.php b/lib/ext/Syncroton/Server.php
index a1f4f55..2a9555a 100644
--- a/lib/ext/Syncroton/Server.php
+++ b/lib/ext/Syncroton/Server.php
@@ -109,9 +109,7 @@ class Syncroton_Server
                 $requestBody = $decoder->decode();
                 if ($this->_logger instanceof Zend_Log) {
                     $requestBody->formatOutput = true;
-                    // remove not important XML information
-                    $request_str = preg_replace('|(</*)([a-z0-9]+:)?([a-z0-9]+)[^>]*>|i', '\\1\\3>', $requestBody->saveXML());
-                    $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " xml request:\n" . trim($request_str));
+                    $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " xml request:\n" . $requestBody->saveXML());
                 }
             } catch(Syncroton_Wbxml_Exception_UnexpectedEndOfFile $e) {
                 $requestBody = NULL;
@@ -158,7 +156,7 @@ class Syncroton_Server
             if ($this->_logger instanceof Zend_Log)
                 $this->_logger->crit(__METHOD__ . '::' . __LINE__ . " exception message: " . $e->getMessage());
             if ($this->_logger instanceof Zend_Log)
-                $this->_logger->info(__METHOD__ . '::' . __LINE__ . " " . $e->getTraceAsString());
+                $this->_logger->crit(__METHOD__ . '::' . __LINE__ . " " . $e->getTraceAsString());
             
             header("HTTP/1.1 500 Internal server error");
             
@@ -168,9 +166,7 @@ class Syncroton_Server
         if ($response instanceof DOMDocument) {
             if ($this->_logger instanceof Zend_Log) {
                 $response->formatOutput = true;
-                // remove not important XML information
-                $response_str = preg_replace('|(</*)([a-z0-9]+:)?([a-z0-9]+)[^>]*>|i', '\\1\\3>', $response->saveXML());
-                $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " xml response:\n" . trim($response_str));
+                $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " xml response:\n" . $response->saveXML());
             }
         
             $outputStream = fopen("php://temp", 'r+');
diff --git a/lib/ext/Syncroton/Wbxml/Abstract.php b/lib/ext/Syncroton/Wbxml/Abstract.php
index e1d0c36..6c86806 100644
--- a/lib/ext/Syncroton/Wbxml/Abstract.php
+++ b/lib/ext/Syncroton/Wbxml/Abstract.php
@@ -202,8 +202,37 @@ abstract class Syncroton_Wbxml_Abstract
         fwrite($this->_stream, $_string);
     }
     
+    /**
+     * write opaque string to stream
+     * 
+     * @param string|resource $_string
+     * @throws Syncroton_Wbxml_Exception
+     */
+    protected function _writeOpaqueString($_string)
+    {
+        if (is_resource($_string)) {
+            $stream = $_string;
+        } else {
+            $stream = fopen("php://temp", 'r+');
+            fwrite($stream, $_string);
+        }
+        $length = ftell($stream);
+        rewind($stream);
+        
+        $this->_writeByte(Syncroton_Wbxml_Abstract::OPAQUE);
+        $this->_writeMultibyteUInt($length);
+        $writenBytes = stream_copy_to_stream($stream, $this->_stream);
+        
+        if($writenBytes !== $length) {
+            throw new Syncroton_Wbxml_Exception('blow');
+        }
+        
+        fclose($stream);
+    }
+    
     protected function _writeTerminatedString($_string)
     {
+        $this->_writeByte(Syncroton_Wbxml_Abstract::STR_I);
         fwrite($this->_stream, $_string);
         fwrite($this->_stream, chr(0));
     }
diff --git a/lib/ext/Syncroton/Wbxml/Encoder.php b/lib/ext/Syncroton/Wbxml/Encoder.php
index 93ee8f7..667cfec 100644
--- a/lib/ext/Syncroton/Wbxml/Encoder.php
+++ b/lib/ext/Syncroton/Wbxml/Encoder.php
@@ -159,8 +159,7 @@ class Syncroton_Wbxml_Encoder extends Syncroton_Wbxml_Abstract
         xml_set_character_data_handler($parser, '_handleCharacters');
         xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
         
-        $xmlString = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F]/', null,  $_dom->saveXML());
-        if (!xml_parse($parser, $xmlString)) {
+        if (!xml_parse($parser, $_dom->saveXML())) {
             #file_put_contents(tempnam(sys_get_temp_dir(), "xmlerrors"), $_dom->saveXML());
             throw new Syncroton_Wbxml_Exception(sprintf('XML error: %s at line %d',
                 xml_error_string(xml_get_error_code($parser)),
@@ -181,10 +180,11 @@ class Syncroton_Wbxml_Encoder extends Syncroton_Wbxml_Abstract
     protected function _handleStartTag($_parser, $_tag, $_attributes)
     {
         $this->_level++;
+        $this->_currentTagData = null;
         
         // write data for previous tag happens whith <tag1><tag2>
         if($this->_currentTag !== NULL) {
-            $this->_writeTag($this->_currentTag, $this->_attributes, true, $this->_currentTagData);
+            $this->_writeTag($this->_currentTag, $this->_attributes, true);
         }
 
         list($nameSpace, $this->_currentTag) = explode(';', $_tag);
@@ -194,6 +194,7 @@ class Syncroton_Wbxml_Encoder extends Syncroton_Wbxml_Abstract
         }
 
         $this->_attributes = $_attributes;
+        
     }
     
     /**
@@ -291,6 +292,13 @@ class Syncroton_Wbxml_Encoder extends Syncroton_Wbxml_Abstract
         // handle the tag
         $identity = $this->_codePage->getIdentity($_tag);
         
+        if (is_array($_attributes) && isset($_attributes['Syncroton:encoding'])) {
+            $encoding = 'opaque';
+            unset($_attributes['Syncroton:encoding']);
+        } else {
+            $encoding = 'termstring';
+        }
+        
         if(!empty($_attributes)) {
             $identity |= 0x80;
         }
@@ -303,8 +311,11 @@ class Syncroton_Wbxml_Encoder extends Syncroton_Wbxml_Abstract
         
         // handle the data
         if($_data !== NULL) {
-            $this->_writeByte(Syncroton_Wbxml_Abstract::STR_I);
-            $this->_writeTerminatedString($_data);
+            if ($encoding == 'opaque') {
+                $this->_writeOpaqueString($_data);
+            } else {
+                $this->_writeTerminatedString($_data);
+            }
         }
         
         $this->_currentTagData = NULL;


commit 4f4538299bee9b20402626b1f56fadd48abc771a
Author: Aleksander Machniak <alec at alec.pl>
Date:   Sun Sep 2 14:27:06 2012 +0200

    More fixes

diff --git a/lib/kolab_sync_data.php b/lib/kolab_sync_data.php
index 06a95d9..1ef5efb 100644
--- a/lib/kolab_sync_data.php
+++ b/lib/kolab_sync_data.php
@@ -190,8 +190,7 @@ abstract class kolab_sync_data implements Syncroton_Data_IData
             // get the folders the user has access to
             $list = $this->backend->folders_list($this->device->deviceid, $this->modelName);
         }
-
-        if ($default = $this->getDefaultFolder()) {
+        else if ($default = $this->getDefaultFolder()) {
             $list = array($default['serverId'] => $default);
         }
 
@@ -337,17 +336,17 @@ abstract class kolab_sync_data implements Syncroton_Data_IData
         $item = $this->getObject($srcFolderId, $serverId, $folder);
 
         if (!$item || !$folder) {
-            throw Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_SOURCE);
+            throw new Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_SOURCE);
         }
 
         $dstname = $this->backend->folder_id2name($dstFolderId, $this->device->deviceid);
 
         if ($dstname === null) {
-            throw Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_DESTINATION);
+            throw new Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_DESTINATION);
         }
 
         if (!$folder->move($serverId, $dstname)) {
-            throw Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_SOURCE);
+            throw new Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_SOURCE);
         }
 
         return $item['uid'];


commit 696761b811a8fb94771fd87492d486f2a13a3e56
Author: Aleksander Machniak <alec at alec.pl>
Date:   Sun Sep 2 14:06:32 2012 +0200

    Fix date format in IMAP SEARCH

diff --git a/lib/kolab_sync_data_email.php b/lib/kolab_sync_data_email.php
index 9c29510..f36c42d 100644
--- a/lib/kolab_sync_data_email.php
+++ b/lib/kolab_sync_data_email.php
@@ -381,7 +381,7 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
             $dt = new DateTime('now', new DateTimeZone('UTC'));
             $dt->modify($mod);
             // RFC3501: IMAP SEARCH
-            $filter[] = 'SINCE ' . $dt->format('d-m-Y');
+            $filter[] = 'SINCE ' . $dt->format('d-M-Y');
         }
 
         return $filter;
@@ -806,6 +806,7 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
                 && !empty($query['and']['greaterThan']['dateReceived'])
                 && !empty($query['and']['greaterThan']['value'])
             ) {
+                // @TODO: date format ?
                 $search_str .= ' SINCE ' . trim($query['and']['greaterThan']['value']);
             }
 
@@ -813,6 +814,7 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
                 && !empty($query['and']['lessThan']['dateReceived'])
                 && !empty($query['and']['lessThan']['value'])
             ) {
+                // @TODO: date format ?
                 $search_str .= ' BEFORE ' . trim($query['and']['lessThan']['value']);
             }
         }


commit cfbfbde475e8415f12a3d095c36213a726bcaec3
Author: Aleksander Machniak <alec at alec.pl>
Date:   Sun Sep 2 13:11:22 2012 +0200

    Improved message sending: Use default idenity if From: is not set,
    Generate Message-ID if not set, other fixes and improvements

diff --git a/lib/kolab_sync.php b/lib/kolab_sync.php
index 0969e8e..5a6d994 100644
--- a/lib/kolab_sync.php
+++ b/lib/kolab_sync.php
@@ -28,11 +28,24 @@
  */
 class kolab_sync extends rcube
 {
+    /**
+     * Application name
+     *
+     * @var string
+     */
     public $app_name = 'ActiveSync for Kolab'; // no double quotes inside
 
+    /**
+     * Current user
+     *
+     * @var rcube_user
+     */
+    public $user;
+
     private $data = array();
 
     const CHARSET = 'UTF-8';
+    const VERSION = 1.0;
 
 
     /**
diff --git a/lib/kolab_sync_backend.php b/lib/kolab_sync_backend.php
index 3cbd548..29235a1 100644
--- a/lib/kolab_sync_backend.php
+++ b/lib/kolab_sync_backend.php
@@ -734,11 +734,21 @@ class kolab_sync_backend
 
         $mailto = $headers['To'];
 
-        $headers['User-Agent'] .= $rcube->app_name;
+        $headers['User-Agent'] .= sprintf('%s v.%.1f', $rcube->app_name, kolab_sync::VERSION);
         if ($agent = $rcube->config->get('useragent')) {
             $headers['User-Agent'] .= '/' . $agent;
         }
 
+        if (empty($headers['From'])) {
+            $headers['From'] = $this->get_identity();
+        }
+        if (empty($headers['Message-ID'])) {
+            $headers['Message-ID'] = $this->gen_message_id();
+        }
+
+        // remove empty headers
+        $headers = array_filter($headers);
+
         // send thru SMTP server using custom SMTP library
         if ($rcube->config->get('smtp_server')) {
             $smtp_headers = $headers;
@@ -840,7 +850,14 @@ class kolab_sync_backend
         return $sent;
     }
 
-
+    /**
+     * MIME message parser
+     *
+     * @param string|resource $message     MIME message source
+     * @param bool            $decode_body Enables body decoding
+     *
+     * @return array Message headers array and message body
+     */
     public function parse_mime($message, $decode_body = false)
     {
         if (is_resource($message)) {
@@ -858,7 +875,7 @@ class kolab_sync_backend
 
         foreach ($headers as $line) {
             if (ord($line[0]) <= 32) {
-                $lines[$ln] .= (empty($lines[$ln]) ? '' : "\n") . trim($line);
+                $lines[$ln] .= (empty($lines[$ln]) ? '' : "\n") . $line;
             }
             else {
                 $lines[++$ln] = trim($line);
@@ -872,6 +889,7 @@ class kolab_sync_backend
             'to'      => 'To',
             'cc'      => 'Cc',
             'bcc'     => 'Bcc',
+            'message-id'   => 'Message-ID',
             'content-type' => 'Content-Type',
             'content-transfer-encoding' => 'Content-Transfer-Encoding',
         );
@@ -937,15 +955,27 @@ class kolab_sync_backend
     }
 
     /**
+     * Returns email address string from default identity of the current user
+     */
+    protected function get_identity()
+    {
+        $user = kolab_sync::get_instance()->user;
+
+        if ($identity = $user->get_identity()) {
+            return format_email_recipient(format_email($identity['email']), $identity['name']);
+        }
+    }
+
+    /**
      * Unique Message-ID generator.
      *
      * @return string Message-ID
      */
-/*
-    public function gen_message_id()
+    protected function gen_message_id()
     {
+        $user        = kolab_sync::get_instance()->user;
         $local_part  = md5(uniqid('rcmail'.mt_rand(),true));
-//        $domain_part = $this->user->get_username('domain');
+        $domain_part = $user->get_username('domain');
 
         // Try to find FQDN, some spamfilters doesn't like 'localhost' (#1486924)
         if (!preg_match('/\.[a-z]+$/i', $domain_part)) {
@@ -959,5 +989,5 @@ class kolab_sync_backend
 
         return sprintf('<%s@%s>', $local_part, $domain_part);
     }
-*/
+
 }


commit f6fe8e0894240b9b0ad1fe95354312d960a7a04d
Author: Aleksander Machniak <alec at alec.pl>
Date:   Sun Sep 2 12:37:49 2012 +0200

    Fixed some errors

diff --git a/lib/kolab_sync.php b/lib/kolab_sync.php
index 2fb9b61..0969e8e 100644
--- a/lib/kolab_sync.php
+++ b/lib/kolab_sync.php
@@ -310,7 +310,7 @@ class kolab_sync extends rcube
             if (function_exists('memory_get_peak_usage'))
                 $mem .= '/' . sprintf('%.1f', memory_get_peak_usage() / 1048576);
 
-            $log = $_SERVER['REQUEST_URI'] . ($mem ? " [$mem]" : '');
+            $log = $_SERVER['QUERY_STRING'] . ($mem ? " [$mem]" : '');
             if (defined('RCMAIL_START'))
                 self::print_timer(RCMAIL_START, $log);
             else
diff --git a/lib/kolab_sync_backend_device.php b/lib/kolab_sync_backend_device.php
index fec5193..91adb54 100644
--- a/lib/kolab_sync_backend_device.php
+++ b/lib/kolab_sync_backend_device.php
@@ -61,8 +61,9 @@ class kolab_sync_backend_device extends kolab_sync_backend_common implements Syn
 
         // Create device entry in kolab backend
         $created = $this->backend->device_create(array(
-            'ID'         => $device->id,
-            'TYPE'       => $device->devicetype,
+            'ID'    => $device->id,
+            'TYPE'  => $device->devicedype,
+            'ALIAS' => $device->friendlyname,
         ), $device->deviceid);
 
         if (!$created) {
diff --git a/lib/kolab_sync_data_email.php b/lib/kolab_sync_data_email.php
index 2f0aaaa..9c29510 100644
--- a/lib/kolab_sync_data_email.php
+++ b/lib/kolab_sync_data_email.php
@@ -421,15 +421,15 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
         $dstname = $this->backend->folder_id2name($dstFolderId, $this->device->deviceid);
 
         if (empty($msg)) {
-            throw Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_SOURCE);
+            throw new Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_SOURCE);
         }
 
         if ($dstname === null) {
-            throw Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_DESTINATION);
+            throw new Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_DESTINATION);
         }
 
         if (!$this->storage->move_message($msg['uid'], $dstname, $msg['foldername'])) {
-            throw Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_SOURCE);
+            throw new Syncroton_Exception_Status_MoveItems(Syncroton_Exception_Status_MoveItems::INVALID_SOURCE);
         }
 
         // Use COPYUID feature (RFC2359) to get the new UID of the copied message
@@ -525,7 +525,7 @@ class kolab_sync_data_email extends kolab_sync_data implements Syncroton_Data_ID
         $sent = $this->backend->send_message($body, $smtp_error);
 
         if (!$sent) {
-            throw Syncroton_Exception_Status(Syncroton_Exception_Status::MAIL_SUBMISSION_FAILED);
+            throw new Syncroton_Exception_Status(Syncroton_Exception_Status::MAIL_SUBMISSION_FAILED);
         }
 
         // Save sent message in Sent folder





More information about the commits mailing list