diff --git a/groupdav.php b/groupdav.php index fb76f619c8..2d123f4c0d 100644 --- a/groupdav.php +++ b/groupdav.php @@ -44,4 +44,4 @@ $headertime = microtime(true); $groupdav = new groupdav(); $groupdav->ServeRequest(); -//error_log(sprintf("GroupDAV %s request took %5.3f s (header include took %5.3f s)",$_SERVER['REQUEST_METHOD'],microtime(true)-$starttime,$headertime-$starttime)); +error_log(sprintf('GroupDAV %s request: status "%s", took %5.3f s'.($headertime?' (header include took %5.3f s)':''),$_SERVER['REQUEST_METHOD'].($_SERVER['REQUEST_METHOD']=='REPORT'?' '.$groupdav->propfind_options['root']['name']:'').' '.$_SERVER['PATH_INFO'],$groupdav->_http_status,microtime(true)-$starttime,$headertime-$starttime)); diff --git a/phpgwapi/inc/class.groupdav.inc.php b/phpgwapi/inc/class.groupdav.inc.php index 6cc3858edd..93abe779fb 100644 --- a/phpgwapi/inc/class.groupdav.inc.php +++ b/phpgwapi/inc/class.groupdav.inc.php @@ -277,32 +277,40 @@ class groupdav extends HTTP_WebDAV_Server */ function OPTIONS($path, &$dav, &$allow) { - list(,$app) = explode('/',$path); - switch($app) + if (preg_match('#/(calendar|inbox|outbox)/#', $path)) { - case 'calendar': - if (!in_array(2,$dav)) $dav[] = 2; - $dav[] = 'access-control'; - $dav[] = 'calendar-access'; - $dav[] = 'calendar-auto-schedule'; - $dav[] = 'calendar-proxy'; - //$dav[] = 'calendar-availibility'; - //$dav[] = 'calendarserver-private-events'; - break; - case 'addressbook': - if (!in_array(2,$dav)) $dav[] = 2; - //$dav[] = 3; // revision aka versioning support not implemented - $dav[] = 'access-control'; - $dav[] = 'addressbook'; // CardDAV uses "addressbook" NOT "addressbook-access" - break; - default: // used eg. for root, and needs all above settings, as some clients only use these! - if (!in_array(2,$dav)) $dav[] = 2; - $dav[] = 'access-control'; - $dav[] = 'calendar-access'; - $dav[] = 'calendar-auto-schedule'; - $dav[] = 'calendar-proxy'; - $dav[] = 'addressbook'; + $app = 'calendar'; } + elseif (strpos($path, '/addressbook/') !== false) + { + $app = 'addressbook'; + } + // CalDAV and CardDAV + if (!in_array(2,$dav)) $dav[] = 2; + $dav[] = 'access-control'; + + if ($app !== 'addressbook') // CalDAV + { + $dav[] = 'calendar-access'; + $dav[] = 'calendar-auto-schedule'; + $dav[] = 'calendar-proxy'; + // required by iOS iCal to use principal-property-search to autocomplete participants (and locations) + $dav[] = 'calendarserver-principal-property-search'; + // other capabilities calendarserver announces + //$dav[] = 'calendar-schedule'; + //$dav[] = 'calendar-availability'; + //$dav[] = 'inbox-availability'; + //$dav[] = 'calendarserver-private-events'; + //$dav[] = 'calendarserver-private-comments'; + //$dav[] = 'calendarserver-sharing'; + //$dav[] = 'calendarserver-sharing-no-scheduling'; + + } + elseif ($app !== 'calendar') // CardDAV + { + $dav[] = 'addressbook'; // CardDAV uses "addressbook" NOT "addressbook-access" + } + //error_log(__METHOD__."('$path') --> app='$app' --> DAV: ".implode(', ', $dav)); } /** @@ -502,7 +510,7 @@ class groupdav extends HTTP_WebDAV_Server $ret = false; foreach($this->propfind_options['props'] as $prop) { - if ($prop['name'] == $name && (is_null($ns) || $prop['ns'] == $ns)) + if ($prop['name'] == $name && (is_null($ns) || $prop['xmlns'] == $ns)) { $ret = true; break; @@ -858,6 +866,11 @@ class groupdav extends HTTP_WebDAV_Server } echo "\n"; + $dav = array(1); + $allow = false; + $this->OPTIONS($options['path'], $dav, $allow); + echo "

DAV: ".implode(', ', $dav)."

