From 19081734cd31af57e5e9740bbc8c7cfc0a4dfc67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lehrke?= Date: Thu, 4 Mar 2010 23:09:55 +0000 Subject: [PATCH] Updated GroupDAV code to support InfoLog calendars, Addressbook and new principal implementation --- .../inc/class.addressbook_groupdav.inc.php | 86 +++- addressbook/inc/class.addressbook_so.inc.php | 4 +- .../inc/class.addressbook_vcal.inc.php | 26 +- calendar/inc/class.calendar_groupdav.inc.php | 85 ++-- calendar/inc/class.calendar_ical.inc.php | 18 +- egw-pear/HTTP/WebDAV/Server.php | 410 ++++++++++-------- egw-pear/HTTP/WebDAV/Server/Filesystem.php | 2 +- infolog/inc/class.infolog_bo.inc.php | 4 +- infolog/inc/class.infolog_groupdav.inc.php | 35 +- infolog/inc/class.infolog_ical.inc.php | 8 +- phpgwapi/inc/class.groupdav.inc.php | 212 ++++++--- phpgwapi/inc/class.groupdav_handler.inc.php | 62 ++- .../inc/class.groupdav_principals.inc.php | 334 ++++++++++++-- phpgwapi/inc/class.vfs_webdav_server.inc.php | 3 +- 14 files changed, 922 insertions(+), 367 deletions(-) diff --git a/addressbook/inc/class.addressbook_groupdav.inc.php b/addressbook/inc/class.addressbook_groupdav.inc.php index 66b054cef8..2f47a2256c 100644 --- a/addressbook/inc/class.addressbook_groupdav.inc.php +++ b/addressbook/inc/class.addressbook_groupdav.inc.php @@ -68,7 +68,7 @@ class addressbook_groupdav extends groupdav_handler */ static function get_path($contact) { - return '/addressbook/'.$contact[self::PATH_ATTRIBUTE].'.vcf'; + return $contact[self::PATH_ATTRIBUTE].'.vcf'; } /** @@ -96,8 +96,9 @@ class addressbook_groupdav extends groupdav_handler } if ($this->debug) error_log(__METHOD__."($path,".array2string($options).",,$user,$id) filter=".array2string($filter)); - // check if we have to return the full calendar data or just the etag's - if (!($filter['address_data'] = $options['props'] == 'all' && $options['root']['ns'] == groupdav::CARDDAV) && is_array($options['props'])) + // check if we have to return the full contact data or just the etag's + if (!($filter['address_data'] = $options['props'] == 'all' && + $options['root']['ns'] == groupdav::CARDDAV) && is_array($options['props'])) { foreach($options['props'] as $prop) { @@ -109,7 +110,7 @@ class addressbook_groupdav extends groupdav_handler } } // return iterator, calling ourself to return result in chunks - $files['files'] = new groupdav_propfind_iterator($this,$filter,$files['files']); + $files['files'] = new groupdav_propfind_iterator($this,$path,$filter,$files['files']); return true; } @@ -117,11 +118,12 @@ class addressbook_groupdav extends groupdav_handler /** * Callback for profind interator * + * @param string $path * @param array $filter * @param array|boolean $start=false false=return all or array(start,num) * @return array with "files" array with values for keys path and props */ - function &propfind_callback(array $filter,$start=false) + function &propfind_callback($path,array $filter,$start=false) { $starttime = microtime(true); @@ -139,7 +141,7 @@ class addressbook_groupdav extends groupdav_handler { $props = array( HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($contact)), - HTTP_WebDAV_Server::mkprop('getcontenttype', 'text/x-vcard'), + HTTP_WebDAV_Server::mkprop('getcontenttype', 'text/vcard'), // getlastmodified and getcontentlength are required by WebDAV and Cadaver eg. reports 404 Not found if not set HTTP_WebDAV_Server::mkprop('getlastmodified', $contact['modified']), ); @@ -147,19 +149,19 @@ class addressbook_groupdav extends groupdav_handler { $content = $handler->getVCard($contact,$this->charset,false); $props[] = HTTP_WebDAV_Server::mkprop('getcontentlength',bytes($content)); - $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'address-data',$content); + $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'address-data',$content,true); } else { $props[] = HTTP_WebDAV_Server::mkprop('getcontentlength', ''); // expensive to calculate and no CalDAV client uses it } $files[] = array( - 'path' => self::get_path($contact), + 'path' => $path.self::get_path($contact), 'props' => $props, ); } } - if ($this->debug) error_log(__METHOD__.'('.array2string($filter).','.array2string($start).") took ".(microtime(true) - $starttime).' to return '.count($files).' items'); + if ($this->debug) error_log(__METHOD__."($path,".array2string($filter).','.array2string($start).") took ".(microtime(true) - $starttime).' to return '.count($files).' items'); return $files; } @@ -266,14 +268,24 @@ class addressbook_groupdav extends groupdav_handler */ function put(&$options,$id,$user=null) { + if ($this->debug) error_log(__METHOD__.'('.array2string($options).",$id,$user)"); $ok = $this->_common_get_put_delete('PUT',$options,$id); if (!is_null($ok) && !is_array($ok)) { return $ok; } $handler = self::_get_handler(); - $contact = $handler->vcardtoegw($options['content']); - + $vCard = htmlspecialchars_decode($options['content']); + $contactId = (is_array($ok) ? -1 : $ok['id']); + $contact = $handler->vcardtoegw($vCard, $contactId); + if (is_array($contact['category'])) + { + $contact['category'] = implode(',',$this->bo->find_or_add_categories($contact['category'], $contactId)); + } + elseif ($contactId > 0) + { + $contact['category'] = $ok['category']; + } if (!is_null($ok)) { $contact['id'] = $ok['id']; @@ -282,6 +294,10 @@ class addressbook_groupdav extends groupdav_handler $contact['owner'] = $ok['owner']; $contact['private'] = $ok['private']; } + else + { + $contact['owner'] = $user; + } if ($this->http_if_match) $contact['etag'] = self::etag2value($this->http_if_match); if (!($save_ok = $this->bo->save($contact))) @@ -295,13 +311,14 @@ class addressbook_groupdav extends groupdav_handler } if (!isset($contact['etag'])) { - $contact = $this->read($contact['id']); + $contact = $this->read($save_ok); } header('ETag: '.$this->get_etag($contact)); if (is_null($ok)) { - header($h='Location: '.$this->base_uri.self::get_path($contact)); + $path = preg_replace('|(.*)/[^/]*|', '\1/', $options['path']); + header($h='Location: '.$path.self::get_path($contact)); if ($this->debug) error_log(__METHOD__."($method,,$id) header('$h'): 201 Created"); return '201 Created'; } @@ -323,7 +340,37 @@ class addressbook_groupdav extends groupdav_handler $result = $this->bo->search(array(),'MAX(contact_modified) AS contact_modified','','','','','',$filter); - return '"'.$result[0]['modified'].'"'; + $ctag = 'EGw-'.$result[0]['modified'].'-wGE'; + return $ctag; + } + + /** + * Add the privileges of the current user + * + * @param array $props=array() regular props by the groupdav handler + * @return array + */ + static function current_user_privilege_set(array $props=array()) + { + $props[] = HTTP_WebDAV_Server::mkprop(groupdav::DAV,'current-user-privilege-set', + array(HTTP_WebDAV_Server::mkprop(groupdav::DAV,'privilege', + array(//HTTP_WebDAV_Server::mkprop(groupdav::DAV,'all',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'read',''), + HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'read-free-busy',''), + //HTTP_WebDAV_Server::mkprop(groupdav::DAV,'read-current-user-privilege-set',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'bind',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'unbind',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-post',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-post-vevent',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-respond',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-respond-vevent',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-deliver',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-deliver-vevent',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write-properties',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write-content',''), + )))); + return $props; } /** @@ -345,11 +392,17 @@ class addressbook_groupdav extends groupdav_handler * @link http://www.mail-archive.com/calendarserver-users@lists.macosforge.org/msg01156.html * * @param array $props=array() regular props by the groupdav handler + * @param string $displayname * @param string $base_uri=null base url of handler * @return array */ - static function extra_properties(array $props=array(), $base_uri=null) + static function extra_properties(array $props=array(), $displayname, $base_uri=null) { + // addressbook description + $displayname = $GLOBALS['egw']->translation->convert(lang('Addressbook of') . ' ' . + $displayname, + $GLOBALS['egw']->translation->charset(),'utf-8'); + $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-description',$displayname); // supported reports (required property for CardDAV) $props[] = HTTP_WebDAV_Server::mkprop('supported-report-set',array( HTTP_WebDAV_Server::mkprop('supported-report',array( @@ -359,6 +412,7 @@ class addressbook_groupdav extends groupdav_handler HTTP_WebDAV_Server::mkprop('report', HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-multiget','')))), )); + //$props = self::current_user_privilege_set($props); return $props; } @@ -392,7 +446,7 @@ class addressbook_groupdav extends groupdav_handler { return '412 Precondition Failed'; } - return $ok; + //return $ok; } /** diff --git a/addressbook/inc/class.addressbook_so.inc.php b/addressbook/inc/class.addressbook_so.inc.php index 8eb0713c3e..42c6fe13cc 100755 --- a/addressbook/inc/class.addressbook_so.inc.php +++ b/addressbook/inc/class.addressbook_so.inc.php @@ -526,8 +526,8 @@ class addressbook_so /** * reads contact data including custom fields * - * @param int/string $contact_id contact_id or 'a'.account_id - * @return array/boolean data if row could be retrived else False + * @param int|string $contact_id contact_id or 'a'.account_id + * @return array|boolean data if row could be retrived else False */ function read($contact_id) { diff --git a/addressbook/inc/class.addressbook_vcal.inc.php b/addressbook/inc/class.addressbook_vcal.inc.php index c180240c71..bf2b48cb2f 100644 --- a/addressbook/inc/class.addressbook_vcal.inc.php +++ b/addressbook/inc/class.addressbook_vcal.inc.php @@ -123,7 +123,7 @@ class addressbook_vcal extends addressbook_bo */ function addVCard($_vcard, $_abID=null, $merge=false) { - if (!$contact = $this->vcardtoegw($_vcard, $_abID)) + if (!$contact = $this->vcardtoegw($_vcard)) { return false; } @@ -148,6 +148,15 @@ class addressbook_vcal extends addressbook_bo { $contact['account_id'] = $old_contact['account_id']; } + if (is_array($contact['category'])) + { + $contact['category'] = implode(',',$this->find_or_add_categories($contact['category'], $_abID)); + } + else + { + // restore from orignal + $contact['category'] = $old_contact['category']; + } } } // update entry @@ -160,6 +169,10 @@ class addressbook_vcal extends addressbook_bo { $contact['owner'] = $GLOBALS['egw_info']['user']['account_primary_group']; } + if (is_array($contact['category'])) + { + $contact['category'] = implode(',',$this->find_or_add_categories($contact['category'], -1)); + } } return $this->save($contact); } @@ -455,8 +468,13 @@ class addressbook_vcal extends addressbook_bo { $result = array(); - if (($contact = $this->vcardtoegw($_vcard, $contentID))) + if (($contact = $this->vcardtoegw($_vcard))) { + if (is_array($contact['category'])) + { + $contact['category'] = implode(',',$this->find_or_add_categories($contact['category'], + $contentID ? $contentID : -1)); + } if ($contentID) { $contact['id'] = $contentID; @@ -475,7 +493,7 @@ class addressbook_vcal extends addressbook_bo if (is_array($_supportedFields)) $this->supportedFields = $_supportedFields; } - function vcardtoegw($_vcard, $_abID=null) + function vcardtoegw($_vcard) { // the horde class does the charset conversion. DO NOT CONVERT HERE. // be as flexible as possible @@ -889,7 +907,7 @@ class addressbook_vcal extends addressbook_bo break; case 'cat_id': - $contact[$fieldName] = implode(',',$this->find_or_add_categories($vcardValues[$vcardKey]['values'], $_abID)); + $contact[$fieldName] = $vcardValues[$vcardKey]['values']; break; case 'jpegphoto': diff --git a/calendar/inc/class.calendar_groupdav.inc.php b/calendar/inc/class.calendar_groupdav.inc.php index faf5dfa574..cd27414acf 100644 --- a/calendar/inc/class.calendar_groupdav.inc.php +++ b/calendar/inc/class.calendar_groupdav.inc.php @@ -23,8 +23,6 @@ class calendar_groupdav extends groupdav_handler */ var $bo; - const DAV = 'DAV:'; - var $filter_prop2cal = array( 'SUMMARY' => 'cal_title', 'UID' => 'cal_uid', @@ -86,7 +84,7 @@ class calendar_groupdav extends groupdav_handler if (!is_array($event)) $event = $this->bo->read($event); $name = $event[self::PATH_ATTRIBUTE]; } - return '/calendar/'.$name.'.ics'; + return $name.'.ics'; } /** @@ -191,7 +189,7 @@ class calendar_groupdav extends groupdav_handler $props[] = HTTP_WebDAV_Server::mkprop('getcontentlength', ''); // expensive to calculate and no CalDAV client uses it } $files['files'][] = array( - 'path' => self::get_path($event), + 'path' => $path.self::get_path($event), 'props' => $props, ); } @@ -447,12 +445,19 @@ class calendar_groupdav extends groupdav_handler if ($this->debug) error_log(__METHOD__.print_r($event,true).function_backtrace()); return $event; } - $handler = $this->_get_handler(); + if (is_null($event) && !$this->bo->check_perms(EGW_ACL_ADD, 0, $user)) + { + // we have not permission on this user's calendar + if ($this->debug) error_log(__METHOD__."(,$user) we have not enough rights on this calendar"); + return '403 Forbidden'; + } + + $handler = $this->_get_handler(); $vCalendar = htmlspecialchars_decode($options['content']); if (!($cal_id = $handler->importVCal($vCalendar, is_numeric($id) ? $id : -1, - self::etag2value($this->http_if_match), false, 0, $this->principalURL))) + self::etag2value($this->http_if_match), false, 0, $this->principalURL, $user))) { if ($this->debug) error_log(__METHOD__."(,$id) importVCal($options[content]) returned false"); return '403 Forbidden'; @@ -461,15 +466,11 @@ class calendar_groupdav extends groupdav_handler header('ETag: '.$this->get_etag($cal_id)); if (is_null($event) || !$return_no_access) // let lightning think the event is added { + $path = preg_replace('|(.*)/[^/]*|', '\1/', $options['path']); if ($this->debug) error_log(__METHOD__."(,$id,$user) cal_id=$cal_id, is_null(\$event)=".(int)is_null($event)); - header('Location: '.$this->base_uri.self::get_path($cal_id)); + header('Location: '.$path.self::get_path($cal_id)); return '201 Created'; } - if (strpos($_SERVER[HTTP_USER_AGENT], 'Mac OS X') !== false) - { - //return '205 Reset Content'; // would be nicer - return '400 Event updated, please reload'; // Enforce a reload by iCal - } return true; } @@ -580,8 +581,7 @@ class calendar_groupdav extends groupdav_handler */ function read($id) { - //$cal_read = $this->bo->read($id,null,false,'server');//njv: do we actually get anything - if ($this->debug > 1) error_log("bo-ical read :$id:");//njv: + if ($this->debug > 1) error_log("bo-ical read :$id:"); return $this->bo->read($id,null,false,'server'); } @@ -622,12 +622,12 @@ class calendar_groupdav extends groupdav_handler { if ($recurrence['reference']) // ignore series master { - $etag .= ':'.substr($this->get_etag($recurrence),1,-1); + $etag .= ':'.substr($this->get_etag($recurrence),4,-4); } } } //error_log(__METHOD__ . "($entry[id] ($entry[etag]): $entry[title] --> etag=$etag"); - return '"'.$etag.'"'; + return 'EGw-'.$etag.'-wGE'; } /** @@ -650,23 +650,23 @@ class calendar_groupdav extends groupdav_handler */ static function current_user_privilege_set(array $props=array()) { - $props[] = HTTP_WebDAV_Server::mkprop(self::DAV,'current-user-privilege-set', - array(HTTP_WebDAV_Server::mkprop(self::DAV,'privilege', - array(//HTTP_WebDAV_Server::mkprop(self::DAV,'all',''), - HTTP_WebDAV_Server::mkprop(self::DAV,'read',''), + $props[] = HTTP_WebDAV_Server::mkprop(groupdav::DAV,'current-user-privilege-set', + array(HTTP_WebDAV_Server::mkprop(groupdav::DAV,'privilege', + array(//HTTP_WebDAV_Server::mkprop(groupdav::DAV,'all',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'read',''), HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'read-free-busy',''), - //HTTP_WebDAV_Server::mkprop(self::DAV,'read-current-user-privilege-set',''), - HTTP_WebDAV_Server::mkprop(self::DAV,'bind',''), - HTTP_WebDAV_Server::mkprop(self::DAV,'unbind',''), - HTTP_WebDAV_Server::mkprop(self::DAV,'schedule-post',''), - HTTP_WebDAV_Server::mkprop(self::DAV,'schedule-post-vevent',''), - HTTP_WebDAV_Server::mkprop(self::DAV,'schedule-respond',''), - HTTP_WebDAV_Server::mkprop(self::DAV,'schedule-respond-vevent',''), - HTTP_WebDAV_Server::mkprop(self::DAV,'schedule-deliver',''), - HTTP_WebDAV_Server::mkprop(self::DAV,'schedule-deliver-vevent',''), - HTTP_WebDAV_Server::mkprop(self::DAV,'write',''), - HTTP_WebDAV_Server::mkprop(self::DAV,'write-properties',''), - HTTP_WebDAV_Server::mkprop(self::DAV,'write-content',''), + //HTTP_WebDAV_Server::mkprop(groupdav::DAV,'read-current-user-privilege-set',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'bind',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'unbind',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-post',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-post-vevent',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-respond',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-respond-vevent',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-deliver',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'schedule-deliver-vevent',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write-properties',''), + HTTP_WebDAV_Server::mkprop(groupdav::DAV,'write-content',''), )))); return $props; } @@ -675,42 +675,41 @@ class calendar_groupdav extends groupdav_handler * Add extra properties for calendar collections * * @param array $props=array() regular props by the groupdav handler + * @param string $displayname * @param string $base_uri=null base url of handler * @return array */ - static function extra_properties(array $props=array(), $base_uri=null) + static function extra_properties(array $props=array(), $displayname, $base_uri=null) { // calendar description - $displayname = $GLOBALS['egw']->translation->convert(lang('Calendar of'). ' ' . - $GLOBALS['egw_info']['user']['account_fullname'], - $GLOBALS['egw']->translation->charset(),'utf-8'); $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-description',$displayname); // BOX URLs of the current user /* $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'schedule-inbox-URL', array(HTTP_WebDAV_Server::mkprop(self::DAV,'href',$base_uri.'/calendar/'))); $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'schedule-outbox-URL', - array(HTTP_WebDAV_Server::mkprop(self::DAV,'href',$base_uri.'/calendar/'))); + array(HTTP_WebDAV_Server::mkprop(groupdav::DAV,'href',$base_uri.'/calendar/'))); $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'schedule-default-calendar-URL', - array(HTTP_WebDAV_Server::mkprop(self::DAV,'href',$base_uri.'/calendar/'))); + array(HTTP_WebDAV_Server::mkprop(groupdav::DAV,'href',$base_uri.'/calendar/'))); $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALENDARSERVER,'dropbox-home-URL', - array(HTTP_WebDAV_Server::mkprop(self::DAV,'href',$base_uri.'/calendar/'))); + array(HTTP_WebDAV_Server::mkprop(groupdav::DAV,'href',$base_uri.'/calendar/'))); $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALENDARSERVER,'notifications-URL', - array(HTTP_WebDAV_Server::mkprop(self::DAV,'href',$base_uri.'/calendar/'))); + array(HTTP_WebDAV_Server::mkprop(groupdav::DAV,'href',$base_uri.'/calendar/'))); */ // email of the current user, see caldav-sheduling draft - $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set','MAILTO:'.$GLOBALS['egw_info']['user']['email']); + $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set',array( + HTTP_WebDAV_Server::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email']))); // supported components, currently only VEVENT $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'supported-calendar-component-set',array( HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VEVENT')), // HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VTODO')), // not yet supported )); - /* + $props[] = HTTP_WebDAV_Server::mkprop('supported-report-set',array( HTTP_WebDAV_Server::mkprop('supported-report',array( HTTP_WebDAV_Server::mkprop('report', HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-multiget')))))); - */ + //$props = self::current_user_privilege_set($props); return $props; } diff --git a/calendar/inc/class.calendar_ical.inc.php b/calendar/inc/class.calendar_ical.inc.php index 8742e6a63d..ad0439960f 100644 --- a/calendar/inc/class.calendar_ical.inc.php +++ b/calendar/inc/class.calendar_ical.inc.php @@ -1029,9 +1029,10 @@ class calendar_ical extends calendar_boupdate * @param int $recur_date=0 if set, import the recurrence at this timestamp, * default 0 => import whole series (or events, if not recurring) * @param string $principalURL='' Used for CalDAV imports + * @param int $user=null account_id of owner, default null * @return int|boolean cal_id > 0 on success, false on failure or 0 for a failed etag */ - function importVCal($_vcalData, $cal_id=-1, $etag=null, $merge=false, $recur_date=0, $principalURL='') + function importVCal($_vcalData, $cal_id=-1, $etag=null, $merge=false, $recur_date=0, $principalURL='', $user=null) { if (!is_array($this->supportedFields)) $this->setSupportedFields(); @@ -1081,7 +1082,7 @@ class calendar_ical extends calendar_boupdate if ($this->log) { error_log(__FILE__.'['.__LINE__.'] '.__METHOD__ - ."($cal_id, $etag, $recur_date, $principalURL)\n" + ."($cal_id, $etag, $recur_date, $principalURL, $user)\n" . array2string($event)."\n",3,$this->logfile); } $updated_id = false; @@ -1204,9 +1205,20 @@ class calendar_ical extends calendar_boupdate $event['non_blocking'] = 1; } + if (!is_null($user)) + { + if ($this->check_perms(EGW_ACL_ADD, 0, $user)) + { + $event['owner'] = $user; + } + else + { + return false; // no permission + } + } // check if an owner is set and the current user has add rights // for that owners calendar; if not set the current user - if (!isset($event['owner']) + elseif (!isset($event['owner']) || !$this->check_perms(EGW_ACL_ADD, 0, $event['owner'])) { $event['owner'] = $this->user; diff --git a/egw-pear/HTTP/WebDAV/Server.php b/egw-pear/HTTP/WebDAV/Server.php index 0a98a2bc01..70f1ca7a6a 100644 --- a/egw-pear/HTTP/WebDAV/Server.php +++ b/egw-pear/HTTP/WebDAV/Server.php @@ -59,6 +59,18 @@ class HTTP_WebDAV_Server */ var $client_require_href_as_url; + /** + * Set if client requires does not allow namespace redundacy. + * The XML Namespace specification does allow both + * But some clients can NOT deal with one or the other! + * + * @var boolean (client_refuses_redundand_namespace_declarations) + */ + var $crrnd = false; + + /** + + /** * URI path for this request * @@ -229,11 +241,18 @@ class HTTP_WebDAV_Server $this->$wrapper(); // call method by name } else { // method not found/implemented if ($this->_SERVER["REQUEST_METHOD"] == "LOCK") { - $this->http_status("412 Precondition failed"); + $error = '412 Precondition failed'; + ; } else { - $this->http_status("405 Method not allowed"); + $error = '405 Method not allowed'; header("Allow: ".join(", ", $this->_allow())); // tell client what's allowed } + $this->http_status($error); + echo "Error $error\n"; + echo "

$error

\n"; + echo "The requested could not by handled by this server.\n"; + echo '(URI ' . $this->_SERVER['REQUEST_URI'] . ")
\n
\n"; + echo "\n"; } } @@ -432,7 +451,7 @@ class HTTP_WebDAV_Server */ // }}} - // {{{ LOCK() + // {{{ ACL() /** * ACL implementation @@ -585,7 +604,7 @@ class HTTP_WebDAV_Server $options['other'] = $propinfo->other; // call user handler - if (!$this->$handler($options, $files)) { + if (!($retval =$this->$handler($options, $files))) { $files = array("files" => array()); if (method_exists($this, "checkLock")) { // is locked? @@ -611,27 +630,43 @@ class HTTP_WebDAV_Server } } - // now we generate the reply header ... - if ($propinfo->root['name'] == 'principal-search-property-set') + // now we generate the reply header ... + if ($retval === true) { - $this->http_status('200 OK'); + $this->http_status('207 Multi-Status'); } else { - $this->http_status('207 Multi-Status'); + $this->http_status($retval); + header('Content-Type: text/html'); + echo "Error $retval\n"; + echo "

$retval

\n"; + switch (substr($retval, 0 ,3)) + { + case '501': // Not Implemented + echo "The requested feature is not (yet) supported by this server.\n"; + break; + default: + echo "The request could not be handled by this server.\n"; + } + echo '(URI ' . $this->_SERVER['REQUEST_URI'] . ")
\n
\n"; + echo "\n"; + return; } + // dav header + $dav = array(1); // assume we are always dav class 1 compliant + $allow = false; + + // allow extending class to modify DAV + if (method_exists($this,'OPTIONS')) { + $this->OPTIONS($this->path,$dav,$allow); + } + header("DAV: " .join(", ", $dav)); header('Content-Type: text/xml; charset="utf-8"'); // ... and payload echo "\n"; - if ($propinfo->root['name'] == 'principal-search-property-set') - { - echo "\n"; - } - else - { - echo "\n"; - } + echo ($this->crrnd?'<':'\n"; // using an ArrayIterator to prevent foreach from copying the array, // as we cant loop by reference, when an iterator is given in $files['files'] @@ -748,7 +783,14 @@ class HTTP_WebDAV_Server $path = $file['path']; if (!is_string($path) || $path==="") continue; - if ($propinfo->root['name'] != 'principal-search-property-set') echo " \n"; + if ($this->crrnd) + { + echo " \n"; + } + else + { + echo " \n"; + } /* TODO right now the user implementation has to make sure collections end in a slash, this should be done in here @@ -756,15 +798,20 @@ class HTTP_WebDAV_Server // path needs to be urlencoded (only basic version of this class!) $href = $this->_urlencode($this->_mergePathes($this->base_uri, $path)); - if ($propinfo->root['name'] != 'principal-search-property-set') echo " $href\n"; + if ($this->crrnd) + { + echo " $href\n"; + } + else + { + echo " $href\n"; + } // report all found properties and their values (if any) if (isset($file["props"]) && is_array($file["props"])) { - if ($propinfo->root['name'] != 'principal-search-property-set') - { - echo " \n"; - echo " \n"; - } + echo ' <'.($this->crrnd?'':'D:')."propstat>\n"; + echo ' <'.($this->crrnd?'':'D:')."prop>\n"; + foreach ($file["props"] as &$prop) { if (!is_array($prop)) continue; @@ -773,7 +820,7 @@ class HTTP_WebDAV_Server if (!isset($prop["val"]) || $prop["val"] === "" || $prop["val"] === false) { // empty properties (cannot use empty() for check as "0" is a legal value here) if ($prop["ns"]=="DAV:") { - echo " \n"; + echo ' <'.($this->crrnd?'':'D:')."$prop[name]/>\n"; } else if (!empty($prop["ns"])) { echo " <".$ns_hash[$prop["ns"]].":$prop[name]/>\n"; } else { @@ -783,61 +830,34 @@ class HTTP_WebDAV_Server // some WebDAV properties need special treatment switch ($prop["name"]) { case "creationdate": - echo " " + echo ' <'.($this->crrnd?'':'D:')."creationdate ns0:dt=\"dateTime.tz\">" . gmdate("Y-m-d\\TH:i:s\\Z", $prop['val']) - . "\n"; + . 'crrnd?'':'D:')."creationdate>\n"; break; case "getlastmodified": - echo " " + echo ' <'.($this->crrnd?'':'D:')."getlastmodified ns0:dt=\"dateTime.rfc1123\">" . gmdate("D, d M Y H:i:s ", $prop['val']) - . "GMT\n"; + . "GMTcrrnd?'':'D:')."getlastmodified>\n"; break; - /* @Todo: breaks CalDAV - 2010/03/01 jlehrke - case "resourcetype": - if (!is_array($prop['val'])) { - echo ' \n"; - } else { // multiple resourcetypes from different namespaces as required by GroupDAV - $vals = $extra_ns = ''; - foreach($prop['val'] as $subprop) - { - if ($subprop['ns'] && $subprop['ns'] != 'DAV:') { - // register property namespace if not known yet - if (!isset($ns_hash[$subprop['ns']])) { - $ns_name = "ns".(count($ns_hash) + 1); - $ns_hash[$subprop['ns']] = $ns_name; - } else { - $ns_name = $ns_hash[$subprop['ns']]; - } - if (strchr($extra_ns,$extra=' xmlns:'.$ns_name.'="'.$subprop['ns'].'"') === false) { - $extra_ns .= $extra; - } - $ns_name .= ':'; - } elseif ($subprop['ns'] == 'DAV:') { - $ns_name = 'D:'; - } else { - $ns_name = ''; - } - $vals .= "<$ns_name$subprop[val]/>"; - } - echo " $vals\n"; - //error_log("resourcetype: $vals"); - } - break; - */ case "supportedlock": - echo " $prop[val]\n"; + echo ' <'.($this->crrnd?'':'D:')."supportedlock>$prop[val]crrnd?'':'D:')."supportedlock>\n"; break; case "lockdiscovery": - echo " \n"; + echo ' <'.($this->crrnd?'':'D:')."lockdiscovery>\n"; echo $prop["val"]; - echo " \n"; + echo ' crrnd?'':'D:')."lockdiscovery>\n"; break; default: - echo " ". - (is_array($prop['val']) ? - $this->_hierarchical_prop_encode($prop['val']) : - $this->_prop_encode(htmlspecialchars($prop['val']))). - "\n"; + if (is_array($prop['val'])) + { + $val = $this->_hierarchical_prop_encode($prop['val']); + } elseif (isset($prop['raw'])) { + $val = $this->_prop_encode(''); + } else { + $val = $this->_prop_encode(htmlspecialchars($prop['val'])); + } + echo ' <'.($this->crrnd?'':'D:')."$prop[name]>$val". + 'crrnd?'':'D:')."$prop[name]>\n"; break; } } else { @@ -850,53 +870,75 @@ class HTTP_WebDAV_Server $vals = $extra_ns = ''; foreach($prop['val'] as $subprop) { - if ($subprop['ns'] && $subprop['ns'] != 'DAV:') { - // register property namespace if not known yet - if (!isset($ns_hash[$subprop['ns']])) { - $ns_name = "ns".(count($ns_hash) + 1); - $ns_hash[$subprop['ns']] = $ns_name; - } else { - $ns_name = $ns_hash[$subprop['ns']]; - } - if (strchr($extra_ns,$extra=' xmlns:'.$ns_name.'="'.$subprop['ns'].'"') === false) { - $extra_ns .= $extra; - } - $ns_name .= ':'; - } elseif ($subprop['ns'] == 'DAV:') { - $ns_name = 'D:'; - } else { - $ns_name = ''; - } - $vals .= "<$ns_name$subprop[name]"; - if (is_array($subprop['val'])) // val contains only attributes, no value - { - foreach($subprop['val'] as $attr => $val) - { - $vals .= ' '.$attr.'="'.htmlspecialchars($val).'"'; - } - $vals .= '/>'; - } - else - { - $vals .= '>'.htmlspecialchars($subprop['val']).""; - } - } - echo " <".$ns_hash[$prop['ns']].":$prop[name]$extra_ns>$vals\n"; - } - else - // properties from namespaces != "DAV:" or without any namespace - if ($prop["ns"]) { - echo " <" . $ns_hash[$prop["ns"]] . ":$prop[name]>" - . $this->_prop_encode(htmlspecialchars($prop['val'])) - . "\n"; + if ($subprop['ns'] && $subprop['ns'] != 'DAV:') { + // register property namespace if not known yet + if (!isset($ns_hash[$subprop['ns']])) { + $ns_name = "ns".(count($ns_hash) + 1); + $ns_hash[$subprop['ns']] = $ns_name; + } else { + $ns_name = $ns_hash[$subprop['ns']]; + } + if (strchr($extra_ns,$extra=' xmlns:'.$ns_name.'="'.$subprop['ns'].'"') === false) { + $extra_ns .= $extra; + } + $ns_name .= ':'; + } elseif ($subprop['ns'] == 'DAV:') { + $ns_name = 'D:'; + } else { + $ns_name = ''; + } + $vals .= "<$ns_name$subprop[name]"; + if (is_array($subprop['val'])) // val contains only attributes, no value + { + foreach($subprop['val'] as $attr => $val) + { + $vals .= ' '.$attr.'="'.htmlspecialchars($val).'"'; + } + $vals .= '/>'; + } + else + { + $vals .= '>'; + if (isset($subprop['raw'])) { + $vals .= ''; + } else { + $vals .= htmlspecialchars($subprop['val']); + } + $vals .= ""; + } + } + echo ' <'.$ns_hash[$prop['ns']].":$prop[name]$extra_ns>$vals\n"; } else { - echo " <$prop[name] xmlns=\"\">" - . $this->_prop_encode(htmlspecialchars($prop['val'])) - . "\n"; + if ($prop['raw']) + { + $val = ''; + } else { + $val = htmlspecialchars($prop['val']); + } + $val = $this->_prop_encode($val); + // properties from namespaces != "DAV:" or without any namespace + if ($prop['ns']) { + if ($this->crrnd) { + echo " <$prop[name]> xmlns:".$ns_hash[$prop['ns']]."=".'"'.$prop["ns"].'">' + . $val . "\n"; + } else { + echo " <" . $ns_hash[$prop["ns"]] . ":$prop[name]>" + . $val . '\n"; + } + } else { + echo " <$prop[name] xmlns=\"\">$val\n"; + } } } } - if ($propinfo->root['name'] != 'principal-search-property-set') + + if ($this->crrnd) + { + echo " \n"; + echo " HTTP/1.1 200 OK\n"; + echo " \n"; + } + else { echo " \n"; echo " HTTP/1.1 200 OK\n"; @@ -906,12 +948,12 @@ class HTTP_WebDAV_Server // now report all properties requested but not found if (isset($file["noprops"])) { - echo " \n"; - echo " \n"; + echo ' <'.($this->crrnd?'':'D:')."propstat>\n"; + echo ' <'.($this->crrnd?'':'D:')."prop>\n"; foreach ($file["noprops"] as &$prop) { if ($prop["ns"] == "DAV:") { - echo " \n"; + echo ' <'.($this->crrnd?'':'D:')."$prop[name]/>\n"; } else if ($prop["ns"] == "") { echo " <$prop[name] xmlns=\"\"/>\n"; } else { @@ -919,23 +961,24 @@ class HTTP_WebDAV_Server } } - echo " \n"; - echo " HTTP/1.1 404 Not Found\n"; - echo " \n"; + if ($this->crrnd) + { + echo " \n"; + echo " HTTP/1.1 404 Not Found\n"; + echo " \n"; + } + else + { + echo " \n"; + echo " HTTP/1.1 404 Not Found\n"; + echo " \n"; + } } - if ($propinfo->root['name'] != 'principal-search-property-set') echo " \n"; - } - - if ($propinfo->root['name'] == 'principal-search-property-set') - { - echo "\n"; - } - else - { - echo "\n"; + echo ' crrnd?'':'D:')."response>\n"; } + echo 'crrnd?'':'D:')."multistatus>\n"; } @@ -973,24 +1016,24 @@ class HTTP_WebDAV_Server echo "\n"; echo "\n"; - echo " \n"; - echo " ".$this->_urlencode($this->_mergePathes($this->_SERVER["SCRIPT_NAME"], $this->path))."\n"; + echo ' <'.($this->crrnd?'':'D:')."response>\n"; + echo ' <'.($this->crrnd?'':'D:')."href>".$this->_urlencode($this->_mergePathes($this->_SERVER["SCRIPT_NAME"], $this->path)).'crrnd?'':'D:')."href>\n"; foreach ($options["props"] as $prop) { - echo " \n"; - echo " <$prop[name] xmlns=\"$prop[ns]\"/>\n"; - echo " HTTP/1.1 $prop[status]\n"; - echo " \n"; + echo ' <'.($this->crrnd?'':'D:')."propstat>\n"; + echo ' <'.($this->crrnd?'':'D:')."prop><$prop[name] xmlns=\"$prop[ns]\"/>crrnd?'':'D:')."prop>\n"; + echo ' <'.($this->crrnd?'':'D:')."status>HTTP/1.1 $prop[status]crrnd?'':'D:')."status>\n"; + echo ' crrnd?'':'D:')."propstat>\n"; } if ($responsedescr) { - echo " ". + echo ' <'.($this->crrnd?'':'D:')."responsedescription>". $this->_prop_encode(htmlspecialchars($responsedescr)). - "\n"; + 'crrnd?'':'D:')."responsedescription>\n"; } - echo " \n"; - echo "\n"; + echo ' crrnd?'':'D:')."response>\n"; + echo 'crrnd?'':'D:')."multistatus>\n"; } else { $this->http_status("423 Locked"); } @@ -1580,17 +1623,17 @@ class HTTP_WebDAV_Server header("Lock-Token: <$options[locktoken]>"); echo "\n"; echo "\n"; - echo " \n"; - echo " \n"; - echo " \n"; - echo " \n"; - echo " $options[depth]\n"; - echo " $options[owner]\n"; - echo " $timeout\n"; - echo " $options[locktoken]\n"; - echo " \n"; - echo " \n"; - echo "\n\n"; + echo ' <'.($this->crrnd?'':'D:')."lockdiscovery>\n"; + echo ' <'.($this->crrnd?'':'D:')."activelock>\n"; + echo ' <'.($this->crrnd?'':'D:')."lockscope>crrnd?'':'D:')."lockscope>\n"; + echo ' <'.($this->crrnd?'':'D:')."locktype>crrnd?'':'D:')."locktype>\n"; + echo ' <'.($this->crrnd?'':'D:')."depth>$options[depth]crrnd?'':'D:')."depth>\n"; + echo ' <'.($this->crrnd?'':'D:')."owner>$options[owner]crrnd?'':'D:')."owner>\n"; + echo ' <'.($this->crrnd?'':'D:')."timeout>$timeoutcrrnd?'':'D:')."timeout>\n"; + echo ' <'.($this->crrnd?'':'D:')."locktoken>$options[locktoken]crrnd?'':'D:')."locktoken>\n"; + echo ' crrnd?'':'D:')."activelock>\n"; + echo ' crrnd?'':'D:')."lockdiscovery>\n"; + echo 'crrnd?'':'D:')."prop>\n\n"; } } @@ -1627,9 +1670,9 @@ class HTTP_WebDAV_Server // }}} - // {{{ http_UNLOCK() + // {{{ http_ACL() - /** + /** * ACL method handler * * @param void @@ -1660,9 +1703,9 @@ class HTTP_WebDAV_Server $content .= "\n"; $content .= " \n"; foreach ($options['errors'] as $violation) { - $content .= "\n"; + $content .= '<'.($this->crrnd?'':'D:')."$violation/>\n"; } - $content .= "\n"; + $content .= 'crrnd?'':'D:')."error>\n"; } header("Content-length: ".$this->bytes($content)); if ($content) echo $options['content']; @@ -1765,20 +1808,27 @@ class HTTP_WebDAV_Server * @param string XML namespace (optional) * @param string property name * @param string property value + * @praram boolen property raw-flag * @return array property array */ function mkprop() { - $args = func_get_args(); - if (count($args) == 3) { - return array("ns" => $args[0], - "name" => $args[1], - "val" => $args[2]); - } else { - return array("ns" => "DAV:", - "name" => $args[0], - "val" => $args[1]); - } + $args = func_get_args(); + switch (count($args)) { + case 4: + return array('ns' => $args[0], + 'name' => $args[1], + 'val' => $args[2], + 'raw' => true); + case 3: + return array('ns' => $args[0], + 'name' => $args[1], + 'val' => $args[2]); + default: + return array("ns" => "DAV:", + "name" => $args[0], + "val" => $args[1]); + } } // {{{ _check_auth @@ -2125,16 +2175,32 @@ class HTTP_WebDAV_Server } // genreate response block - $activelocks.= " - - - - $lock[depth] - $lock[owner] - $timeout - $lock[token] - - "; + if ($this->crrnd) + { + $activelocks.= " + + <$lock[scope]/> + <$lock[type]/> + $lock[depth] + $lock[owner] + $timeout + $lock[token] + + "; + } + else + { + $activelocks.= " + + + + $lock[depth] + $lock[owner] + $timeout + $lock[token] + + "; + } } // return generated response diff --git a/egw-pear/HTTP/WebDAV/Server/Filesystem.php b/egw-pear/HTTP/WebDAV/Server/Filesystem.php index 48c30da8d1..b6b98fd691 100644 --- a/egw-pear/HTTP/WebDAV/Server/Filesystem.php +++ b/egw-pear/HTTP/WebDAV/Server/Filesystem.php @@ -186,7 +186,7 @@ class HTTP_WebDAV_Server_Filesystem extends HTTP_WebDAV_Server // type and size (caller already made sure that path exists) if (is_dir($fspath)) { // directory (WebDAV collection) - $info["props"][] = $this->mkprop("resourcetype", "collection"); + $info["props"][] = $this->mkprop("resourcetype", array($this->mkprop('collection', ''))); $info["props"][] = $this->mkprop("getcontenttype", "httpd/unix-directory"); } else { // plain file (WebDAV resource) diff --git a/infolog/inc/class.infolog_bo.inc.php b/infolog/inc/class.infolog_bo.inc.php index 097ca0d434..5eb9b1ad72 100644 --- a/infolog/inc/class.infolog_bo.inc.php +++ b/infolog/inc/class.infolog_bo.inc.php @@ -420,13 +420,13 @@ class infolog_bo /** * Read an infolog entry specified by $info_id * - * @param int/array $info_id integer id or array with key 'info_id' of the entry to read + * @param int|array $info_id integer id or array with key 'info_id' of the entry to read * @param boolean $run_link_id2from=true should link_id2from run, default yes, * need to be set to false if called from link-title to prevent an infinit recursion * @param string $date_format='ts' date-formats: 'ts'=timestamp, 'server'=timestamp in server-time, * 'array'=array or string with date-format * - * @return array/boolean infolog entry, null if not found or false if no permission to read it + * @return array|boolean infolog entry, null if not found or false if no permission to read it */ function &read($info_id,$run_link_id2from=true,$date_format='ts') { diff --git a/infolog/inc/class.infolog_groupdav.inc.php b/infolog/inc/class.infolog_groupdav.inc.php index 206bd0acef..ea102d49f7 100644 --- a/infolog/inc/class.infolog_groupdav.inc.php +++ b/infolog/inc/class.infolog_groupdav.inc.php @@ -57,7 +57,7 @@ class infolog_groupdav extends groupdav_handler if (!is_array($info)) $info = $this->bo->read($info); $name = $info[self::PATH_ATTRIBUTE]; } - return '/infolog/'.$name.'.ics'; + return $name.'.ics'; } /** @@ -73,6 +73,8 @@ class infolog_groupdav extends groupdav_handler { $starttime = microtime(true); + $myself = ($user == $GLOBALS['egw_info']['user']['account_id']); + if ($options['filters']) { @@ -113,14 +115,17 @@ class infolog_groupdav extends groupdav_handler $filter = array( 'info_type' => 'task', ); + + //if (!$myself) $filter['info_owner'] = $user; + if ($id) $filter['info_id'] = $id; // propfind on a single id // ToDo: add parameter to only return id & etag if (($tasks =& $this->bo->search($params=array( 'order' => 'info_datemodified', 'sort' => 'DESC', - 'filter' => 'own', // filter my: entries user is responsible for, - // filter own: entries the user own or is responsible for + 'filter' => ($myself ? 'own' : 'own'), // filter my: entries user is responsible for, + // filter own: entries the user own or is responsible for 'date_format' => 'server', 'col_filter' => $filter, )))) @@ -148,7 +153,7 @@ class infolog_groupdav extends groupdav_handler $props[] = HTTP_WebDAV_Server::mkprop('getcontentlength', ''); // expensive to calculate and no CalDAV client uses it } $files['files'][] = array( - 'path' => self::get_path($task), + 'path' => $path.self::get_path($task), 'props' => $props, ); } @@ -193,8 +198,11 @@ class infolog_groupdav extends groupdav_handler { return $ok; } + $handler = $this->_get_handler(); - if (!($info_id = $handler->importVTODO($options['content'],is_numeric($id) ? $id : -1))) + $vTodo = htmlspecialchars_decode($options['content']); + + if (!($info_id = $handler->importVTODO($vTodo,is_numeric($id) ? $id : -1, false, $user))) { if ($this->debug) error_log(__METHOD__."(,$id) import_vtodo($options[content]) returned false"); return '403 Forbidden'; @@ -202,7 +210,8 @@ class infolog_groupdav extends groupdav_handler header('ETag: '.$this->get_etag($info_id)); if (is_null($ok) || $id != $info_id) { - header('Location: '.$this->base_uri.self::get_path($info_id)); + $path = preg_replace('|(.*)/[^/]*|', '\1/', $options['path']); + header('Location: '.$path.self::get_path($info_id)); return '201 Created'; } return true; @@ -263,36 +272,38 @@ class infolog_groupdav extends groupdav_handler { return false; } - return '"'.$info['info_id'].':'.$info['info_datemodified'].'"'; + return 'EGw-'.$info['info_id'].':'.$info['info_datemodified'].'-wGE'; } /** * Add extra properties for calendar collections * * @param array $props=array() regular props by the groupdav handler + * @param string $displayname * @param string $base_uri=null base url of handler * @return array */ - static function extra_properties(array $props=array(), $base_uri=null) + static function extra_properties(array $props=array(), $displayname, $base_uri=null) { // calendar description $displayname = $GLOBALS['egw']->translation->convert(lang('Tasks of') . ' ' . - $GLOBALS['egw_info']['user']['account_fullname'], + $displayname, $GLOBALS['egw']->translation->charset(),'utf-8'); $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-description',$displayname); // email of the current user, see caldav-sheduling draft - $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set','MAILTO:'.$GLOBALS['egw_info']['user']['email']); + $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set',array( + HTTP_WebDAV_Server::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email']))); // supported components, currently only VEVENT $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'supported-calendar-component-set',array( // HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VEVENT')), HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'comp',array('name' => 'VTODO')), )); - /* + $props[] = HTTP_WebDAV_Server::mkprop('supported-report-set',array( HTTP_WebDAV_Server::mkprop('supported-report',array( HTTP_WebDAV_Server::mkprop('report', HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-multiget')))))); - */ + return $props; } diff --git a/infolog/inc/class.infolog_ical.inc.php b/infolog/inc/class.infolog_ical.inc.php index b169b996d9..97bbf77068 100644 --- a/infolog/inc/class.infolog_ical.inc.php +++ b/infolog/inc/class.infolog_ical.inc.php @@ -380,9 +380,10 @@ class infolog_ical extends infolog_bo * @param string $_vcalData * @param int $_taskID=-1 info_id, default -1 = new entry * @param boolean $merge=false merge data with existing entry + * @param int $user=null delegate new task to this account_id, default null * @return int|boolean integer info_id or false on error */ - function importVTODO(&$_vcalData, $_taskID=-1, $merge=false) + function importVTODO(&$_vcalData, $_taskID=-1, $merge=false, $user=null) { if (!($taskData = $this->vtodotoegw($_vcalData,$_taskID))) return false; @@ -397,6 +398,11 @@ class infolog_ical extends infolog_bo $taskData['info_datecompleted'] = 0; } + if (!is_null($user)) + { + $taskData['info_responsible'] = array($user); + } + if ($this->log) { error_log(__FILE__.'['.__LINE__.'] '.__METHOD__."()\n" . diff --git a/phpgwapi/inc/class.groupdav.inc.php b/phpgwapi/inc/class.groupdav.inc.php index 1d0b6e989d..d92a862288 100644 --- a/phpgwapi/inc/class.groupdav.inc.php +++ b/phpgwapi/inc/class.groupdav.inc.php @@ -36,6 +36,10 @@ require_once('HTTP/WebDAV/Server.php'); */ class groupdav extends HTTP_WebDAV_Server { + /** + * DAV namespace + */ + const DAV = 'DAV:'; /** * GroupDAV namespace */ @@ -107,6 +111,12 @@ class groupdav extends HTTP_WebDAV_Server * @var string */ var $principalURL; + /** + * Reference to the accounts class + * + * @var accounts + */ + var $accounts; function __construct() @@ -122,6 +132,8 @@ class groupdav extends HTTP_WebDAV_Server case 'davkit': // iCal app in OS X 10.6 created wrong request, if full url given $this->client_require_href_as_url = false; break; + case 'cfnetwork': + $this->crrnd = true; // Apple Addressbook.app does not cope with namespace redundancy } parent::HTTP_WebDAV_Server(); @@ -136,6 +148,8 @@ class groupdav extends HTTP_WebDAV_Server $this->principalURL = (@$_SERVER["HTTPS"] === "on" ? "https:" : "http:") . '//' . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'] . '/'; } + $this->principalURL .= 'principals/users/'.$GLOBALS['egw_info']['user']['account_lid'].'/'; + $this->accounts = $GLOBALS['egw']->accounts; } /** @@ -162,12 +176,24 @@ class groupdav extends HTTP_WebDAV_Server switch($app) { case 'calendar': - case 'infolog': + $dav[] = 2; + $dav[] = 'access-control'; $dav[] = 'calendar-access'; + //$dav[] = 'calendar-schedule'; + //$dav[] = 'calendar-proxy'; + //$dav[] = 'calendar-avialibility'; + //$dav[] = 'calendarserver-private-events'; break; case 'addressbook': - $dav[] = 'addressbook'; + $dav[] = 2; + $dav[] = 3; + $dav[] = 'access-control'; + $dav[] = 'addressbook-access'; break; + default: + $dav[] = 2; + $dav[] = 'access-control'; + $dav[] = 'calendar-access'; } // not yet implemented: $dav[] = 'access-control'; } @@ -179,77 +205,101 @@ class groupdav extends HTTP_WebDAV_Server * @param array return array for file properties * @return bool true on success */ - function PROPFIND(&$options, &$files,$method='PROPFIND') + function PROPFIND(&$options, &$files, $method='PROPFIND') { if ($this->debug) error_log(__CLASS__."::$method(".array2string($options,true).')'); + + if (groupdav_handler::get_agent() == 'cfnetwork' && // Apple Addressbook + $options['root']['name'] == 'propfind') + { + foreach ($options['props'] as $props) + { + if ($props['name'] == 'current-user-privilege-set') + { + if ($this->debug > 2) error_log(__CLASS__."::$method: current-user-privilege-set not implemented!"); + return '501 Not Implemented'; + } + } + } // parse path in form [/account_lid]/app[/more] if (!self::_parse_path($options['path'],$id,$app,$user,$user_prefix) && $app && !$user) { - if ($this->debug > 1) error_log(__CLASS__."::$method: user=$user, app=$app, id=$id: 404 not found!"); + if ($this->debug > 1) error_log(__CLASS__."::$method: user='$user', app='$app', id='$id': 404 not found!"); return '404 Not Found'; } - if ($this->debug > 1) error_log(__CLASS__."::$method: user=$user, app='$app', id=$id"); + if ($this->debug > 1) error_log(__CLASS__."::$method: user='$user', app='$app', id='$id'"); + + if ($user) + { + $account_lid = $this->accounts->id2name($user); + } + else + { + $account_lid = $GLOBALS['egw_info']['user']['account_lid']; + } + $account = $this->accounts->read($account_lid); + $displayname = $GLOBALS['egw']->translation->convert($account['account_fullname'], + $GLOBALS['egw']->translation->charset(),'utf-8'); $files = array('files' => array()); + $path = $user_prefix = $this->_slashify($user_prefix); - if (!$app) // root folder containing apps + if (!$app) // user root folder containing apps { if (empty($user_prefix)) + { + $user_prefix = '/'.$GLOBALS['egw_info']['user']['account_lid'].'/'; + } + if ($options['depth']) { $displayname = 'EGroupware (Cal|Card|Group)DAV server'; } - else - { - $displayname = $this->translation->convert($GLOBALS['egw_info']['user']['account_fullname'],$this->egw_charset,'utf-8'); - } // self url - $files['files'][] = array( - 'path' => $user_prefix.'/', - 'props' => array( + $props = array( self::mkprop('displayname',$displayname), - self::mkprop('resourcetype','collection'), + self::mkprop('resourcetype',array(self::mkprop('collection',''))), // adding the calendar extra property (calendar-home-set, etc.) here, allows apple iCal to "autodetect" the URL - self::mkprop(groupdav::CALDAV,'calendar-home-set',array(self::mkprop('href',$this->base_uri.'/calendar/'))), + self::mkprop(groupdav::CALDAV,'calendar-home-set',array( + self::mkprop('href',$this->base_uri.$user_prefix.'calendar/'))), + self::mkprop(groupdav::CARDDAV,'addressbook-home-set',array( + self::mkprop('href',$this->base_uri.$user_prefix))), self::mkprop('current-user-principal',array(self::mkprop('href',$this->principalURL))), - self::mkprop(groupdav::CALDAV,'calendar-user-address-set','MAILTO:'.$GLOBALS['egw_info']['user']['email']), + self::mkprop(groupdav::CALDAV,'calendar-user-address-set',array( + self::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email']))), //self::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))), //self::mkprop('principal-collection-set',array(self::mkprop('href',$this->base_uri.'/principals/'))), - ), + ); + //$props = self::current_user_privilege_set($props); + $files['files'][] = array( + 'path' => $path, + 'props' => $props, ); if ($options['depth']) { - if (empty($user_prefix)) + if (strlen($path) == 1) // GroupDAV Root { // principals collection $files['files'][] = array( 'path' => '/principals/', 'props' => array( self::mkprop('displayname',lang('Accounts')), - self::mkprop('resourcetype','collection'), + self::mkprop('resourcetype',array(self::mkprop('collection',''))), self::mkprop('current-user-principal',array(self::mkprop('href',$this->principalURL))), - self::mkprop(groupdav::CALDAV,'calendar-home-set',array(self::mkprop('href',$this->base_uri.'/calendar/'))), - //self::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))), + self::mkprop(groupdav::CALDAV,'calendar-home-set',array( + self::mkprop('href',$this->base_uri.$user_prefix.'calendar/'))), + self::mkprop(groupdav::CARDDAV,'addressbook-home-set',array( + self::mkprop('href',$this->base_uri.'/'))), + self::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))), ), ); - // groups collection - $files['files'][] = array( - 'path' => '/groups/', - 'props' => array( - self::mkprop('displayname',lang('Groups')), - self::mkprop('resourcetype','collection'), - self::mkprop('current-user-principal',array(self::mkprop('href',$this->principalURL))), - self::mkprop(groupdav::CALDAV,'calendar-home-set',array(self::mkprop('href',$this->base_uri.'/calendar/'))), - //self::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))), - ), - ); } foreach($this->root as $app => $data) { if (!$GLOBALS['egw_info']['user']['apps'][$app]) continue; // no rights for the given app $files['files'][] = array( - 'path' => $user_prefix.'/'.$app.'/', + 'path' => $path.$app.'/', 'props' => $this->_properties($app,false,$user), ); } @@ -266,9 +316,9 @@ class groupdav extends HTTP_WebDAV_Server if ($method != 'REPORT' && !$id) // no self URL for REPORT requests (only PROPFIND) or propfinds on an id { $files['files'][0] = array( - 'path' => '/'.$app.'/', + 'path' => $path.$app.'/', // KAddressbook doubles the folder, if the self URL contains the GroupDAV/CalDAV resourcetypes - 'props' => $this->_properties($app,$app=='addressbook'&&strpos($_SERVER['HTTP_USER_AGENT'],'KHTML') !== false), + 'props' => $this->_properties($app,$app=='addressbook'&&strpos($_SERVER['HTTP_USER_AGENT'],'KHTML') !== false,$user), ); } if (isset($options['depth']) && !$options['depth'] && !$id) @@ -281,7 +331,7 @@ class groupdav extends HTTP_WebDAV_Server } return true; // depth 0 --> show only the self url } - return $handler->propfind($options['path'],$options,$files,$user,$id); + return $handler->propfind($this->_slashify($options['path']),$options,$files,$user,$id); } return '501 Not Implemented'; } @@ -296,25 +346,52 @@ class groupdav extends HTTP_WebDAV_Server */ function _properties($app,$no_extra_types=false,$user=null) { - if (!$user) $user = $GLOBALS['egw_info']['user']['account_fullname']; - $displayname = $this->translation->convert($GLOBALS['egw_info']['user']['account_fullname'],$this->egw_charset,'utf-8'); + if ($this->debug) error_log(__CLASS__."::$method: user='$user', app='$app'"); + if ($user) + { + $account_lid = $this->accounts->id2name($user); + } + else + { + $account_lid = $GLOBALS['egw_info']['user']['account_lid']; + } + $account = $this->accounts->read($account_lid); + $displayname = $GLOBALS['egw']->translation->convert($account['account_fullname'], + $GLOBALS['egw']->translation->charset(),'utf-8'); $props = array( self::mkprop('current-user-principal',array(self::mkprop('href',$this->principalURL))), self::mkprop('owner',$displayname), - //self::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))), - //self::mkprop('principal-collection-set',array(self::mkprop('href',$this->base_uri.'/principals/'))), + self::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))), + self::mkprop('alternate-URI-set',array( + self::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email']))), + self::mkprop(groupdav::CALDAV,'calendar-user-address-set',array( + self::mkprop('href','MAILTO:'.$GLOBALS['egw_info']['user']['email']))), + self::mkprop('principal-collection-set',array( + self::mkprop('href',$this->base_uri.'/principals/users/'), + self::mkprop('href',$this->base_uri.'/principals/groups/'), + )), ); switch ($app) { case 'calendar': - $props[] = self::mkprop(groupdav::CALDAV,'calendar-home-set',array(self::mkprop('href',$this->base_uri.'/calendar/'))); + $props[] = self::mkprop(groupdav::CALDAV,'calendar-home-set',array( + self::mkprop('href',$this->base_uri.'/'.$account_lid.'/calendar/'))); break; case 'infolog': - $props[] = self::mkprop(groupdav::CALDAV,'calendar-home-set',array(self::mkprop('href',$this->base_uri.'/infolog/'))); + $props[] = self::mkprop(groupdav::CALDAV,'calendar-home-set',array( + self::mkprop('href',$this->base_uri.'/'.$account_lid.'/infolog/'))); + $displayname = $this->translation->convert(lang($app).' '. + common::grab_owner_name($user),$this->egw_charset,'utf-8'); + break; default: - $displayname = $this->translation->convert(lang($app).' '.common::grab_owner_name($user),$this->egw_charset,'utf-8'); + $props[] = self::mkprop(groupdav::CALDAV,'calendar-home-set',array( + self::mkprop('href',$this->base_uri.'/'.$account_lid.'/calendar/'))); + $displayname = $this->translation->convert(lang($app).' '. + common::grab_owner_name($user),$this->egw_charset,'utf-8'); } + $props[] = self::mkprop(groupdav::CARDDAV,'addressbook-home-set',array( + self::mkprop('href',$this->base_uri.'/'.$account_lid.'/'))); $props[] = self::mkprop('displayname',$displayname); foreach((array)$this->root[$app] as $prop => $values) @@ -343,7 +420,9 @@ class groupdav extends HTTP_WebDAV_Server } if (method_exists($app.'_groupdav','extra_properties')) { - $props = ExecMethod2($app.'_groupdav::extra_properties',$props,$this->base_uri); + $displayname = $GLOBALS['egw']->translation->convert($account['account_fullname'], + $GLOBALS['egw']->translation->charset(),'utf-8'); + $props = ExecMethod2($app.'_groupdav::extra_properties',$props,$displayname,$this->base_uri); } return $props; } @@ -448,8 +527,7 @@ class groupdav extends HTTP_WebDAV_Server $props = $this->props2array($file['props']); //echo $file['path']; _debug_array($props); $class = $class == 'row_on' ? 'row_off' : 'row_on'; - $name = $this->_slashify(basename($this->_unslashify($file['path']))); - /* + if (substr($file['path'],-1) == '/') { $name = basename(substr($file['path'],0,-1)).'/'; @@ -458,7 +536,7 @@ class groupdav extends HTTP_WebDAV_Server { $name = basename($file['path']); } - */ + echo "\t\n\t\t$n\n\t\t".html::a_href(htmlspecialchars($name),'/groupdav.php'.$file['path'])."\n"; echo "\t\t".$props['DAV:getcontentlength']."\n"; echo "\t\t".(!empty($props['DAV:getlastmodified']) ? date('Y-m-d H:i:s',$props['DAV:getlastmodified']) : '')."\n"; @@ -761,7 +839,7 @@ class groupdav extends HTTP_WebDAV_Server } $parts = explode('/', $this->_unslashify($path)); - if ($GLOBALS['egw']->accounts->name2id($parts[0])) + if ($this->accounts->name2id($parts[0])) { // /$user/$app/... $user = array_shift($parts); @@ -772,7 +850,7 @@ class groupdav extends HTTP_WebDAV_Server if ($user) { $user_prefix = '/'.$user; - $user = $GLOBALS['egw']->accounts->name2id($user,'account_lid',$app != 'addressbook' ? 'u' : null); + $user = $this->accounts->name2id($user,'account_lid',$app != 'addressbook' ? 'u' : null); } else { @@ -785,13 +863,39 @@ class groupdav extends HTTP_WebDAV_Server list($id) = explode('.',$id); // remove evtl. .ics extension } - if (!($ok = $id && in_array($app,array('addressbook','calendar','infolog','principals','groups')) && $user)) + $ok = $id && $user && in_array($app,array('addressbook','calendar','infolog','principals','groups')); + if ($this->debug) { - if ($this->debug) - { - error_log(__METHOD__."('$path') returning false: id=$id, app='$app', user=$user"); - } + error_log(__METHOD__."('$path') returning " . ($ok ? 'true' : 'false') . ": id='$id', app='$app', user='$user', user_prefix='$user_prefix'"); } return $ok; } + /** + * Add the privileges of the current user + * + * @param array $props=array() regular props by the groupdav handler + * @return array + */ + static function current_user_privilege_set(array $props=array()) + { + $props[] = HTTP_WebDAV_Server::mkprop('current-user-privilege-set', + array(HTTP_WebDAV_Server::mkprop('privilege', + array(//HTTP_WebDAV_Server::mkprop('all',''), + HTTP_WebDAV_Server::mkprop('read',''), + HTTP_WebDAV_Server::mkprop('read-free-busy',''), + //HTTP_WebDAV_Server::mkprop('read-current-user-privilege-set',''), + HTTP_WebDAV_Server::mkprop('bind',''), + HTTP_WebDAV_Server::mkprop('unbind',''), + HTTP_WebDAV_Server::mkprop('schedule-post',''), + HTTP_WebDAV_Server::mkprop('schedule-post-vevent',''), + HTTP_WebDAV_Server::mkprop('schedule-respond',''), + HTTP_WebDAV_Server::mkprop('schedule-respond-vevent',''), + HTTP_WebDAV_Server::mkprop('schedule-deliver',''), + HTTP_WebDAV_Server::mkprop('schedule-deliver-vevent',''), + HTTP_WebDAV_Server::mkprop('write',''), + HTTP_WebDAV_Server::mkprop('write-properties',''), + HTTP_WebDAV_Server::mkprop('write-content',''), + )))); + return $props; + } } diff --git a/phpgwapi/inc/class.groupdav_handler.inc.php b/phpgwapi/inc/class.groupdav_handler.inc.php index c0c90c505b..4fd0c3400a 100644 --- a/phpgwapi/inc/class.groupdav_handler.inc.php +++ b/phpgwapi/inc/class.groupdav_handler.inc.php @@ -37,6 +37,12 @@ abstract class groupdav_handler * @var translation */ var $translation; + /** + * Reference to the accounts class + * + * @var accounts + */ + var $accounts; /** * Translates method names into ACL bits * @@ -88,7 +94,6 @@ abstract class groupdav_handler */ function __construct($app,$debug=null,$base_uri=null,$principalURL=null) { - //error_log(__METHOD__." called"); $this->app = $app; if (!is_null($debug)) $this->debug = $debug; $this->base_uri = is_null($base_uri) ? $base_uri : $_SERVER['SCRIPT_NAME']; @@ -99,13 +104,15 @@ abstract class groupdav_handler } else { - $this->principalURL = $principalURL; + $this->principalURL = $principalURL.'principals/users/'. + $GLOBALS['egw_info']['user']['account_lid'].'/'; } $this->agent = self::get_agent(); $this->translation =& $GLOBALS['egw']->translation; $this->egw_charset = $this->translation->charset(); + $this->accounts = $GLOBALS['egw']->accounts; } /** @@ -122,12 +129,13 @@ abstract class groupdav_handler /** * Propfind callback, if interator is used * + * @param string $path * @param array $filter * @param array|boolean $start false=return all or array(start,num) * @param int &$total * @return array with "files" array with values for keys path and props */ - function &propfind_callback(array $filter,$start,&$total) { } + function &propfind_callback($path, array $filter,$start,&$total) { } /** * Handle get request for an applications entry @@ -178,10 +186,11 @@ abstract class groupdav_handler * Add extra properties for collections * * @param array $props=array() regular props by the groupdav handler + * @param string $displayname * @param string $base_uri=null base url of handler * @return array */ - static function extra_properties(array $props=array(), $base_uri=null) + static function extra_properties(array $props=array(), $displayname, $base_uri=null) { return $props; } @@ -203,7 +212,7 @@ abstract class groupdav_handler // error_log(__METHOD__."(".array2string($entry).") Cant create etag!"); return false; } - return '"'.$entry['id'].':'.(isset($entry['etag']) ? $entry['etag'] : $entry['modified']).'"'; + return 'EGw-'.$entry['id'].':'.(isset($entry['etag']) ? $entry['etag'] : $entry['modified']).'-wGE'; } /** @@ -214,7 +223,7 @@ abstract class groupdav_handler */ static function etag2value($etag) { - list(,$val) = explode(':',substr($etag,1,-1),2); + list(,$val) = explode(':',substr($etag,4,-4),2); return $val; } @@ -278,6 +287,7 @@ abstract class groupdav_handler * * @static * @param string $app 'calendar', 'addressbook' or 'infolog' + * @param int $user=null owner of the collection, default current user * @param int $debug=null debug-level to set * @param string $base_uri=null base url of handler * @param string $principalURL=null pricipal url of handler @@ -297,7 +307,9 @@ abstract class groupdav_handler $handler_cache[$app]->$debug = $debug; $handler_cache[$app]->$base_uri = $base_uri; $handler_cache[$app]->$principalURL = $principalURL; - if ($debug) error_log(__METHOD__."($app, $base_uri, $principalURL)"); + + if ($debug) error_log(__METHOD__."('$app', '$base_uri', '$principalURL')"); + return $handler_cache[$app]; } @@ -317,6 +329,7 @@ abstract class groupdav_handler $user_agent = strtolower($_SERVER['HTTP_USER_AGENT']); foreach(array( 'davkit' => 'davkit', // Apple iCal + 'cfnetwork' => 'cfnetwork', // Apple Addressbook 'bionicmessage.net' => 'funambol', // funambol GroupDAV connector from bionicmessage.net 'zideone' => 'zideone', // zideone outlook plugin 'lightning' => 'lightning', // Lighting (SOGo connector for addressbook) @@ -356,6 +369,13 @@ abstract class groupdav_handler */ class groupdav_propfind_iterator implements Iterator { + /** + * current path + * + * @var string + */ + protected $path; + /** * Handler to call for entries * @@ -375,8 +395,16 @@ class groupdav_propfind_iterator implements Iterator * * @var array */ + protected $common_files; + + /** + * current chunk + * + * @var array + */ protected $files; + /** * Start value for callback * @@ -397,6 +425,8 @@ class groupdav_propfind_iterator implements Iterator */ public $debug = false; + /** + /** * Constructor * @@ -404,13 +434,14 @@ class groupdav_propfind_iterator implements Iterator * @param array $filter filter for propfind call * @param array $files=null extra files/responses to return too */ - public function __construct(groupdav_handler $handler,array $filter,array &$files=null) + public function __construct(groupdav_handler $handler, $path, array $filter,array &$files=null) { - if ($this->debug) error_log(__METHOD__."(,".array2string($filter).",)"); - + if ($this->debug) error_log(__METHOD__."('$path', ".array2string($filter).",)"); + $this->path = $path; $this->handler = $handler; $this->filter = $filter; $this->files = $files; + $this->common_files = $files; reset($this->files); } @@ -422,7 +453,6 @@ class groupdav_propfind_iterator implements Iterator public function current() { if ($this->debug) error_log(__METHOD__."() returning ".array2string(current($this->files))); - return current($this->files); } @@ -447,21 +477,20 @@ class groupdav_propfind_iterator implements Iterator if (next($this->files) !== false) { if ($this->debug) error_log(__METHOD__."() returning TRUE"); - return true; } - if (is_array($this->files) && count($this->files) < self::CHUNK_SIZE) // less entries then asked --> no further available + if (is_array($this->files) && count($this->files) < self::CHUNK_SIZE) // less entries then asked --> no further available { if ($this->debug) error_log(__METHOD__."() returning FALSE (no more entries)"); - return false; // no further entries } // try query further files via propfind callback of handler and store result in $this->files - $this->files = $this->handler->propfind_callback($this->filter,array($this->start,self::CHUNK_SIZE)); + $this->files = $this->handler->propfind_callback($this->path,$this->filter,array($this->start,self::CHUNK_SIZE)); $this->start += self::CHUNK_SIZE; reset($this->files); if ($this->debug) error_log(__METHOD__."() returning ".array2string(current($this->files) !== false)); + return current($this->files) !== false; } @@ -474,7 +503,8 @@ class groupdav_propfind_iterator implements Iterator // query first set of files via propfind callback of handler and store result in $this->files $this->start = 0; - $this->files = $this->handler->propfind_callback($this->filter,array($this->start,self::CHUNK_SIZE)); + $files = $this->handler->propfind_callback($this->path,$this->filter,array($this->start,self::CHUNK_SIZE)); + $this->files = $this->common_files + $files; $this->start += self::CHUNK_SIZE; reset($this->files); } diff --git a/phpgwapi/inc/class.groupdav_principals.inc.php b/phpgwapi/inc/class.groupdav_principals.inc.php index 61d49e426b..ab767243bc 100644 --- a/phpgwapi/inc/class.groupdav_principals.inc.php +++ b/phpgwapi/inc/class.groupdav_principals.inc.php @@ -7,7 +7,7 @@ * @package api * @subpackage groupdav * @author Ralf Becker - * @copyright (c) 2008 by Ralf Becker + * @copyright (c) 2008-10 by Ralf Becker * @version $Id$ */ @@ -16,12 +16,6 @@ */ class groupdav_principals extends groupdav_handler { - /** - * Reference to the accounts class - * - * @var accounts - */ - var $accounts; /** * Constructor @@ -34,8 +28,6 @@ class groupdav_principals extends groupdav_handler function __construct($app,$debug=null,$base_uri=null,$principalURL=null) { parent::__construct($app,$debug,$base_uri,$principalURL); - - $this->accounts = $GLOBALS['egw']->accounts; } /** @@ -49,10 +41,41 @@ class groupdav_principals extends groupdav_handler */ function propfind($path,$options,&$files,$user) { - if ($this->debug) error_log(__METHOD__."($path,".array2string($options).",,$user,$id)"); + list(,$principals,$type,$name,$rest) = explode('/',$path,5); + // /principals/users/$name/ + // /users/$name/calendar-proxy-read/ + // /users/$name/calendar-proxy-write/ + // /groups/$name/ + // /resources/$resource/ + // /__uids__/$uid/.../ + + switch($type) + { + case 'users': + $files['files'] = $this->propfind_users($name,$rest,$options); + break; + case 'groups': + $files['files'] = $this->propfind_groups($name,$rest,$options); + break; + case 'resources': + $files['files'] = $this->propfind_resources($name,$rest,$options); + break; + case '__uids__': + $files['files'] = $this->propfind_uids($name,$rest,$options); + break; + case '': + $files['files'] = $this->propfind_principals($options); + break; + default: + return '404 Not Found'; + } + if (!is_array($files['files'])) + { + return $files['files']; + } + return true; list(,,$id) = explode('/',$path); - if ($id && !($id = $this->accounts->id2name($id))) { return false; @@ -61,31 +84,18 @@ class groupdav_principals extends groupdav_handler { $displayname = $GLOBALS['egw']->translation->convert($account['account_fullname'], $GLOBALS['egw']->translation->charset(),'utf-8'); - if ($options['root']['name'] == 'principal-search-property-set') + $props = array( + HTTP_WebDAV_Server::mkprop('displayname',$displayname), + HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($account)), + HTTP_WebDAV_Server::mkprop('resourcetype','principal'), + HTTP_WebDAV_Server::mkprop('alternate-URI-set',''), + HTTP_WebDAV_Server::mkprop('principal-URL',$this->base_uri.'/principals/'.$account['account_lid']), + HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',$this->base_uri.$account['account_lid'].'/calendar/'), + HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set','MAILTO:'.$account['account_email']), + ); + foreach($this->accounts->memberships($account['account_id']) as $gid => $group) { - $props = array(HTTP_WebDAV_Server::mkprop('principal-search-property', - array(HTTP_WebDAV_Server::mkprop('prop', - array(HTTP_WebDAV_Server::mkprop('displayname',$displayname)) - ), - HTTP_WebDAV_Server::mkprop('description', 'Full name'))) - ); - } - else - { - $props = array( - HTTP_WebDAV_Server::mkprop('displayname',$displayname), - HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($account)), - HTTP_WebDAV_Server::mkprop('resourcetype','principal'), - HTTP_WebDAV_Server::mkprop('alternate-URI-set',''), - HTTP_WebDAV_Server::mkprop('current-user-principal',array(HTTP_WebDAV_Server::mkprop('href',$this->principalURL))), - HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',array(HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/calendar/'))), - HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set','MAILTO:'.$account['account_email']), - //HTTP_WebDAV_Server::mkprop('principal-URL',array(HTTP_WebDAV_Server::mkprop('href',$this->principalURL))), - ); - foreach($this->accounts->memberships($account['account_id']) as $gid => $group) - { - $props[] = HTTP_WebDAV_Server::mkprop('group-membership',$this->base_uri.'/groups/'.$group); - } + $props[] = HTTP_WebDAV_Server::mkprop('group-membership',$this->base_uri.'/groups/'.$group); } $files['files'][] = array( 'path' => '/principals/'.$account['account_lid'], @@ -93,7 +103,250 @@ class groupdav_principals extends groupdav_handler ); if ($this->debug > 1) error_log(__METHOD__."($path) path=/principals/".$account['account_lid'].', props='.array2string($props)); } - return true; + return files; + } + + /** + * Do propfind in /pricipals/users + * + * @param string $name name of account or empty + * @param string $rest rest of path behind account-name + * @param array $options + * @return array|string array with files or HTTP error code + */ + protected function propfind_users($name,$rest,array $options) + { + //echo "

