From 3242e169ccfbdc3d86ea618179f42a158481781d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lehrke?= Date: Sat, 9 Jan 2010 18:04:21 +0000 Subject: [PATCH] More Funambol adjustments and workarounds --- .../inc/class.addressbook_vcal.inc.php | 58 +++++-- calendar/inc/class.calendar_ical.inc.php | 86 +++++++--- infolog/inc/class.infolog_ical.inc.php | 158 +++++++++++++++--- phpgwapi/inc/horde/Horde/iCalendar.php | 20 ++- 4 files changed, 247 insertions(+), 75 deletions(-) diff --git a/addressbook/inc/class.addressbook_vcal.inc.php b/addressbook/inc/class.addressbook_vcal.inc.php index 9d73228211..55bb1c1b0b 100644 --- a/addressbook/inc/class.addressbook_vcal.inc.php +++ b/addressbook/inc/class.addressbook_vcal.inc.php @@ -96,8 +96,12 @@ class addressbook_vcal extends addressbook_bo function __construct($contact_app='addressbook', $_contentType='text/x-vcard', &$_clientProperties = array()) { parent::__construct($contact_app); - if($this->log)$this->logfile = $GLOBALS['egw_info']['server']['temp_dir']."/log-vcard"; - if($this->log)error_log(__LINE__.__METHOD__.__FILE__.array2string($_contentType)."\n",3,$this->logfile); + if ($this->log) + { + $this->logfile = $GLOBALS['egw_info']['server']['temp_dir']."/log-vcard"; + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . + array2string($_contentType)."\n",3,$this->logfile); + } switch($_contentType) { case 'text/vcard': @@ -367,7 +371,11 @@ class addressbook_vcal extends addressbook_bo } elseif ($this->productManufacturer == 'funambol') { - $options['ENCODING'] = 'FUNAMBOL-QP'; + if ($this->productName == 'mozilla sync client') + { + $valueData = str_replace( "\n", '\\n', $valueData); + } + $options['ENCODING'] = 'FUNAMBOL-QP'; } elseif (preg_match('/([\000-\012\015\016\020-\037\075])/', $value)) { @@ -411,8 +419,10 @@ class addressbook_vcal extends addressbook_bo $result = $vCard->exportvCalendar(); if ($this->log) { - error_log(__LINE__.__METHOD__.__FILE__."'$this->productManufacturer','$this->productName'"."\n",3,$this->logfile); - error_log(__LINE__.__METHOD__.__FILE__."\n".array2string($result)."\n",3,$this->logfile); + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ . + "() '$this->productManufacturer','$this->productName'\n",3,$this->logfile); + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . + array2string($result)."\n",3,$this->logfile); } return $result; } @@ -484,7 +494,11 @@ class addressbook_vcal extends addressbook_bo 'UID' => array('uid'), ); - if ($this->log) error_log(__LINE__.__METHOD__.__FILE__."\n".array2string($_vcard)."\n",3,$this->logfile); + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . + array2string($_vcard)."\n",3,$this->logfile); + } //Horde::logMessage("vCalAddressbook vcardtoegw:\n$_vcard", __FILE__, __LINE__, PEAR_LOG_DEBUG); @@ -658,18 +672,19 @@ class addressbook_vcal extends addressbook_bo $rowNames[$key] = $rowName; } + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . + array2string($rowNames)."\n",3,$this->logfile); + } - - if($this->log)error_log(__LINE__.__METHOD__.__FILE__."\n".array2string($rowNames)."\n",3,$this->logfile); - - // All rowNames of the vCard are now concatenated with their qualifiers. - // If qualifiers are missing we apply a default strategy. - // E.g. ADR will be either ADR;WORK, if no ADR;WORK is given, - // or else ADR;HOME, if not available elsewhere. + // All rowNames of the vCard are now concatenated with their qualifiers. + // If qualifiers are missing we apply a default strategy. + // E.g. ADR will be either ADR;WORK, if no ADR;WORK is given, + // or else ADR;HOME, if not available elsewhere. //error_log(print_r($rowNames, true)); - $finalRowNames = array(); foreach ($rowNames as $vcardKey => $rowName) @@ -815,7 +830,11 @@ class addressbook_vcal extends addressbook_bo } - if($this->log)error_log(__LINE__.__METHOD__.__FILE__."\n".array2string($finalRowNames)."\n",3,$this->logfile); + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . + array2string($finalRowNames)."\n",3,$this->logfile); + } //error_log(print_r($finalRowNames, true)); @@ -884,8 +903,13 @@ class addressbook_vcal extends addressbook_bo $this->fixup_contact($contact); - if ($this->log) error_log(__LINE__.__METHOD__.__FILE__."'$this->productManufacturer','$this->productName'"."\n",3,$this->logfile); - if ($this->log) error_log(__LINE__.__METHOD__.__FILE__."\n".array2string($contact)."\n",3,$this->logfile); + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ . + "() '$this->productManufacturer','$this->productName'\n",3,$this->logfile); + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . + array2string($contact)."\n",3,$this->logfile); + } return $contact; } diff --git a/calendar/inc/class.calendar_ical.inc.php b/calendar/inc/class.calendar_ical.inc.php index 343943ecf4..a9c9ba049b 100644 --- a/calendar/inc/class.calendar_ical.inc.php +++ b/calendar/inc/class.calendar_ical.inc.php @@ -259,7 +259,11 @@ class calendar_ical extends calendar_boupdate $event['recur_enddate'] = $this->date2ts($time); } - if ($this->log) error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n".array2string($event)."\n",3,$this->logfile); + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . + array2string($event)."\n",3,$this->logfile); + } if ($this->tzid === false) { @@ -491,7 +495,8 @@ class calendar_ical extends calendar_boupdate break; case 'PRIORITY': - if($this->productManufacturer == 'funambol') + if($this->productManufacturer == 'funambol' && + strpos($this->productName, 'outlook') !== false) { $attributes['PRIORITY'] = (int) $this->priority_egw2funambol[$event['priority']]; } @@ -701,54 +706,61 @@ class calendar_ical extends calendar_boupdate $valuesData = (array) $GLOBALS['egw']->translation->convert($values[$key], $GLOBALS['egw']->translation->charset(),'UTF-8'); //echo "$key:$valueID: value=$valueData, param=".print_r($paramDate,true)."\n"; - $vevent->setAttribute($key, $valueData, $paramData, true, $valuesData); - $options = array(); - if ($paramData['CN']) $valueData .= $paramData['CN']; // attendees or organizer CN can contain utf-8 content - - if (preg_match('/[^\x20-\x7F]/', $valueData)) + // attendees or organizer CN can contain utf-8 content + $paramData['CHARSET'] = 'UTF-8'; + if (preg_match('/[^\x20-\x7F]/', $valueData) || + ($paramData['CN'] && preg_match('/[^\x20-\x7F]/', $paramData['CN']))) { switch ($this->productManufacturer) { case 'groupdav': if ($this->productName == 'kde') { - $options['CHARSET'] = 'UTF-8'; - $options['ENCODING'] = 'QUOTED-PRINTABLE'; + $paramData['ENCODING'] = 'QUOTED-PRINTABLE'; } else { - $options['CHARSET'] = ''; - + $paramData['CHARSET'] = ''; if (preg_match('/([\000-\012\015\016\020-\037\075])/', $valueData)) { - $options['ENCODING'] = 'QUOTED-PRINTABLE'; + $paramData['ENCODING'] = 'QUOTED-PRINTABLE'; } else { - $options['ENCODING'] = ''; + $paramData['ENCODING'] = ''; } } break; case 'funambol': - $options['ENCODING'] = 'FUNAMBOL-QP'; - default: - // force UTF-8 - $options['CHARSET'] = 'UTF-8'; + if ($this->productName == 'mozilla sync client') + { + $valueData = str_replace( "\n", '\\n', $valueData); + } + $paramData['ENCODING'] = 'FUNAMBOL-QP'; } } if (preg_match('/([\000-\012])/', $valueData)) { - if ($this->log) error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."() Has invalid XML data: $valueData",3,$this->logfile); + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ . + "() Has invalid XML data: $valueData",3,$this->logfile); + } } - $vevent->setParameter($key, $options); + $vevent->setAttribute($key, $valueData, $paramData, true, $valuesData); } } $vcal->addComponent($vevent); } $retval = $vcal->exportvCalendar(); - if ($this->log) error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n".array2string($retval)."\n",3,$this->logfile); - + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ . + "() '$this->productManufacturer','$this->productName'\n",3,$this->logfile); + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ . + "()\n".array2string($retval)."\n",3,$this->logfile); + } return $retval; } @@ -793,7 +805,11 @@ class calendar_ical extends calendar_boupdate */ function importVCal($_vcalData, $cal_id=-1, $etag=null, $merge=false, $recur_date=0) { - if ($this->log) error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n".array2string($_vcalData)."\n",3,$this->logfile); + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . + array2string($_vcalData)."\n",3,$this->logfile); + } if (!is_array($this->supportedFields)) $this->setSupportedFields(); @@ -815,7 +831,11 @@ class calendar_ical extends calendar_boupdate } foreach ($events as $event) { - if ($this->log) error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n".array2string($event)."\n",3,$this->logfile); + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . + array2string($event)."\n",3,$this->logfile); + } $updated_id = false; $event_info = $this->get_event_info($event); @@ -1137,7 +1157,8 @@ class calendar_ical extends calendar_boupdate if ($this->log) { - error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n".array2string($event_info['stored_event'])."\n",3,$this->logfile); + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . + array2string($event_info['stored_event'])."\n",3,$this->logfile); } } @@ -1308,6 +1329,8 @@ class calendar_ical extends calendar_boupdate ); $defaultFields['funambol'] = $defaultFields['basic'] + array( + 'participants' => 'participants', + 'owner' => 'owner', 'category' => 'category', 'non_blocking' => 'non_blocking', 'recurrence' => 'recurrence', @@ -1468,7 +1491,11 @@ class calendar_ical extends calendar_boupdate $vcal = new Horde_iCalendar; if (!$vcal->parsevCalendar($_vcalData)) { - if ($this->log) error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."(): No vCalendar Container found!\n",3,$this->logfile); + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. + "(): No vCalendar Container found!\n",3,$this->logfile); + } if ($this->tzid) { date_default_timezone_set($GLOBALS['egw_info']['server']['server_timezone']); @@ -1906,7 +1933,8 @@ class calendar_ical extends calendar_boupdate } break; case 'PRIORITY': - if($this->productManufacturer == 'funambol') + if($this->productManufacturer == 'funambol' && + strpos($this->productName, 'outlook') !== false) { $vcardData['priority'] = (int) $this->priority_funambol2egw[$attributes['value']]; } @@ -2165,7 +2193,11 @@ class calendar_ical extends calendar_boupdate } if ($this->calendarOwner) $event['owner'] = $this->calendarOwner; - if ($this->log) error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n".array2string($event)."\n",3,$this->logfile); + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . + array2string($event)."\n",3,$this->logfile); + } //Horde::logMessage("vevent2egw:\n" . print_r($event, true), // __FILE__, __LINE__, PEAR_LOG_DEBUG); return $event; diff --git a/infolog/inc/class.infolog_ical.inc.php b/infolog/inc/class.infolog_ical.inc.php index 3faa170738..3ed62c2f1e 100644 --- a/infolog/inc/class.infolog_ical.inc.php +++ b/infolog/inc/class.infolog_ical.inc.php @@ -20,23 +20,42 @@ require_once EGW_SERVER_ROOT.'/phpgwapi/inc/horde/lib/core.php'; class infolog_ical extends infolog_bo { /** - * @var array conversion of the priority egw => ical + * @var array $priority_egw2ical conversion of the priority egw => ical */ - var $egw_priority2vcal_priority = array( - 0 => 9, // low - 1 => 5, // normal - 2 => 3, // high - 3 => 1, // urgent + var $priority_egw2ical = array( + 0 => 9, // low + 1 => 5, // normal + 2 => 3, // high + 3 => 1, // urgent ); /** - * @var array conversion of the priority ical => egw + * @var array $priority_ical2egw conversion of the priority ical => egw */ - var $vcal_priority2egw_priority = array( - 9 => 0, 8 => 0, 7 => 0, // low - 6 => 1, 5 => 1, 4 => 1, 0 => 1, // normal - 3 => 2, 2 => 2, // high - 1 => 3, // urgent + var $priority_ical2egw = array( + 9 => 0, 8 => 0, 7 => 0, // low + 6 => 1, 5 => 1, 4 => 1, 0 => 1, // normal + 3 => 2, 2 => 2, // high + 1 => 3, // urgent + ); + + /** + * @var array $priority_egw2funambol conversion of the priority egw => funambol + */ + var $priority_egw2funambol = array( + 0 => 0, // low + 1 => 1, // normal + 2 => 2, // high + 3 => 2, // urgent + ); + + /** + * @var array $priority_funambol2egw conversion of the priority funambol => egw + */ + var $priority_funambol2egw = array( + 0 => 0, // low + 1 => 1, // normal + 2 => 3, // high ); /** @@ -61,6 +80,15 @@ class infolog_ical extends infolog_bo */ var $clientProperties; + /** + * Set Logging + * + * @var boolean + */ + var $log = false; + var $logfile="/tmp/log-infolog-vcal"; + + /** * Constructor * @@ -69,7 +97,7 @@ class infolog_ical extends infolog_bo function __construct(&$_clientProperties = array()) { parent::__construct(); - + if ($this->log) $this->logfile = $GLOBALS['egw_info']['server']['temp_dir']."/log-infolog-vcal"; $this->clientProperties = $_clientProperties; } @@ -201,6 +229,10 @@ class infolog_ical extends infolog_bo } break; case 'funambol': + if ($this->productName == 'mozilla sync client') + { + $value = str_replace( "\n", '\\n', $value); + } $options['ENCODING'] = 'FUNAMBOL-QP'; } } @@ -228,13 +260,27 @@ class infolog_ical extends infolog_bo // we try to preserv the original infolog status as X-INFOLOG-STATUS, so we can restore it, if the user does not modify STATUS $vevent->setAttribute('X-INFOLOG-STATUS',$taskData['info_status']); $vevent->setAttribute('PERCENT-COMPLETE',$taskData['info_percent']); - $vevent->setAttribute('PRIORITY',$this->egw_priority2vcal_priority[$taskData['info_priority']]); - + if($this->productManufacturer == 'funambol' && + strpos($this->productName, 'outlook') !== false) + { + $priority = (int) $this->priority_egw2funambol[$taskData['info_priority']]; + } + else + { + $priority = (int) $this->priority_egw2ical2[$taskData['info_priority']]; + } + $vevent->setAttribute('PRIORITY', $priority); $vcal->addComponent($vevent); $retval = $vcal->exportvCalendar(); - Horde::logMessage("exportVTODO:\n" . print_r($retval, true), __FILE__, __LINE__, PEAR_LOG_DEBUG); + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . + array2string($retval)."\n",3,$this->logfile); + } + // Horde::logMessage("exportVTODO:\n" . print_r($retval, true), + // __FILE__, __LINE__, PEAR_LOG_DEBUG); return $retval; } @@ -272,7 +318,7 @@ class infolog_ical extends infolog_bo */ function importVTODO(&$_vcalData, $_taskID=-1, $merge=false) { - if (!$taskData = $this->vtodotoegw($_vcalData,$_taskID)) return false; + if (!($taskData = $this->vtodotoegw($_vcalData,$_taskID))) return false; // we suppose that a not set status in a vtodo means that the task did not started yet if (empty($taskData['info_status'])) @@ -285,6 +331,12 @@ class infolog_ical extends infolog_bo $taskData['info_datecompleted'] = 0; } + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . + array2string($taskData)."\n",3,$this->logfile); + } + return $this->write($taskData); } @@ -319,8 +371,23 @@ class infolog_ical extends infolog_bo */ function vtodotoegw($_vcalData, $_taskID=-1) { + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . + array2string($_vcalData)."\n",3,$this->logfile); + } + $vcal = new Horde_iCalendar; - if (!($vcal->parsevCalendar($_vcalData))) return false; + if (!($vcal->parsevCalendar($_vcalData))) + { + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. + "(): No vCalendar Container found!\n",3,$this->logfile); + } + return false; + } + $version = $vcal->getAttribute('VERSION'); if (isset($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'])) @@ -332,11 +399,22 @@ class infolog_ical extends infolog_bo $minimum_uid_length = 8; } - $components = $vcal->getComponents(); - - foreach ($components as $component) + foreach ($vcal->getComponents() as $component) { - if (is_a($component, 'Horde_iCalendar_vtodo')) + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . + array2string($component)."\n",3,$this->logfile); + } + if (!is_a($component, 'Horde_iCalendar_vtodo')) + { + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__. + "(): Not a vTODO container, skipping...\n",3,$this->logfile); + } + } + else { $taskData = array(); $taskData['info_type'] = 'task'; @@ -399,7 +477,15 @@ class infolog_ical extends infolog_bo case 'PRIORITY': if (1 <= $attributes['value'] && $attributes['value'] <= 9) { - $taskData['info_priority'] = $this->vcal_priority2egw_priority[$attributes['value']]; + if($this->productManufacturer == 'funambol' && + strpos($this->productName, 'outlook') !== false) + { + $taskData['info_priority'] = (int) $this->priority_funambol2egw[$attributes['value']]; + } + else + { + $taskData['info_priority'] = (int) $this->priority_ical2egw[$attributes['value']]; + } } else { $taskData['info_priority'] = 1; // default = normal } @@ -520,6 +606,10 @@ class infolog_ical extends infolog_bo } break; case 'funambol': + if ($this->productName == 'mozilla sync client') + { + $value = str_replace( "\n", '\\n', $value); + } $options['ENCODING'] = 'FUNAMBOL-QP'; } } @@ -534,8 +624,13 @@ class infolog_ical extends infolog_bo #$vnote->setAttribute('CLASS',$taskData['info_access'] == 'public' ? 'PUBLIC' : 'PRIVATE'); - return $vnote->exportvCalendar(); - break; + $retval = $vnote->exportvCalendar(); + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . + array2string($retval)."\n",3,$this->logfile); + } + return $retval; } return false; } @@ -551,13 +646,24 @@ class infolog_ical extends infolog_bo */ function importVNOTE(&$_vcalData, $_type, $_noteID=-1, $merge=false) { + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . + array2string($_vcalData)."\n",3,$this->logfile); + } + if (!($note = $this->vnotetoegw($_vcalData, $_type, $_noteID))) return false; if($_noteID > 0) $note['info_id'] = $_noteID; if (empty($note['info_status'])) $note['info_status'] = 'done'; - #_debug_array($taskData);exit; + if ($this->log) + { + error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . + array2string($note)."\n",3,$this->logfile); + } + return $this->write($note); } diff --git a/phpgwapi/inc/horde/Horde/iCalendar.php b/phpgwapi/inc/horde/Horde/iCalendar.php index cd7fe9b9dd..0176e0073f 100644 --- a/phpgwapi/inc/horde/Horde/iCalendar.php +++ b/phpgwapi/inc/horde/Horde/iCalendar.php @@ -520,14 +520,20 @@ class Horde_iCalendar { * * @return boolean True on successful import, false otherwise. */ - function parsevCalendar($text, $base = 'VCALENDAR', $charset = null, - $clear = true) + function parsevCalendar($text, $base = 'VCALENDAR', $charset = null, $clear = true) { if ($clear) { $this->clear(); } - if (preg_match('/^BEGIN:' . $base . '(.*)^END:' . $base . '/ism', $text, $matches)) { + if ($base == 'VTODO' && + preg_match('/^BEGIN:VTODO(.*)^END:VEVENT/ism', $text, $matches)) { + // Workaround for Funambol VTODO bug in Mozilla Sync Plugins + Horde::logMessage('iCalendar: Funambol VTODO-bug detected, workaround activated...', + __FILE__, __LINE__, PEAR_LOG_WARNING); + $container = true; + $vCal = $matches[1]; + } elseif (preg_match('/^BEGIN:' . $base . '(.*)^END:' . $base . '/ism', $text, $matches)) { $container = true; $vCal = $matches[1]; } else { @@ -543,12 +549,14 @@ class Horde_iCalendar { $this->setAttribute('VERSION', $matches[1]); } - // Preserve a trailing CR + // Preserve a trailing CR $vCal = trim($vCal) . "\n"; // All subcomponents. $matches = null; - if (preg_match_all('/^BEGIN:(.*)(\r\n|\r|\n)(.*)^END:\1/Uims', $vCal, $matches)) { + // Workaround for Funambol VTODO bug in Mozilla Sync Plugins + if (preg_match_all('/^BEGIN:(VTODO)(\r\n|\r|\n)(.*)^END:VEVENT/Uims', $vCal, $matches) || + preg_match_all('/^BEGIN:(.*)(\r\n|\r|\n)(.*)^END:\1/Uims', $vCal, $matches)) { // vTimezone components are processed first. They are // needed to process vEvents that may use a TZID. foreach ($matches[0] as $key => $data) { @@ -652,6 +660,8 @@ class Horde_iCalendar { $value = $GLOBALS['egw']->translation->convert($value, empty($charset) ? ($this->isOldFormat() ? 'iso-8859-1' : 'utf-8') : $charset); } + // Funambol hack :-( + $value = str_replace('\\\\n', "\n", $value); break; case 'B': case 'BASE64':