[sync] SyncML working! Step 2/3

Karsten Fourmont fourmont at gmx.de
Wed Jun 30 10:07:53 PDT 2004


Hi,

here are the patches for the various modules. They are named
<modulename>.patch

Patch framework.patch
---------------------

Contains all patches to framework _except_ to the SyncML Package itself:

DataTreee/sql.php:

1) _load missed a $this->_connect

2) for some strange reasons I sometimes had $this->_connected==true but 
$this->_db->connection was int(0) rather than a database resource 
handle. I did not take a closer look why this might happen but just 
enforced reconnection on !$this->_db->connection


WB_XML/ContentHandler.php:

My P900 was picky about the spacious XML the ContentHandler produces. It 
didn't like stuff like:
<tag> password
</tag>
Note the space after <tag>
So I changed it to add newlines and padding _before_ _opening_ tags 
only. So you get output like
<foo>
   <bar>password</bar></foo>

which seems to be fine with my phone as all tags with actual character 
data in it don't have "wrong" whitespace any more.

Patch nag.patch
---------------

lib/api.php: -wrong order of parameters for listBy in api spec
              -removed notices for incomplete arguments


Patch mnemo.patch
-----------------

lib/api.php: wrong order of parameters for listBy in api spec and 
implementation

Patch kronolith.patch
---------------------

This is a somewhat bigger patch. Basically the issue is as follows: 
there seem to be two packages for dealing with iCalendar Data: the 
ICalendar package and the Horde_Data package. The later can do proper 
charset handling which is needed for SyncML. So I converted the 
Kronolith api to use this rather than the ICalendar package. SyncML 
works without this patch, but you may run into Charset problems.

1) lib/Driver.php: ignore strange recurrence definitions without "="

2) lib/api.php

import and replace now use Horde::Data rather than the iCalendar 
package. This provides charset and other parameter (quoted-printable) 
support.

I also added a "listEvents" api function to the Kronolith-Api. It's just 
a thin wrapper around Kronoliths listEvents function. This is totally 
unrelated to SyncML: I just needed it at some point in time and think it 
is just missing from the api to make it reaully useful.

3) lib/Driver/sql.php: fixed small Typo


OK, that's it. I'm really curious if somebody else can get this thing 
running.

Having full SyncML support in Horde would be really great: overtaking 
Exchange Server and Lotus Notes is fun :-)

Cheers,
   Karsten
-------------- next part --------------
Index: lib/api.php
===================================================================
RCS file: /repository/nag/lib/api.php,v
retrieving revision 1.86
diff -u -r1.86 api.php
--- lib/api.php	17 Jun 2004 15:02:26 -0000	1.86
+++ lib/api.php	30 Jun 2004 16:47:41 -0000
@@ -58,7 +58,7 @@
 );
 
 $_services['listBy'] = array(
-    'args' => array('timestamp', 'action'),
+    'args' => array('action', 'timestamp'),
     'type' => 'stringArray'
 );
 
@@ -248,7 +248,7 @@
  *
  * @return string  The new GUID, or false on failure.
  */