".__METHOD__."($name,$rest,".array2string($options).")

\n"; + if (empty($name)) + { + $files = array(); + // add /pricipals/users/ entry + $files[] = $this->add_collection('/principals/users/',array( + HTTP_WebDAV_Server::mkprop('current-user-principal',array( + HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/principals/'.$GLOBALS['egw_info']['user']['account_lid'].'/'))), + )); + if ($options['depth']) + { + // add all users + foreach($this->accounts->search(array('type' => 'accounts')) as $account) + { + $files[] = $this->add_account($account); + } + } + } + else + { + if (!($id = $GLOBALS['egw']->accounts->name2id($name,'account_lid','u')) || + !($account = $GLOBALS['egw']->accounts->read($id))) + { + return '404 Not Found'; + } + switch((string)$rest) + { + case '': + $files[] = $this->add_account($account); + $files[] = $this->add_collection('/principals/users/'.$account['account_lid'].'/calendar-proxy-read'); + $files[] = $this->add_collection('/principals/users/'.$account['account_lid'].'/calendar-proxy-write'); + break; + case 'calendar-proxy-read': + case 'calendar-proxy-write': + $files = array(); + $files[] = $this->add_collection('/principals/users/'.$account['account_lid'].'/'.$rest); + // add proxys + break; + default: + return '404 Not Found'; + } + } + return $files; + } + + /** + * Do propfind in /pricipals/groups + * + * @param string $name name of group or empty + * @param string $rest rest of path behind account-name + * @param array $options + * @return array|string array with files or HTTP error code + */ + protected function propfind_groups($name,$rest,array $options) + { + //echo "