\n"; + echo "\n\n"; common::egw_exit(); diff --git a/phpgwapi/inc/class.groupdav_principals.inc.php b/phpgwapi/inc/class.groupdav_principals.inc.php index d171946c75..ffeb2bd303 100644 --- a/phpgwapi/inc/class.groupdav_principals.inc.php +++ b/phpgwapi/inc/class.groupdav_principals.inc.php @@ -49,6 +49,9 @@ class groupdav_principals extends groupdav_handler 'principal-property-search' => array( 'method' => 'principal_property_search_report', ), + 'principal-search-property-set' => array( + 'method' => 'principal_search_property_set_report', + ), /*'expand-property' => array( // an other report calendarserver announces ),*/ @@ -189,6 +192,43 @@ class groupdav_principals extends groupdav_handler * but interprets returning all principals (all have a matching calendar-home-set) as NOT supporting CalDAV scheduling * --> search only current user's principal, when Lightning searches for calendar-home-set * + * Example from iOS iCal autocompleting invitees using calendarserver-principal-property-search WebDAV extension + * + * + * + * + * + * beck + * + * + * + * + * + * beck + * + * + * + * + * + * beck + * + * + * + * + * + * beck + * + * + * + * + * + * + * + * + * + * + * + * * @param string $path * @param array $options * @param array &$files @@ -223,7 +263,7 @@ class groupdav_principals extends groupdav_handler if (isset($property_search) && is_array($search_props[$property_search])) { $search_props[$property_search]['match'] = $prop['data']; - // optional match-type: "contains" (default), "starts-with" + // optional match-type: "contains" (default), "starts-with", "ends-with", "equals" $search_props[$property_search]['match-type'] = $prop['attrs']['match-type']; } break; @@ -314,7 +354,7 @@ class groupdav_principals extends groupdav_handler * * @param string $value value to test * @param string $match criteria/sub-string - * @param string $match_type='contains' or 'starts-with' + * @param string $match_type='contains' 'starts-with', 'ends-with' or 'equals' */ private static function match($value, $match, $match_type='contains') { @@ -335,6 +375,109 @@ class groupdav_principals extends groupdav_handler } } + /** + * Handle principal-search-property-set report + * + * REPORT /principals/ HTTP/1.1 + * + * + * + * + * + * + * + * + * + * Display Name + * + * + * + * + * + * Email Addresses + * + * + * + * + * + * Last Name + * + * + * + * + * + * Calendar User Type + * + * + * + * + * + * First Name + * + * + * + * + * + * Calendar User Address Set + * + * + * + * @param string $path + * @param array $options + * @param array &$files + * @param int $user account_id + * @return mixed boolean true on success, false on failure or string with http status (eg. '404 Not Found') + */ + function principal_search_property_set_report($path,&$options,&$files,$user) + { + static $search_props = array( + // from iOS iCal + 'displayname' => 'Display Name', + 'email-address-set' => array('description' => 'Email Addresses', 'ns' => groupdav::CALENDARSERVER), + 'last-name' => array('description' => 'Last Name', 'ns' => groupdav::CALENDARSERVER), + 'calendar-user-type' => array('description' => 'Calendar User Type', 'ns' => groupdav::CALENDARSERVER), + 'first-name' => array('description' => 'First Name', 'ns' => groupdav::CALENDARSERVER), + 'calendar-user-address-set' => array('description' => 'Calendar User Address Set', 'ns' => groupdav::CALENDARSERVER), + // Lightning + 'calendar-home-set' => array('description' => 'Calendar Home Set', 'ns' => groupdav::CALENDARSERVER), + // others, we generally support all properties of the principal + ); + header('Content-type: text/xml; charset=UTF-8'); + + $xml = new XMLWriter; + $xml->openMemory(); + $xml->setIndent(true); + $xml->startDocument('1.0', 'UTF-8'); + $xml->startElementNs(null, 'principal-search-property-set', 'DAV:'); + + foreach($search_props as $name => $data) + { + $xml->startElement('principal-search-property'); + $xml->startElement('prop'); + if (is_array($data) && !empty($data['ns'])) + { + $xml->writeElementNs(null, $name, $data['ns']); + } + else + { + $xml->writeElement($name); + } + $xml->endElement(); // prop + + $xml->startElement('description'); + $xml->writeAttribute('xml:lang', 'en'); + $xml->text(is_array($data) ? $data['description'] : $data); + $xml->endElement(); // description + + $xml->endElement(); // principal-search-property + } + $xml->endElement(); // principal-search-property-set + $xml->endDocument(); + echo $xml->outputMemory(); + + common::egw_exit(); + } + /** * Do propfind in /pricipals/users *