-function _nag_import($content, $contentType, $tasklist)
+function _nag_import($content, $contentType, $tasklist = null)
 {
     require_once dirname(__FILE__) . '/base.php';
     global $prefs;
@@ -297,7 +297,10 @@
          * @TODO Need to check for alarms and set due date, etc.
          */
         $hash = $content->toArray();
-        $taskId = $storage->add($hash['name'], $hash['desc'], 0, $hash['priority']);
+        $taskId = $storage->add(isset($hash['name']) ? $hash['name'] : "",
+				isset($hash['desc']) ? $hash['desc'] : "",
+				0,
+				isset($hash['priority']) ? $hash['priority'] : 3);
         break;
 
     default:
@@ -430,7 +433,12 @@
          * @TODO Need to check for alarms and set due date, etc.
          */
         $hash = $content->toArray();
-        $result = $storage->modify($task['task_id'], $hash['name'], $hash['desc'], 0, $hash['priority']);
+        $result = $storage->modify($task['task_id'],
+				   isset($hash['name']) ? $hash['name'] : "",
+				   isset($hash['desc']) ? $hash['desc'] : "",
+				   0,
+				   isset($hash['priority']) ? $hash['priority'] : 3);
+
         break;
 
     default:
-------------- next part --------------
Index: lib/Driver.php
===================================================================
RCS file: /repository/kronolith/lib/Driver.php,v
retrieving revision 1.75
diff -u -r1.75 Driver.php
--- lib/Driver.php	27 Jun 2004 17:06:25 -0000	1.75
+++ lib/Driver.php	29 Jun 2004 19:03:40 -0000
@@ -611,7 +611,7 @@
 
         // Recurrence.
         $rrule = $vEvent->getAttribute('RRULE');
-        if (!is_array($rrule) && !is_a($rrule, 'PEAR_Error')) {
+        if (!is_array($rrule) && !is_a($rrule, 'PEAR_Error') && strstr($rrule,'=')) {
             // Parse the recurrence rule into keys and values.
             $parts = explode(';', $rrule);
             foreach ($parts as $part) {
Index: lib/api.php
===================================================================
RCS file: /repository/kronolith/lib/api.php,v
retrieving revision 1.115
diff -u -r1.115 api.php
--- lib/api.php	9 Jun 2004 16:05:22 -0000	1.115
+++ lib/api.php	29 Jun 2004 19:03:44 -0000
@@ -66,6 +66,11 @@
     'type' => 'boolean'
 );
 
+$_services['listEvents'] = array(
+    'args' => array('startstamp' => 'int', 'endstamp' => 'int', 'calendar' => 'string', 'showRecurrence' => 'string'),
+    'type' => 'array'
+);
+
 
 function _kronolith_listCalendars($owneronly = false, $permission = null)
 {
@@ -125,70 +130,100 @@
  *                             text/x-vevent
  * @param string $calendar     (optional) What calendar should the event be added to?
  *
- * @return string  The new GUID, or false on failure.
+ * @return string  The new GUID, or a PEAR_Error on failure.
  */
 function _kronolith_import($content, $contentType, $calendar = null)
 {
-    require_once dirname(__FILE__) . '/base.php';
-    global $kronolith;
+  require_once dirname(__FILE__) . '/base.php';
+  require_once 'Horde/Data.php';
+
+  global $kronolith;
+
+  if (is_a($content, 'PEAR_Error')) {
+    return $content;
+  }
 
-    if (!isset($calendar)) {
-        $calendar = Kronolith::getDefaultCalendar(PERMS_EDIT);
+  if (!isset($calendar)) {
+    $calendar = Kronolith::getDefaultCalendar(PERMS_EDIT);
+  }
+  if (!array_key_exists($calendar, Kronolith::listCalendars(false, PERMS_EDIT))) {
+    return PEAR::raiseError(_("Permission Denied"));
+  }
+  $kronolith->open($calendar);
+
+  // legacy code to import a Horde_iCalendar_vevent from the
+  // iCalendar package. We use Horde_Data now instead of the
+  // iCalendar package. If this package gets removed, this
+  // code can go as well.
+
+  if (is_a($content, 'Horde_iCalendar_vevent')) {
+    $event = &$kronolith->getEvent();
+    $event->fromiCalendar($content);
+
+    $guid = $content->getAttribute('UID');
+    if (!is_a($guid, 'PEAR_Error')) {
+      $event->setID($guid);
     }
-    if (!array_key_exists($calendar, Kronolith::listCalendars(false, PERMS_EDIT))) {
-        return PEAR::raiseError(_("Permission Denied"));
+
+    $id = $event->save();
+    return $event->getGUID($id);
+  }
+
+  switch ($contentType) {
+  case 'text/calendar':
+  case 'text/x-icalendar':
+  case 'text/x-vcalendar':
+  case 'text/x-vevent':
+    $data = &Horde_Data::singleton('icalendar');
+    if (is_a($data, 'PEAR_Error')) {
+      return $data;
+    }
+    $r = $data->importData($content);
+    if (is_a($r, 'PEAR_Error')) {
+      return $r;
+    }
+    if ($data->count() == 0) {
+      return PEAR::raiseError(_("No iCalendar data was found."));
+    }
+    if ($data->count() > 1) {
+      return PEAR::raiseError(_("Multiple iCalendar components found; only one vEvent is supported."));
+    }
+    $r = $data->toHash(0);
+    if (is_a($r, 'PEAR_Error')) {
+      return $r;
     }
-    $kronolith->open($calendar);
 
-    switch ($contentType) {
-    case 'text/calendar':
-    case 'text/x-icalendar':
-    case 'text/x-vcalendar':
-    case 'text/x-vevent':
-        if (!is_a($content, 'Horde_iCalendar_vevent')) {
-            require_once 'Horde/iCalendar.php';
-            $iCal = &new Horde_iCalendar();
-            if (!$iCal->parsevCalendar($content)) {
-                return PEAR::raiseError(_("There was an error importing the iCalendar data."));
-            }
-
-            $components = $iCal->getComponents();
-            switch (count($components)) {
-            case 0:
-                return PEAR::raiseError(_("No iCalendar data was found."));
-
-            case 1:
-                $content = $components[0];
-                if (!is_a($content, 'Horde_iCalendar_vevent')) {
-                    return PEAR::raiseError(_("vEvent not found."));
-                }
-                break;
-
-            default:
-                return PEAR::raiseError(_("Multiple iCalendar components found; only one vEvent is supported."));
-            }
-        }
-
-        $event = &$kronolith->getEvent();
-        $event->fromiCalendar($content);
-
-        $guid = $content->getAttribute('UID');
-        if (!is_a($guid, 'PEAR_Error')) {
-            $event->setID($guid);
-        }
+    $event = &$kronolith->getEvent();
 
-        $eventId = $event->save();
-        break;
+    if (!$event) {
+      return PEAR::raiseError("Unable to create event object");
+    }
+    if(is_a($event, 'PEAR_Error')) {
+      return $event;
+    }
 
-    default:
-        return PEAR::raiseError(_("Unsupported Content-Type."));
+    $event->fromHash($r);
+
+    // if a uid was specified in the content, use it.
+    if(!empty($r['UID'])) {
+	 $guid = $r['UID'];
+	 if (!is_a($guid, 'PEAR_Error')) {
+	   $event->setID($guid);
+	 }
     }
+       
+    $id = $event->save();
 
-    if (is_a($eventId, 'PEAR_Error')) {
-        return $eventId;
+    if (is_a($id , 'PEAR_Error')) {
+      return $id;
     }
 
-    return $kronolith->getGUID($eventId);
+    return $kronolith->getGUID($id);
+
+  default:
+    return PEAR::raiseError(_("Unsupported Content-Type."));
+  }
+
 }
 
 /**
@@ -265,66 +300,93 @@
  *                             text/x-vcalendar
  *                             text/x-vevent
  *
- * @return boolean  Success or failure.
+ * @return mixed true on Success, PEAR_Error otherwise.
  */
 function _kronolith_replace($guid, $content, $contentType)
 {
-    require_once dirname(__FILE__) . '/base.php';
-    global $kronolith;
+  require_once dirname(__FILE__) . '/base.php';
+  require_once 'Horde/Data.php';
 
-    $event = $kronolith->getByGUID($guid);
-    if (is_a($event, 'PEAR_Error')) {
-        return $event;
+  global $kronolith;
+
+  $event = $kronolith->getByGUID($guid);
+  if (is_a($event, 'PEAR_Error')) {
+    return $event;
+  }
+
+  if (!array_key_exists($event->getCalendar(), Kronolith::listCalendars(false, PERMS_EDIT))) {
+    return PEAR::raiseError(_("Permission Denied"));
+  }
+
+  // legacy code to import a Horde_iCalendar_vevent from the
+  // iCalendar package. We use Horde_Data now instead of the
+  // iCalendar package. If this package gets removed, this
+  // code can go as well.
+  if (is_a($content, 'Horde_iCalendar_vevent')) {
+    $event->fromiCalendar($content);
+
+    $guid = $content->getAttribute('UID');
+    if (!is_a($guid, 'PEAR_Error')) {
+      $event->setID($guid);
     }
 
-    if (!array_key_exists($event->getCalendar(), Kronolith::listCalendars(false, PERMS_EDIT))) {
-        return PEAR::raiseError(_("Permission Denied"));
+    $id = $event->save();
+    return $is_a($id, 'PEAR_Error') ? $id : true;
+  }
+
+  switch ($contentType) {
+  case 'text/calendar':
+  case 'text/x-icalendar':
+  case 'text/x-vcalendar':
+  case 'text/x-vevent':
+    $data = &Horde_Data::singleton('icalendar');
+    if (is_a($data, 'PEAR_Error')) {
+      return $data;
+    }
+    $r = $data->importData($content);
+    if (is_a($r, 'PEAR_Error')) {
+      return $r;
+    }
+    if ($data->count() == 0) {
+      var_dump($r);die("OK");
+      return PEAR::raiseError(_("No iCalendar data was found."));
+    }
+    if ($data->count() > 1) {
+      return PEAR::raiseError(_("Multiple iCalendar components found; only one vEvent is supported."));
+    }
+    $r = $data->toHash(0);
+    if (is_a($r, 'PEAR_Error')) {
+      return $r;
     }
 
-    switch ($contentType) {
-    case 'text/calendar':
-    case 'text/x-icalendar':
-    case 'text/x-vcalendar':
-    case 'text/x-vevent':
-        if (!is_a($content, 'Horde_iCalendar_vevent')) {
-            require_once 'Horde/iCalendar.php';
-            $iCal = &new Horde_iCalendar();
-            if (!$iCal->parsevCalendar($content)) {
-                return PEAR::raiseError(_("There was an error importing the iCalendar data."));
-            }
-
-            $components = $iCal->getComponents();
-            switch (count($components)) {
-            case 0:
-                return PEAR::raiseError(_("No iCalendar data was found."));
-
-            case 1:
-                $content = $components[0];
-                if (!is_a($content, 'Horde_iCalendar_vevent')) {
-                    return PEAR::raiseError(_("vEvent not found."));
-                }
-                break;
-
-            default:
-                return PEAR::raiseError(_("Multiple iCalendar components found; only one vEvent is supported."));
-            }
-        }
-
-        $event->fromiCalendar($content);
-
-        $guid = $content->getAttribute('UID');
-        if (!is_a($guid, 'PEAR_Error')) {
-            $event->setID($guid);
-        }
+    if (!$event) {
+      return PEAR::raiseError("Unable to create event object");
+    }
+    if(is_a($event, 'PEAR_Error')) {
+      return $event;
+    }
 
-        $eventId = $event->save();
-        break;
+    $event->fromHash($r);
 
-    default:
-        return PEAR::raiseError(_("Unsupported Content-Type."));
+    // if a uid was specified in the content, use it.
+    if(!empty($r['UID'])) {
+	 $guid = $r['UID'];
+	 if (!is_a($guid, 'PEAR_Error')) {
+	   $event->setID($guid);
+	 }
     }
 
-    return is_a($eventId, 'PEAR_Error') ? $eventId : true;
+    $id = $event->save();
+
+    if (is_a($id , 'PEAR_Error')) {
+      return $id;
+    }
+
+    return true;
+
+  default:
+    return PEAR::raiseError(_("Unsupported Content-Type."));
+  }    
 }
 
 /**
@@ -420,4 +482,29 @@
     }
 
     return true;
+}
+
+/**
+ * List Events for a given time period.
+ *
+ * @param integer $startstamp  (optional) The start of the time period to retrieve.
+ * @param integer $endstamp    (optional) The end of the time period to retrieve.
+ * @param string  $calendar    (optional) The calendar to view free/busy
+ *                             slots for. Defaults to the user's default calendar.
+ * @param boolean $showRecurrence  Return every instance of a recurring event?
+ *                                 If false, will only return recurring events
+ *                                 once inside the $startDate - $endDate range.
+ *
+ * @return array of guids
+ */
+function _kronolith_listEvents($startstamp = null, $endstamp = null, $calendar = null,$showRecurrence = true)
+{
+    require_once dirname(__FILE__) . '/base.php';
+
+    if (is_null($calendar)) {
+        $calendar = $GLOBALS['prefs']->getValue('default_share');
+    }
+
+    return Kronolith::listEvents($startstamp, $endstamp, $calendar,$showRecurrence);
+
 }
Index: lib/Driver/sql.php
===================================================================
RCS file: /repository/kronolith/lib/Driver/sql.php,v
retrieving revision 1.121
diff -u -r1.121 sql.php
--- lib/Driver/sql.php	22 May 2004 13:02:13 -0000	1.121
+++ lib/Driver/sql.php	29 Jun 2004 19:03:45 -0000
@@ -292,7 +292,7 @@
 
             $res = $this->_db->query($query);
             if (is_a($res, 'PEAR_Error')) {
-                Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+                Horde::logMessage($res, __FILE__, __LINE__, PEAR_LOG_ERR);
                 return $res;
             }
 
-------------- next part --------------
Index: lib/api.php
===================================================================
RCS file: /repository/mnemo/lib/api.php,v
retrieving revision 1.48
diff -u -r1.48 api.php
--- lib/api.php	9 Jun 2004 16:05:23 -0000	1.48
+++ lib/api.php	29 Jun 2004 19:15:30 -0000
@@ -16,7 +16,7 @@
 );
 
 $_services['listBy'] = array(
-    'args' => array('timestamp', 'action'),
+    'args' => array('action', 'timestamp'),
     'type' => 'stringArray'
 );
 
@@ -79,7 +79,7 @@
  *
  * @return array  An array of GUIDs matching the action and time criteria.
  */
-function &_mnemo_listBy($timestamp, $action)
+function &_mnemo_listBy($action, $timestamp)
 {
     require_once dirname(__FILE__) . '/base.php';
     require_once 'Horde/History.php';
-------------- next part --------------
? list.txt
? myinstall.php
? shell.out
? x.php
Index: DataTree/DataTree/sql.php
===================================================================
RCS file: /repository/framework/DataTree/DataTree/sql.php,v
retrieving revision 1.133
diff -u -r1.133 sql.php
--- DataTree/DataTree/sql.php	29 Jun 2004 15:12:45 -0000	1.133
+++ DataTree/DataTree/sql.php	29 Jun 2004 18:44:53 -0000
@@ -102,6 +102,8 @@
             }
             $root = (string)$root;
 
+            $this->_connect();
+
             $query = sprintf('SELECT datatree_id, datatree_parents FROM %s' .
                              ' WHERE datatree_name = %s AND group_uid = %s ORDER BY datatree_id',
                              $this->_params['table'],
@@ -1046,7 +1048,7 @@
      */
     function _connect()
     {
-        if (!$this->_connected) {
+        if (!$this->_connected || !$this->_db->connection) {
             Horde::assertDriverConfig($this->_params, 'storage',
                 array('phptype', 'hostspec', 'username', 'database', 'charset'),
                 'DataTree SQL');
Index: RPC/RPC/syncml.php
===================================================================
RCS file: /repository/framework/RPC/RPC/syncml.php,v
retrieving revision 1.16
diff -u -r1.16 syncml.php
--- RPC/RPC/syncml.php	7 Apr 2004 17:43:42 -0000	1.16
+++ RPC/RPC/syncml.php	29 Jun 2004 18:45:05 -0000
@@ -69,6 +69,12 @@
             if (!isset($packetNum)) {
                 $packetNum = 0;
             }
+            $f = @fopen($this->_debugDir . '/syncml.packetnum', 'wb');
+            if ($f) {
+                fwrite($f, $packetNum+1);
+                fclose($f);
+            }
+
 
             $f = @fopen($this->_debugDir . '/syncml_client_' . $packetNum . '.xml', 'wb');
             if ($f) {
@@ -99,12 +105,6 @@
                 fclose($f);
             }
 
-            $packetNum++;
-            $f = @fopen($this->_debugDir . '/syncml.packetnum', 'wb');
-            if ($f) {
-                fwrite($f, $packetNum);
-                fclose($f);
-            }
         }
 
         return $xmlinput;
Index: XML_WBXML/WBXML/ContentHandler.php
===================================================================
RCS file: /repository/framework/XML_WBXML/WBXML/ContentHandler.php,v
retrieving revision 1.8
diff -u -r1.8 ContentHandler.php
--- XML_WBXML/WBXML/ContentHandler.php	20 Jan 2004 02:21:54 -0000	1.8
+++ XML_WBXML/WBXML/ContentHandler.php	29 Jun 2004 18:45:12 -0000
@@ -69,6 +69,7 @@
 
     function startElement($uri, $element, $attrs)
     {
+        $this->_output .= "\n";
         $this->_padspaces();
         $this->_output .= '<' . $element;
 
@@ -84,7 +85,7 @@
             $this->_output .= ' ' . $attr['attiribute'] . '="' . $attr['value'] . '"';
         }
 
-        $this->_output .= ">\n";
+        $this->_output .= ">";
 
         $this->_indent++;
     }
@@ -93,16 +94,14 @@
     {
         $this->_indent--;
 
-        $this->_padspaces();
-        $this->_output .= '</' . $element . ">\n";
+        $this->_output .= '</' . $element . ">";
 
         $this->_currentUri->pop();
     }
 
     function characters($str)
     {
-        $this->_padspaces();
-        $this->_output .= $str . "\n";
+      $this->_output .= $str; // . "\n";
     }
 
     function opaque($o)


More information about the sync mailing list