".__METHOD__."($name,$rest,".array2string($options).")

\n"; + if (empty($name)) + { + $files = array(); + // add /pricipals/users/ entry + $files[] = $this->add_collection('/principals/groups/',array( + HTTP_WebDAV_Server::mkprop('current-user-principal',array( + HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/principals/'.$GLOBALS['egw_info']['user']['account_lid'].'/'))), + )); + if ($options['depth']) + { + // add all users + foreach($this->accounts->search(array('type' => 'groups')) as $account) + { + $files[] = $this->add_group($account); + } + } + } + else + { + if (!($id = $GLOBALS['egw']->accounts->name2id($name,'account_lid','g')) || + !($account = $GLOBALS['egw']->accounts->read($id))) + { + return '404 Not Found'; + } + switch((string)$rest) + { + case '': + $files[] = $this->add_group($account); + $files[] = $this->add_collection('/principals/groups/'.$account['account_lid'].'/calendar-proxy-read'); + $files[] = $this->add_collection('/principals/groups/'.$account['account_lid'].'/calendar-proxy-write'); + break; + case 'calendar-proxy-read': + case 'calendar-proxy-write': + $files = array(); + $files[] = $this->add_collection('/principals/groups/'.$account['account_lid'].'/'.$rest); + // add proxys + break; + default: + return '404 Not Found'; + } + } + return $files; + } + + /** + * Add collection of a single account to a collection + * + * @param array $account + * @return array with values for keys 'path' and 'props' + */ + protected function add_account(array $account) + { + //echo "

