From 43f860ba8f2081fd5b59fc4feb0616011838234d Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 3 Nov 2008 09:36:20 +0000 Subject: [PATCH] Reworked GroupDAV and iCal/vCard handler to set 'GroupDAV' as product manufacturer and the recogniced GroupDAV client as product name. This way we are able to handle different GroupDAV clients, as we allready do with different SyncML clients. Also removed the no longer needed code enabling the use of the real UID, as SyncML does no longer misuse the UID for it's GUID. --- .../inc/class.addressbook_groupdav.inc.php | 6 +-- .../inc/class.addressbook_vcal.inc.php | 17 ++++--- calendar/inc/class.calendar_groupdav.inc.php | 24 ++++++++-- calendar/inc/class.calendar_ical.inc.php | 18 +++++--- infolog/inc/class.infolog_groupdav.inc.php | 19 ++++++-- infolog/inc/class.infolog_ical.inc.php | 36 +++++++++++---- phpgwapi/inc/class.groupdav_handler.inc.php | 44 +++++++++++++++++++ 7 files changed, 133 insertions(+), 31 deletions(-) diff --git a/addressbook/inc/class.addressbook_groupdav.inc.php b/addressbook/inc/class.addressbook_groupdav.inc.php index 00689b24dd..f4df479a60 100644 --- a/addressbook/inc/class.addressbook_groupdav.inc.php +++ b/addressbook/inc/class.addressbook_groupdav.inc.php @@ -291,10 +291,8 @@ class addressbook_groupdav extends groupdav_handler private function _get_handler() { $handler =& new addressbook_vcal(); - if (strpos($_SERVER['HTTP_USER_AGENT'],'KHTML') !== false) - { - $handler->setSupportedFields('KDE'); - } + $handler->setSupportedFields('GroupDAV',$this->agent); + return $handler; } diff --git a/addressbook/inc/class.addressbook_vcal.inc.php b/addressbook/inc/class.addressbook_vcal.inc.php index 83e2259dd6..2770e16c60 100644 --- a/addressbook/inc/class.addressbook_vcal.inc.php +++ b/addressbook/inc/class.addressbook_vcal.inc.php @@ -128,11 +128,11 @@ class addressbook_vcal extends addressbook_bo { $value = $GLOBALS['egw']->translation->convert(trim($value), $sysCharSet,$_charset); - if(($extra_charset_attribute || $this->productManufacturer == 'kde') && preg_match('/([\177-\377])/',$value)) + if(($extra_charset_attribute || $this->productName == 'kde') && preg_match('/([\177-\377])/',$value)) { $options['CHARSET'] = $_charset; // KAddressbook requires non-ascii chars to be qprint encoded, other clients eg. nokia phones have trouble with that - if ($this->productManufacturer == 'kde') + if ($this->productName == 'kde') { $options['ENCODING'] = 'QUOTED-PRINTABLE'; } @@ -251,7 +251,7 @@ class addressbook_vcal extends addressbook_bo 'TITLE' => array('title'), ); - $defaultFields[1] = array( // all entries, nexthaus corporation, ... + $defaultFields[1] = array( // all entries, nexthaus corporation, groupdav, ... 'ADR;WORK' => array('','','adr_one_street','adr_one_locality','adr_one_region', 'adr_one_postalcode','adr_one_countryname'), 'ADR;HOME' => array('','','adr_two_street','adr_two_locality','adr_two_region', @@ -618,8 +618,15 @@ class addressbook_vcal extends addressbook_bo $this->supportedFields = $defaultFields[1]; break; - case 'kde': // KDE Addressbook via GroupDAV - $this->supportedFields = $defaultFields[8]; + case 'groupdav': // all GroupDAV access goes through here + switch($this->productName) + { + case 'kde': // KDE Addressbook + $this->supportedFields = $defaultFields[8]; + break; + default: + $this->supportedFields = $defaultFields[1]; + } break; // the fallback for SyncML diff --git a/calendar/inc/class.calendar_groupdav.inc.php b/calendar/inc/class.calendar_groupdav.inc.php index a14333a201..9f1c050126 100644 --- a/calendar/inc/class.calendar_groupdav.inc.php +++ b/calendar/inc/class.calendar_groupdav.inc.php @@ -99,14 +99,15 @@ class calendar_groupdav extends groupdav_handler //header('X-EGROUPWARE-EVENT-'.$event['id'].': '.$event['title'].': '.date('Y-m-d H:i:s',$event['start']).' - '.date('Y-m-d H:i:s',$event['end'])); $props = array( HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($event)), - HTTP_WebDAV_Server::mkprop('getcontenttype', strpos($_SERVER['HTTP_USER_AGENT'],'KHTML') === false ? + HTTP_WebDAV_Server::mkprop('getcontenttype', $this->agent != 'kde' ? 'text/calendar; charset=utf-8; component=VEVENT' : 'text/calendar'), // getlastmodified and getcontentlength are required by WebDAV and Cadaver eg. reports 404 Not found if not set HTTP_WebDAV_Server::mkprop('getlastmodified', $event['modified']), ); if ($calendar_data) { - $content = ExecMethod2('calendar.calendar_ical.exportVCal',array($event),'2.0','PUBLISH',false,false); + if (is_null($handler)) $handler = $this->_get_handler(); + $content = $handler->exportVCal(array($event),'2.0','PUBLISH'); $props[] = HTTP_WebDAV_Server::mkprop('getcontentlength',bytes($content)); $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-data',$content); } @@ -247,7 +248,8 @@ class calendar_groupdav extends groupdav_handler { return $event; } - $options['data'] = ExecMethod2('calendar.calendar_ical.exportVCal',array($event),'2.0','PUBLISH',false,false); + $handler = $this->_get_handler(); + $options['data'] = $handler->exportVCal(array($event),'2.0','PUBLISH'); $options['mimetype'] = 'text/calendar; charset=utf-8'; header('Content-Encoding: identity'); header('ETag: '.$this->get_etag($event)); @@ -270,7 +272,8 @@ class calendar_groupdav extends groupdav_handler { return $event; } - if (!($cal_id = ExecMethod2('calendar.calendar_ical.importVCal',$options['content'],is_numeric($id) ? $id : -1, + $handler = $this->_get_handler(); + if (!($cal_id = $handler->importVCal($options['content'],is_numeric($id) ? $id : -1, self::etag2value($this->http_if_match)))) { if ($this->debug) error_log(__METHOD__."(,$id) importVCal($options[content]) returned false"); @@ -373,4 +376,17 @@ class calendar_groupdav extends groupdav_handler return $props; } + + /** + * Get the handler and set the supported fields + * + * @return calendar_ical + */ + private function _get_handler() + { + $handler =& new calendar_ical(); + $handler->setSupportedFields('GroupDAV',$this->agent); + + return $handler; + } } \ No newline at end of file diff --git a/calendar/inc/class.calendar_ical.inc.php b/calendar/inc/class.calendar_ical.inc.php index d7f6200a4c..1d343e7b79 100644 --- a/calendar/inc/class.calendar_ical.inc.php +++ b/calendar/inc/class.calendar_ical.inc.php @@ -107,12 +107,9 @@ class calendar_ical extends calendar_boupdate * @param int/array $events (array of) cal_id or array of the events * @param string $version='1.0' could be '2.0' too * @param string $method='PUBLISH' - * @param boolean $force_own_uid=true ignore the stored and maybe from the client transfered uid and generate a new one - * RalfBecker: GroupDAV/CalDAV requires to switch that non RFC conform behavior off, dont know if SyncML still needs it - * @param boolean $extra_charset_attribute=true GroupDAV/CalDAV dont need the charset attribute and some clients have problems with it - * @return string/boolean string with vCal or false on error (eg. no permission to read the event) + * @return string/boolean string with iCal or false on error (eg. no permission to read the event) */ - function &exportVCal($events,$version='1.0', $method='PUBLISH',$force_own_uid=true,$extra_charset_attribute=true) + function &exportVCal($events,$version='1.0', $method='PUBLISH') { $egwSupportedFields = array( 'CLASS' => array('dbName' => 'public'), @@ -443,7 +440,6 @@ class calendar_ical extends calendar_boupdate } } - //$attributes['UID'] = $force_own_uid ? $eventGUID : $event['uid']; $attributes['UID'] = $event['uid']; foreach($attributes as $key => $value) { @@ -459,7 +455,7 @@ class calendar_ical extends calendar_boupdate { $options['ENCODING'] = 'QUOTED-PRINTABLE'; } - if($extra_charset_attribute && preg_match('/([\177-\377])/',$valueData)) + if($this->productManufacturer != 'GroupDAV' && preg_match('/([\177-\377])/',$valueData)) { $options['CHARSET'] = 'UTF-8'; } @@ -1221,6 +1217,14 @@ class calendar_ical extends calendar_boupdate $this->supportedFields = $defaultFields['full']; break; + case 'groupdav': // all GroupDAV access goes through here + switch($this->productName) + { + default: + $this->supportedFields = $defaultFields['full']; + } + break; + // the fallback for SyncML default: error_log("Unknown calendar SyncML client: manufacturer='$_productManufacturer' product='$_productName'"); diff --git a/infolog/inc/class.infolog_groupdav.inc.php b/infolog/inc/class.infolog_groupdav.inc.php index a5dcd09557..9857db6c2d 100644 --- a/infolog/inc/class.infolog_groupdav.inc.php +++ b/infolog/inc/class.infolog_groupdav.inc.php @@ -69,7 +69,7 @@ class infolog_groupdav extends groupdav_handler 'path' => '/infolog/'.$task['info_id'].'.ics', 'props' => array( HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($task)), - HTTP_WebDAV_Server::mkprop('getcontenttype', strpos($_SERVER['HTTP_USER_AGENT'],'KHTML') === false ? + HTTP_WebDAV_Server::mkprop('getcontenttype',$this->agent != 'kde' ? 'text/calendar; charset=utf-8; component=VTODO' : 'text/calendar'), // Konqueror (3.5) dont understand it otherwise // getlastmodified and getcontentlength are required by WebDAV and Cadaver eg. reports 404 Not found if not set HTTP_WebDAV_Server::mkprop('getlastmodified', $task['info_datemodified']), @@ -94,7 +94,7 @@ class infolog_groupdav extends groupdav_handler { return $task; } - $handler = new infolog_ical(); + $handler = $this->_get_handler(); $options['data'] = $handler->exportVTODO($id,'2.0',false,false); // keep UID the client set and no extra charset attributes $options['mimetype'] = 'text/calendar; charset=utf-8'; header('Content-Encoding: identity'); @@ -117,7 +117,7 @@ class infolog_groupdav extends groupdav_handler { return $ok; } - $handler = new infolog_ical(); + $handler = $this->_get_handler(); if (!($info_id = $handler->importVTODO($options['content'],is_numeric($id) ? $id : -1))) { if ($this->debug) error_log(__METHOD__."(,$id) import_vtodo($options[content]) returned false"); @@ -189,4 +189,17 @@ class infolog_groupdav extends groupdav_handler } return '"'.$info['info_id'].':'.$info['info_datemodified'].'"'; } + + /** + * Get the handler and set the supported fields + * + * @return infolog_ical + */ + private function _get_handler() + { + $handler =& new infolog_ical(); + $handler->setSupportedFields('GroupDAV',$this->agent); + + return $handler; + } } \ No newline at end of file diff --git a/infolog/inc/class.infolog_ical.inc.php b/infolog/inc/class.infolog_ical.inc.php index 7a14d1b502..fe6f535db1 100644 --- a/infolog/inc/class.infolog_ical.inc.php +++ b/infolog/inc/class.infolog_ical.inc.php @@ -31,17 +31,23 @@ class infolog_ical extends infolog_bo 3 => 0, ); + /** + * manufacturer and name of the sync-client + * + * @var string + */ + var $productManufacturer = 'file'; + var $productName = ''; + /** * Exports one InfoLog tast to an iCalendar VTODO * * @param int $_taskID info_id - * @param string $version='2.0' could be '1.0' too - * @param boolean $force_own_uid=true ignore the stored and maybe from the client transfered uid and generate a new one - * RalfBecker: GroupDAV/CalDAV requires to switch that non RFC conform behavior off, dont know if SyncML still needs it - * @param boolean $extra_charset_attribute=true GroupDAV/CalDAV dont need the charset attribute and some clients have problems with it + * @param string $_version='2.0' could be '1.0' too + * @param string $_method='PUBLISH' * @return string/boolean string with vCal or false on error (eg. no permission to read the event) */ - function exportVTODO($_taskID, $_version='2.0',$force_own_uid=true,$extra_charset_attribute=true) + function exportVTODO($_taskID, $_version='2.0',$_method='PUBLISH') { $taskData = $this->read($_taskID); @@ -51,7 +57,7 @@ class infolog_ical extends infolog_bo $vcal = &new Horde_iCalendar; $vcal->setAttribute('VERSION',$_version); - $vcal->setAttribute('METHOD','PUBLISH'); + $vcal->setAttribute('METHOD',$_method); $vevent = Horde_iCalendar::newComponent('VTODO',$vcal); @@ -68,7 +74,7 @@ class infolog_ical extends infolog_bo { $options['ENCODING'] = 'QUOTED-PRINTABLE'; } - if($extra_charset_attribute && preg_match('/([\177-\377])/',$value)) + if($this->productManufacturer != 'GroupDAV' && preg_match('/([\177-\377])/',$value)) { $options['CHARSET'] = 'UTF-8'; } @@ -83,7 +89,6 @@ class infolog_ical extends infolog_bo $vevent->setAttribute('DTSTAMP',time()); $vevent->setAttribute('CREATED',$GLOBALS['egw']->contenthistory->getTSforAction('infolog_task',$_taskID,'add')); $vevent->setAttribute('LAST-MODIFIED',$GLOBALS['egw']->contenthistory->getTSforAction('infolog_task',$_taskID,'modify')); - //$vevent->setAttribute('UID',$force_own_uid ? $taskGUID : $taskData['info_uid']); $vevent->setAttribute('UID',$taskData['info_uid']); $vevent->setAttribute('CLASS',$taskData['info_access'] == 'public' ? 'PUBLIC' : 'PRIVATE'); $vevent->setAttribute('STATUS',$this->status2vtodo($taskData['info_status'])); @@ -375,5 +380,20 @@ class infolog_ical extends infolog_bo } return FALSE; } + + /** + * Set the supported fields + * + * Currently we only store manufacturer and name + * + * @param string $_productManufacturer + * @param string $_productName + */ + function setSupportedFields($_productManufacturer='file', $_productName='') + { + // save them vor later use + $this->productManufacturer = $_productManufacturer; + $this->productName = $_productName; + } } diff --git a/phpgwapi/inc/class.groupdav_handler.inc.php b/phpgwapi/inc/class.groupdav_handler.inc.php index ae07141988..bfe0efe3e8 100644 --- a/phpgwapi/inc/class.groupdav_handler.inc.php +++ b/phpgwapi/inc/class.groupdav_handler.inc.php @@ -65,6 +65,12 @@ abstract class groupdav_handler * @var string */ var $http_if_match; + /** + * Identified user agent + * + * @var string + */ + var $agent; /** * Constructor @@ -78,6 +84,7 @@ abstract class groupdav_handler $this->app = $app; if (!is_null($debug)) $this->debug = $debug; $this->base_uri = is_null($base_uri) ? $base_uri : $_SERVER['SCRIPT_NAME']; + $this->agent = self::get_agent(); $this->translation =& $GLOBALS['egw']->translation; $this->egw_charset = $this->translation->charset(); @@ -259,4 +266,41 @@ abstract class groupdav_handler } return $handler_cache[$app]; } + + /** + * Identify know GroupDAV agents by HTTP_USER_AGENT header + * + * @return string|boolean agent name or false + */ + static function get_agent() + { + static $agent; + + if (is_null($agent)) + { + $agent = false; + // identify the agent (GroupDAV client) from the HTTP_USER_AGENT header + $user_agent = strtolower($_SERVER['HTTP_USER_AGENT']); + foreach(array( + 'davkit' => 'davkit', // Apple iCal + 'bionicmessage.net' => 'funambol', // funambol GroupDAV connector from bionicmessage.net + 'zideone' => 'zideone', // zideone outlook plugin + 'lightning' => 'lightning', // Lighting (SOGo connector for addressbook) + 'khtml' => 'kde', // KDE clients + 'cadaver' => 'cadaver', + ) as $pattern => $name) + { + if (strpos($user_agent,$pattern) !== false) + { + $agent = $name; + break; + } + } + if (!$agent) + { + error_log("Unrecogniced GroupDAV client: HTTP_USER_AGENT='$_SERVER[HTTP_USER_AGENT]'!"); + } + } + return $agent; + } } \ No newline at end of file