".__METHOD__."(".array2string($account).")

\n"; + $displayname = $GLOBALS['egw']->translation->convert($account['account_fullname'], + $GLOBALS['egw']->translation->charset(),'utf-8'); + $memberships = array(); + foreach($this->accounts->memberships($account['account_id']) as $gid => $group) + { + if ($group) + { + $memberships[] = HTTP_WebDAV_Server::mkprop('href', + $this->base_uri.'/principals/groups/'.$group); + } + } + $props = array( + HTTP_WebDAV_Server::mkprop('displayname',$displayname), + HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($account)), + HTTP_WebDAV_Server::mkprop('resourcetype','principal'), + HTTP_WebDAV_Server::mkprop('alternate-URI-set',array( + HTTP_WebDAV_Server::mkprop('href','MAILTO:'.$account['account_email']))), + HTTP_WebDAV_Server::mkprop('principal-URL',$this->base_uri.'/principals/users/'.$account['account_lid'].'/'), + HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',array( + HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/calendar/'))), + HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-user-address-set',array( + HTTP_WebDAV_Server::mkprop('href','MAILTO:'.$account['account_email']))), + HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-home-set',array( + HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/'))), + HTTP_WebDAV_Server::mkprop('group-member-ship', $memberships), + HTTP_WebDAV_Server::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))), + ); + if ($this->debug > 1) error_log(__METHOD__."($path) path=/principals/users/".$account['account_lid'].', props='.array2string($props)); + return array( + 'path' => '/principals/users/'.$account['account_lid'].'/', + 'props' => $props, + ); + } + + /** + * Add collection of a single group to a collection + * + * @param array $account + * @return array with values for keys 'path' and 'props' + */ + protected function add_group(array $account) + { + $displayname = $GLOBALS['egw']->translation->convert(lang('Group').' '.$account['account_lid'], + $GLOBALS['egw']->translation->charset(),'utf-8'); + $members = array(); + foreach($this->accounts->members($account['account_id']) as $gid => $user) + { + if ($user) + { + $members[] = HTTP_WebDAV_Server::mkprop('href', + $this->base_uri.'/principals/users/'.$user); + } + } + $props = array( + HTTP_WebDAV_Server::mkprop('displayname',$displayname), + HTTP_WebDAV_Server::mkprop('getetag',$this->get_etag($account)), + HTTP_WebDAV_Server::mkprop('resourcetype','principal'), + HTTP_WebDAV_Server::mkprop('alternate-URI-set',''), + HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-home-set',array( + HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/calendar/'))), + HTTP_WebDAV_Server::mkprop(groupdav::CARDDAV,'addressbook-home-set',array( + HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/'.$account['account_lid'].'/'))), + HTTP_WebDAV_Server::mkprop('group-member-set', $members), + //HTTP_WebDAV_Server::mkprop('principal-URL',array(self::mkprop('href',$this->principalURL))), + ); + $files['files'][] = array( + 'path' => '/principals/groups/'.$account['account_lid'].'/', + 'props' => $props, + ); + if ($this->debug > 1) error_log(__METHOD__."($path) path=/principals/groups/".$account['account_lid'].', props='.array2string($props)); + return array( + 'path' => '/principals/groups/'.$account['account_lid'].'/', + 'props' => $props, + ); + } + + /** + * Add a collection + * + * @param string $path + * @param array $props=array() extra properties 'resourcetype' is added anyway + * @return array + */ + protected function add_collection($path,$props=array()) + { + //echo "

".__METHOD__."($path,".array($props).")

\n"; + $props[] = HTTP_WebDAV_Server::mkprop('resourcetype',array( + HTTP_WebDAV_Server::mkprop('collection',''), + HTTP_WebDAV_Server::mkprop('resourcetype','principal'), + )); + return array( + 'path' => $path, + 'props' => $props, + ); + } + + /** + * Do propfind of /pricipals + * + * @param string $name name of group or empty + * @param string $rest name of rest of path behind group-name + * @param array $options + * @return array|string array with files or HTTP error code + */ + protected function propfind_principals(array $options) + { + //echo "

".__METHOD__."(".array($options).")

\n"; + $files = array(); + $files[] = $this->add_collection('/principals/',array( + HTTP_WebDAV_Server::mkprop('current-user-principal',array( + HTTP_WebDAV_Server::mkprop('href',$this->base_uri.'/principals/users/'.$GLOBALS['egw_info']['user']['account_lid'].'/'))), + )); + + if ($options['depth']) + { + $options['depth'] = 0; + $files = array_merge($files,$this->propfind_users('','',$options)); + $files = array_merge($files,$this->propfind_groups('','',$options)); + //$files = array_merge($this->propfind_resources('','',$options)); + //$files = array_merge($this->propfind_uids('','',$options)); + } + return $files; } /** @@ -109,12 +362,12 @@ class groupdav_principals extends groupdav_handler { return $account; } - $displayname = $GLOBALS['egw']->translation->convert( + $name = $GLOBALS['egw']->translation->convert( trim($account['account_firstname'].' '.$account['account_lastname']), $GLOBALS['egw']->translation->charset(),'utf-8'); $options['data'] = 'Principal: '.$account['account_lid']. "\nURL: ".$this->base_uri.$options['path']. - "\nName: ".$displayname. + "\nName: ".$name. "\nEmail: ".$account['account_email']. "\nMemberships: ".implode(', ',$this->accounts->memberships($id))."\n"; $options['mimetype'] = 'text/plain; charset=utf-8'; @@ -156,7 +409,8 @@ class groupdav_principals extends groupdav_handler */ function read($id) { - return $this->accounts->read($id); + return false; + //return $this->accounts->read($id); } /** @@ -191,6 +445,6 @@ class groupdav_principals extends groupdav_handler { $account = $this->read($account); } - return '"'.$account['account_id'].':'.md5(serialize($account)).'"'; + return 'EGw-'.$account['account_id'].':'.md5(serialize($account)).'-wGE'; } } \ No newline at end of file diff --git a/phpgwapi/inc/class.vfs_webdav_server.inc.php b/phpgwapi/inc/class.vfs_webdav_server.inc.php index d1b60fa737..12d425b8bc 100644 --- a/phpgwapi/inc/class.vfs_webdav_server.inc.php +++ b/phpgwapi/inc/class.vfs_webdav_server.inc.php @@ -263,7 +263,8 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem // type and size (caller already made sure that path exists) if (is_dir($fspath)) { // directory (WebDAV collection) - $info['props'][] = HTTP_WebDAV_Server::mkprop ('resourcetype', 'collection'); + $info['props'][] = HTTP_WebDAV_Server::mkprop ('resourcetype', array( + HTTP_WebDAV_Server::mkprop('collection', ''))); $info['props'][] = HTTP_WebDAV_Server::mkprop ('getcontenttype', 'httpd/unix-directory'); } else { // plain file (WebDAV resource)