12 require_once(
'AwlCache.php');
13 require_once(
'AwlQuery.php');
14 require_once(
'DAVPrincipal.php');
15 require_once(
'DAVTicket.php');
16 require_once(
'iCalendar.php');
39 protected $unique_tag;
54 protected $resourcetypes;
59 protected $contenttype;
64 protected $bound_from;
84 private $_is_collection;
89 private $_is_principal;
94 private $_is_calendar;
104 private $_is_external;
109 private $_is_addressbook;
114 private $_is_proxy_resource;
124 private $supported_methods;
129 private $supported_reports;
134 private $dead_properties;
139 private $supported_components;
156 $this->exists =
null;
161 $this->collection =
null;
162 $this->principal =
null;
163 $this->parent =
null;
164 $this->resourcetypes =
null;
165 $this->contenttype =
null;
166 $this->privileges =
null;
167 $this->dead_properties =
null;
168 $this->supported_methods =
null;
169 $this->supported_reports =
null;
171 $this->_is_collection =
false;
172 $this->_is_principal =
false;
173 $this->_is_calendar =
false;
174 $this->_is_binding =
false;
175 $this->_is_external =
false;
176 $this->_is_addressbook =
false;
177 $this->_is_proxy_resource =
false;
179 if ( isset($prefetched_collection) ) {
180 $this->collection = $prefetched_collection;
183 if ( isset($parameters) && is_object($parameters) ) {
186 else if ( isset($parameters) && is_array($parameters) ) {
187 if ( isset($parameters[
'path']) ) {
188 $this->
FromPath($parameters[
'path']);
191 else if ( isset($parameters) && is_string($parameters) ) {
204 if ( $row ==
null )
return;
206 $this->exists =
true;
208 $this->
bound_from = (isset($row->bound_from)? $row->bound_from : $row->dav_name);
209 $this->_is_collection = preg_match(
'{/$}', $this->
dav_name );
211 if ( $this->_is_collection ) {
212 $this->contenttype =
'httpd/unix-directory';
213 $this->collection = (object) array();
216 $this->_is_principal = preg_match(
'{^/[^/]+/$}', $this->
dav_name );
217 if ( preg_match(
'#^(/principals/[^/]+/[^/]+)/?$#', $this->
dav_name, $matches) ) {
218 $this->collection->dav_name = $matches[1].
'/';
219 $this->collection->type =
'principal_link';
220 $this->_is_principal =
true;
225 if ( isset($row->dav_id) ) $this->
resource_id = $row->dav_id;
228 dbg_error_log(
'DAVResource',
':FromRow: Named "%s" is%s a collection.', $this->
dav_name, ($this->_is_collection?
'':
' not') );
230 foreach( $row AS $k => $v ) {
231 if ( $this->_is_collection )
232 $this->collection->{$k} = $v;
241 case 'resourcetypes':
242 if ( $this->_is_collection ) $this->{$k} = $v;
252 if ( $this->_is_collection ) {
253 if ( !isset( $this->collection->type ) || $this->collection->type ==
'collection' ) {
254 if ( $this->_is_principal )
255 $this->collection->type =
'principal';
256 else if ( $row->is_calendar ==
't' ) {
257 $this->collection->type =
'calendar';
259 else if ( $row->is_addressbook ==
't' ) {
260 $this->collection->type =
'addressbook';
262 else if ( isset($row->is_proxy) && $row->is_proxy ==
't' ) {
263 $this->collection->type =
'proxy';
265 else if ( preg_match(
'#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
266 $this->collection->type =
'schedule-'. $matches[3].
'box';
267 else if ( $this->dav_name ==
'/' )
268 $this->collection->type =
'root';
270 $this->collection->type =
'collection';
273 $this->_is_calendar = ($this->collection->is_calendar ==
't');
274 $this->_is_addressbook = ($this->collection->is_addressbook ==
't');
275 $this->_is_proxy_resource = ($this->collection->type ==
'proxy');
276 if ( $this->_is_principal && !isset($this->resourcetypes) ) {
277 $this->resourcetypes =
'<DAV::collection/><DAV::principal/>';
279 else if ( $this->_is_proxy_resource ) {
280 $this->resourcetypes = $this->collection->resourcetypes;
281 preg_match(
'#^/[^/]+/calendar-proxy-(read|write)/?[^/]*$#', $this->dav_name, $matches );
282 $this->proxy_type = $matches[1];
284 if ( isset($this->collection->dav_displayname) ) $this->collection->displayname = $this->collection->dav_displayname;
287 $this->resourcetypes =
'';
288 if ( isset($this->resource->caldav_data) ) {
289 if ( isset($this->resource->summary) )$this->resource->displayname = $this->resource->summary;
290 if ( strtoupper(substr($this->resource->caldav_data,0,15)) ==
'BEGIN:VCALENDAR' ) {
291 $this->contenttype =
'text/calendar';
292 if ( isset($this->resource->caldav_type) ) $this->contenttype .=
"; component=" . strtolower($this->resource->caldav_type);
293 if ( !$this->HavePrivilegeTo(
'read') && $this->HavePrivilegeTo(
'read-free-busy') ) {
294 $vcal =
new iCalComponent($this->resource->caldav_data);
295 $confidential = $vcal->CloneConfidential();
296 $this->resource->caldav_data = $confidential->Render();
297 $this->resource->displayname = $this->resource->summary = translate(
'Busy');
298 $this->resource->description =
null;
299 $this->resource->location =
null;
300 $this->resource->url =
null;
303 if ( isset($this->resource->class) && strtoupper($this->resource->class)==
'CONFIDENTIAL' && !$this->HavePrivilegeTo(
'all') && $session->user_no != $this->resource->user_no ) {
304 $vcal =
new iCalComponent($this->resource->caldav_data);
305 $confidential = $vcal->CloneConfidential();
306 $this->resource->caldav_data = $confidential->Render();
308 if ( isset($c->hide_alarm) && $c->hide_alarm && !$this->HavePrivilegeTo(
'write') ) {
309 $vcal1 =
new iCalComponent($this->resource->caldav_data);
310 $comps = $vcal1->GetComponents();
311 $vcal2 =
new iCalComponent();
313 foreach( $comps AS $comp ) {
314 $comp->ClearComponents(
'VALARM');
315 $vcal2->AddComponent($comp);
317 $this->resource->displayname = $this->resource->summary = $vcal2->GetPValue(
'SUMMARY');
318 $this->resource->caldav_data = $vcal2->Render();
322 else if ( strtoupper(substr($this->resource->caldav_data,0,11)) ==
'BEGIN:VCARD' ) {
323 $this->contenttype =
'text/vcard';
325 else if ( strtoupper(substr($this->resource->caldav_data,0,11)) ==
'BEGIN:VLIST' ) {
326 $this->contenttype =
'text/x-vlist';
340 $this->dav_name = DeconstructURL($inpath);
342 $this->FetchCollection();
343 if ( $this->_is_collection ) {
344 if ( $this->_is_principal || $this->collection->type ==
'principal' ) $this->FetchPrincipal();
347 $this->FetchResource();
349 dbg_error_log(
'DAVResource',
':FromPath: Path "%s" is%s a collection%s.',
350 $this->dav_name, ($this->_is_collection?
' '.$this->resourcetypes:
' not'), ($this->_is_principal?
' and a principal':
'') );
354 private function ReadCollectionFromDatabase() {
357 $this->collection = (object) array(
358 'collection_id' => -1,
359 'type' =>
'nonexistent',
360 'is_calendar' =>
false,
'is_principal' =>
false,
'is_addressbook' =>
false
363 $base_sql =
'SELECT collection.*, path_privs(:session_principal::int8, collection.dav_name,:scan_depth::int), ';
364 $base_sql .=
'p.principal_id, p.type_id AS principal_type_id, ';
365 $base_sql .=
'p.displayname AS principal_displayname, p.default_privileges AS principal_default_privileges, ';
366 $base_sql .=
'timezones.vtimezone ';
367 $base_sql .=
'FROM collection LEFT JOIN principal p USING (user_no) ';
368 $base_sql .=
'LEFT JOIN timezones ON (collection.timezone=timezones.tzid) ';
369 $base_sql .=
'WHERE ';
370 $sql = $base_sql .
'collection.dav_name = :raw_path ';
371 $params = array(
':raw_path' => $this->dav_name,
':session_principal' => $session->principal_id,
':scan_depth' => $c->permission_scan_depth );
372 if ( !preg_match(
'#/$#', $this->dav_name ) ) {
373 $sql .=
' OR collection.dav_name = :up_to_slash OR collection.dav_name = :plus_slash ';
374 $params[
':up_to_slash'] = preg_replace(
'#[^/]*$#',
'', $this->dav_name);
375 $params[
':plus_slash'] = $this->dav_name.
'/';
377 $sql .=
'ORDER BY LENGTH(collection.dav_name) DESC LIMIT 1';
378 $qry =
new AwlQuery( $sql, $params );
379 if ( $qry->Exec(
'DAVResource') && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
380 $this->collection = $row;
381 $this->collection->exists =
true;
382 if ( $row->is_calendar ==
't' )
383 $this->collection->type =
'calendar';
384 else if ( $row->is_addressbook ==
't' )
385 $this->collection->type =
'addressbook';
386 else if ( preg_match(
'#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
387 $this->collection->type =
'schedule-'. $matches[3].
'box';
389 $this->collection->type =
'collection';
391 else if ( preg_match(
'{^( ( / ([^/]+) / ) \.(in|out)/ ) [^/]*$}x', $this->dav_name, $matches ) ) {
393 $params = array(
':username' => $matches[3],
':parent_container' => $matches[2],
':dav_name' => $matches[1] );
394 $params[
':boxname'] = ($matches[4] ==
'in' ?
' Inbox' :
' Outbox');
395 $this->collection_type =
'schedule-'. $matches[4].
'box';
396 $params[
':resourcetypes'] = sprintf(
'<DAV::collection/><urn:ietf:params:xml:ns:caldav:%s/>', $this->collection_type );
398 INSERT INTO collection ( user_no, parent_container, dav_name, dav_displayname, is_calendar, created, modified, dav_etag, resourcetypes )
399 VALUES( (SELECT user_no FROM usr WHERE username = text(:username)),
400 :parent_container, :dav_name,
401 (SELECT fullname FROM usr WHERE username = text(:username)) || :boxname,
402 FALSE, current_timestamp, current_timestamp,
'1', :resourcetypes )
404 $qry =
new AwlQuery( $sql, $params );
405 $qry->Exec(
'DAVResource');
406 dbg_error_log(
'DAVResource',
'Created new collection as "%s".', trim($params[
':boxname']) );
408 $params = array(
':raw_path' => $this->dav_name,
':session_principal' => $session->principal_id,
':scan_depth' => $c->permission_scan_depth );
409 $qry =
new AwlQuery( $base_sql .
' dav_name = :raw_path', $params );
410 if ( $qry->Exec(
'DAVResource') && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
411 $this->collection = $row;
412 $this->collection->exists =
true;
413 $this->collection->type = $this->collection_type;
416 else if ( preg_match(
'#^(/([^/]+)/calendar-proxy-(read|write))/?[^/]*$#', $this->dav_name, $matches ) ) {
417 $this->collection->type =
'proxy';
418 $this->_is_proxy_resource =
true;
419 $this->proxy_type = $matches[3];
420 $this->collection->dav_name = $this->dav_name;
421 $this->collection->dav_displayname = sprintf(
'%s proxy %s', $matches[2], $matches[3] );
422 $this->collection->exists =
true;
423 $this->collection->parent_container =
'/' . $matches[2] .
'/';
425 else if ( preg_match(
'#^(/[^/]+)/?$#', $this->dav_name, $matches)
426 || preg_match(
'#^((/principals/[^/]+/)[^/]+)/?$#', $this->dav_name, $matches) ) {
427 $this->_is_principal =
true;
428 $this->FetchPrincipal();
429 $this->collection->is_principal =
true;
430 $this->collection->type =
'principal';
432 else if ( $this->dav_name ==
'/' ) {
433 $this->collection->dav_name =
'/';
434 $this->collection->type =
'root';
435 $this->collection->exists =
true;
436 $this->collection->displayname = $c->system_name;
437 $this->collection->default_privileges = (1 | 16 | 32);
438 $this->collection->parent_container =
'/';
442 SELECT collection.*, path_privs(:session_principal::int8, collection.dav_name,:scan_depth::int), p.principal_id,
443 p.type_id AS principal_type_id, p.displayname AS principal_displayname, p.default_privileges AS principal_default_privileges,
444 timezones.vtimezone, dav_binding.access_ticket_id, dav_binding.parent_container AS bind_parent_container,
445 dav_binding.dav_displayname, owner.dav_name AS bind_owner_url, dav_binding.dav_name AS bound_to,
446 dav_binding.external_url AS external_url, dav_binding.type AS external_type, dav_binding.bind_id AS bind_id
448 LEFT JOIN collection ON (collection.collection_id=bound_source_id)
449 LEFT JOIN principal p USING (user_no)
450 LEFT JOIN dav_principal owner ON (dav_binding.dav_owner_id=owner.principal_id)
451 LEFT JOIN timezones ON (collection.timezone=timezones.tzid)
452 WHERE dav_binding.dav_name = :raw_path
454 $params = array( ':raw_path' => $this->dav_name, ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
455 if ( !preg_match( '
#/$#', $this->dav_name ) ) {
456 $sql .=
' OR dav_binding.dav_name = :up_to_slash OR collection.dav_name = :plus_slash OR dav_binding.dav_name = :plus_slash ';
457 $params[
':up_to_slash'] = preg_replace(
'#[^/]*$#',
'', $this->dav_name);
458 $params[
':plus_slash'] = $this->dav_name.
'/';
460 $sql .=
' ORDER BY LENGTH(dav_binding.dav_name) DESC LIMIT 1';
461 $qry =
new AwlQuery( $sql, $params );
462 if ( $qry->Exec(
'DAVResource',__LINE__,__FILE__) && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
463 $this->collection = $row;
464 $this->collection->exists =
true;
465 $this->collection->parent_set = $row->parent_container;
466 $this->collection->parent_container = $row->bind_parent_container;
467 $this->collection->bound_from = $row->dav_name;
468 $this->collection->dav_name = $row->bound_to;
469 if ( $row->is_calendar ==
't' )
470 $this->collection->type =
'calendar';
471 else if ( $row->is_addressbook ==
't' )
472 $this->collection->type =
'addressbook';
473 else if ( preg_match(
'#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
474 $this->collection->type =
'schedule-'. $matches[3].
'box';
476 $this->collection->type =
'collection';
477 if ( strlen($row->external_url) > 8 ) {
478 $this->_is_external =
true;
479 if ( $row->external_type ==
'calendar' )
480 $this->collection->type =
'calendar';
481 else if ( $row->external_type ==
'addressbook' )
482 $this->collection->type =
'addressbook';
484 $this->collection->type =
'collection';
486 $this->_is_binding =
true;
487 $this->bound_from = str_replace( $row->bound_to, $row->dav_name, $this->dav_name);
488 if ( isset($row->access_ticket_id) ) {
489 if ( !isset($this->tickets) ) $this->tickets = array();
490 $this->tickets[] =
new DAVTicket($row->access_ticket_id);
494 dbg_error_log(
'DAVResource',
'No collection for path "%s".', $this->dav_name );
495 $this->collection->exists =
false;
496 $this->collection->dav_name = preg_replace(
'{/[^/]*$}',
'/', $this->dav_name);
519 dbg_error_log(
'DAVResource',
':FetchCollection: Looking for collection for "%s".', $this->dav_name );
522 $cache = getCacheInstance();
523 $cache_ns =
'collection-'.preg_replace(
'{/[^/]*$}',
'/', $this->dav_name);
524 $cache_key =
'dav_resource'.$session->user_no;
525 $this->collection = $cache->get( $cache_ns, $cache_key );
526 if ( $this->collection ===
false ) {
527 $this->ReadCollectionFromDatabase();
528 if ( $this->collection->type !=
'principal' ) {
529 $cache_ns =
'collection-'.$this->collection->dav_name;
530 @dbg_error_log(
'Cache',
':FetchCollection: Setting cache ns "%s" key "%s". Type: %s', $cache_ns, $cache_key, $this->collection->type );
531 $cache->set( $cache_ns, $cache_key, $this->collection );
533 @dbg_error_log(
'DAVResource',
':FetchCollection: Found collection named "%s" of type "%s".', $this->collection->dav_name, $this->collection->type );
536 @dbg_error_log(
'Cache',
':FetchCollection: Got cache ns "%s" key "%s". Type: %s', $cache_ns, $cache_key, $this->collection->type );
537 if ( preg_match(
'#^(/[^/]+)/?$#', $this->dav_name, $matches)
538 || preg_match(
'#^((/principals/[^/]+/)[^/]+)/?$#', $this->dav_name, $matches) ) {
539 $this->_is_principal =
true;
540 $this->FetchPrincipal();
541 $this->collection->is_principal =
true;
542 $this->collection->type =
'principal';
544 @dbg_error_log(
'DAVResource',
':FetchCollection: Read cached collection named "%s" of type "%s".', $this->collection->dav_name, $this->collection->type );
547 if ( isset($this->collection->bound_from) ) {
548 $this->_is_binding =
true;
549 $this->bound_from = str_replace( $this->collection->bound_to, $this->collection->bound_from, $this->dav_name);
550 if ( isset($this->collection->access_ticket_id) ) {
551 if ( !isset($this->tickets) ) $this->tickets = array();
552 $this->tickets[] =
new DAVTicket($this->collection->access_ticket_id);
556 $this->_is_collection = ( $this->_is_principal || $this->collection->dav_name == $this->dav_name || $this->collection->dav_name == $this->dav_name.
'/' );
557 if ( $this->_is_collection ) {
558 $this->dav_name = $this->collection->dav_name;
559 $this->resource_id = $this->collection->collection_id;
560 $this->_is_calendar = ($this->collection->type ==
'calendar');
561 $this->_is_addressbook = ($this->collection->type ==
'addressbook');
562 $this->contenttype =
'httpd/unix-directory';
563 if ( !isset($this->exists) && isset($this->collection->exists) ) {
565 $this->exists = $this->collection->exists;
567 if ( $this->exists ) {
568 if ( isset($this->collection->dav_etag) ) $this->unique_tag =
'"'.$this->collection->dav_etag.
'"';
569 if ( isset($this->collection->created) ) $this->created = $this->collection->created;
570 if ( isset($this->collection->modified) ) $this->modified = $this->collection->modified;
571 if ( isset($this->collection->dav_displayname) ) $this->collection->displayname = $this->collection->dav_displayname;
574 if ( !isset($this->parent) ) $this->GetParentContainer();
575 $this->user_no = $this->parent->GetProperty(
'user_no');
577 if ( isset($this->collection->resourcetypes) )
578 $this->resourcetypes = $this->collection->resourcetypes;
580 $this->resourcetypes =
'<DAV::collection/>';
581 if ( $this->_is_principal ) $this->resourcetypes .=
'<DAV::principal/>';
582 if ( $this->_is_addressbook ) $this->resourcetypes .=
'<urn:ietf:params:xml:ns:carddav:addressbook/>';
583 if ( $this->_is_calendar ) $this->resourcetypes .=
'<urn:ietf:params:xml:ns:caldav:calendar/>';
593 if ( isset($this->principal) )
return;
594 $this->principal =
new DAVPrincipal( array(
"path" => $this->bound_from() ) );
595 if ( $this->_is_principal ) {
596 $this->exists = $this->principal->Exists();
597 $this->collection->dav_name = $this->dav_name();
598 $this->collection->type =
'principal';
599 if ( $this->exists ) {
600 $this->collection = $this->principal->AsCollection();
601 $this->displayname = $this->principal->GetProperty(
'displayname');
602 $this->user_no = $this->principal->user_no();
603 $this->resource_id = $this->principal->principal_id();
604 $this->created = $this->principal->created;
605 $this->modified = $this->principal->modified;
606 $this->resourcetypes = $this->principal->resourcetypes;
616 if ( isset($this->exists) )
return;
617 if ( $this->_is_collection )
return;
620 SELECT calendar_item.*, addressbook_resource.*, caldav_data.*
621 FROM caldav_data LEFT OUTER JOIN calendar_item USING (collection_id,dav_id)
622 LEFT OUTER JOIN addressbook_resource USING (dav_id)
623 WHERE caldav_data.dav_name = :dav_name
625 $params = array(
':dav_name' => $this->bound_from() );
627 $qry =
new AwlQuery( $sql, $params );
628 if ( $qry->Exec(
'DAVResource') && $qry->rows() > 0 ) {
629 $this->exists =
true;
630 $row = $qry->Fetch();
631 $this->FromRow($row);
634 $this->exists =
false;
643 if ( isset($this->dead_properties) )
return;
645 $this->dead_properties = array();
646 if ( !$this->exists || !$this->_is_collection )
return;
648 $qry =
new AwlQuery(
'SELECT property_name, property_value FROM property WHERE dav_name= :dav_name', array(
':dav_name' => $this->dav_name) );
649 if ( $qry->Exec(
'DAVResource') ) {
650 while ( $property = $qry->Fetch() ) {
651 $this->dead_properties[$property->property_name] = self::BuildDeadPropertyXML($property->property_name,$property->property_value);
663 if ( !preg_match(
'{^\s*<.*>\s*$}s', $raw_string) )
return $raw_string;
665 if ( preg_match(
'{^(.*):([^:]+)$}', $property_name, $matches) ) {
666 $xmlns = $matches[1];
667 $property_name = $matches[2];
669 $xml = sprintf(
'<%s%s>%s</%s>', $property_name, (isset($xmlns)?
' xmlns="'.$xmlns.
'"':
''), $raw_string, $property_name);
670 $xml_parser = xml_parser_create_ns(
'UTF-8');
672 xml_parser_set_option ( $xml_parser, XML_OPTION_SKIP_WHITE, 1 );
673 xml_parser_set_option ( $xml_parser, XML_OPTION_CASE_FOLDING, 0 );
674 $rc = xml_parse_into_struct( $xml_parser, $xml, $xml_tags );
675 if ( $rc ==
false ) {
676 $errno = xml_get_error_code($xml_parser);
677 dbg_error_log(
'ERROR',
'XML parsing error: %s (%d) at line %d, column %d',
678 xml_error_string($errno), $errno,
679 xml_get_current_line_number($xml_parser), xml_get_current_column_number($xml_parser) );
680 dbg_error_log(
'ERROR',
"Error occurred in:\n%s\n",$xml);
681 if ($errno >= 200 && $errno < 300 && count($xml_tags) >= 3) {
683 dbg_error_log(
'ERROR',
'XML namespace error but tags extracted, trying to continue');
688 xml_parser_free($xml_parser);
690 $xmltree = BuildXMLTree( $xml_tags, $position);
691 return $xmltree->GetContent();
698 global $session, $request;
700 if ( $this->dav_name ==
'/' || $this->dav_name ==
'' || $this->_is_external ) {
701 $this->privileges = (1 | 16 | 32);
702 dbg_error_log(
'DAVResource',
':FetchPrivileges: Read permissions for user accessing /' );
706 if ( $session->AllowedTo(
'Admin') ) {
707 $this->privileges = privilege_to_bits(
'all');
708 dbg_error_log(
'DAVResource',
':FetchPrivileges: Full permissions for an administrator.' );
712 if ( $this->IsPrincipal() ) {
713 if ( !isset($this->principal) ) $this->FetchPrincipal();
714 $this->privileges = $this->principal->Privileges();
715 dbg_error_log(
'DAVResource',
':FetchPrivileges: Privileges of "%s" for user accessing principal "%s"', $this->privileges, $this->principal->username() );
719 if ( ! isset($this->collection) ) $this->FetchCollection();
720 $this->privileges = 0;
721 if ( !isset($this->collection->path_privs) ) {
722 if ( !isset($this->parent) ) $this->GetParentContainer();
724 $this->collection->path_privs = $this->parent->Privileges();
725 $this->collection->user_no = $this->parent->GetProperty(
'user_no');
726 $this->collection->principal_id = $this->parent->GetProperty(
'principal_id');
729 $this->privileges = $this->collection->path_privs;
730 if ( is_string($this->privileges) ) $this->privileges = bindec( $this->privileges );
732 dbg_error_log(
'DAVResource',
':FetchPrivileges: Privileges of "%s" for user "%s" accessing "%s"',
733 decbin($this->privileges), $session->username, $this->dav_name() );
735 if ( isset($request->ticket) && $request->ticket->MatchesPath($this->bound_from()) ) {
736 $this->privileges |= $request->ticket->privileges();
737 dbg_error_log(
'DAVResource',
':FetchPrivileges: Applying permissions for ticket "%s" now: %s', $request->ticket->id(), decbin($this->privileges) );
740 if ( isset($this->tickets) ) {
741 if ( !isset($this->resource_id) ) $this->FetchResource();
742 foreach( $this->tickets AS $k => $ticket ) {
743 if ( $ticket->MatchesResource($this->resource_id()) || $ticket->MatchesPath($this->bound_from()) ) {
744 $this->privileges |= $ticket->privileges();
745 dbg_error_log(
'DAVResource',
':FetchPrivileges: Applying permissions for ticket "%s" now: %s', $ticket->id(), decbin($this->privileges) );
756 if ( $this->dav_name ==
'/' )
return null;
757 if ( !isset($this->parent) ) {
758 if ( $this->_is_collection ) {
759 dbg_error_log(
'DAVResource',
'Retrieving "%s" - parent of "%s" (dav_name: %s)', $this->parent_path(), $this->collection->dav_name, $this->dav_name() );
760 $this->parent =
new DAVResource( $this->parent_path() );
763 dbg_error_log(
'DAVResource',
'Retrieving "%s" - parent of "%s" (dav_name: %s)', $this->parent_path(), $this->collection->dav_name, $this->dav_name() );
764 $this->parent =
new DAVResource($this->collection->dav_name);
767 return $this->parent;
776 deprecated(
'DAVResource::FetchParentContainer');
777 return $this->GetParentContainer();
785 if ( !isset($this->privileges) ) $this->FetchPrivileges();
786 return $this->privileges;
797 if ( !isset($this->privileges) ) $this->FetchPrivileges();
798 if ( !isset($any) ) $any = ($do_what !=
'all');
799 $test_bits = privilege_to_bits( $do_what );
800 dbg_error_log(
'DAVResource',
'Testing %s privileges of "%s" (%s) against allowed "%s" => "%s" (%s)', ($any?
'any':
'exactly'),
801 $do_what, decbin($test_bits), decbin($this->privileges), ($this->privileges & $test_bits), decbin($this->privileges & $test_bits) );
803 return ($this->privileges & $test_bits) > 0;
806 return ($this->privileges & $test_bits) == $test_bits;
822 if ( $this->HavePrivilegeTo($privilege, $any) )
return;
825 $request->NeedPrivilege( $privilege, $this->dav_name );
834 if ( $privilege_names ==
null ) {
835 if ( !isset($this->privileges) ) $this->FetchPrivileges();
836 $privilege_names = bits_to_privilege($this->privileges, ($this->_is_collection ? $this->collection->type :
null ) );
838 return privileges_to_XML( $privilege_names, $xmldoc);
846 if ( isset($this->supported_methods) )
return $this->supported_methods;
848 $this->supported_methods = array(
857 if ( $this->IsCollection() ) {
862 switch ( $this->collection->type ) {
866 $this->supported_methods = array(
873 case 'schedule-outbox':
874 $this->supported_methods = array_merge(
875 $this->supported_methods,
877 'POST' =>
'',
'PROPPATCH' =>
'',
'MKTICKET' =>
'',
'DELTICKET' =>
''
881 case 'schedule-inbox':
883 $this->supported_methods[
'GET'] =
'';
884 $this->supported_methods[
'PUT'] =
'';
885 $this->supported_methods[
'HEAD'] =
'';
886 $this->supported_methods[
'MKTICKET'] =
'';
887 $this->supported_methods[
'DELTICKET'] =
'';
888 $this->supported_methods[
'ACL'] =
'';
891 $this->supported_methods[
'MKTICKET'] =
'';
892 $this->supported_methods[
'DELTICKET'] =
'';
893 $this->supported_methods[
'BIND'] =
'';
894 $this->supported_methods[
'ACL'] =
'';
896 $this->supported_methods[
'GET'] =
'';
897 $this->supported_methods[
'HEAD'] =
'';
898 $this->supported_methods[
'MKCOL'] =
'';
899 $this->supported_methods[
'MKCALENDAR'] =
'';
900 $this->supported_methods[
'PROPPATCH'] =
'';
901 $this->supported_methods[
'BIND'] =
'';
902 $this->supported_methods[
'ACL'] =
'';
907 $this->supported_methods = array_merge(
908 $this->supported_methods,
910 'GET' =>
'',
'HEAD' =>
'',
'PUT' =>
'',
'MKTICKET' =>
'',
'DELTICKET' =>
''
915 return $this->supported_methods;
923 if ( !isset($this->supported_methods) ) $this->FetchSupportedMethods();
925 foreach( $this->supported_methods AS $k => $v ) {
927 $methods[] =
new XMLElement(
'supported-method',
null, array(
'name' => $k) );
937 if ( isset($this->supported_reports) )
return $this->supported_reports;
939 $this->supported_reports = array(
940 'DAV::principal-property-search' =>
'',
941 'DAV::principal-search-property-set' =>
'',
942 'DAV::expand-property' =>
'',
943 'DAV::principal-match' =>
'',
944 'DAV::sync-collection' =>
''
947 if ( !isset($this->collection) ) $this->FetchCollection();
949 if ( $this->collection->is_calendar ) {
950 $this->supported_reports = array_merge(
951 $this->supported_reports,
953 'urn:ietf:params:xml:ns:caldav:calendar-query' =>
'',
954 'urn:ietf:params:xml:ns:caldav:calendar-multiget' =>
'',
955 'urn:ietf:params:xml:ns:caldav:free-busy-query' =>
''
959 if ( $this->collection->is_addressbook ) {
960 $this->supported_reports = array_merge(
961 $this->supported_reports,
963 'urn:ietf:params:xml:ns:carddav:addressbook-query' =>
'',
964 'urn:ietf:params:xml:ns:carddav:addressbook-multiget' =>
''
968 return $this->supported_reports;
976 if ( !isset($this->supported_reports) ) $this->FetchSupportedReports();
978 foreach( $this->supported_reports AS $k => $v ) {
979 dbg_error_log(
'DAVResource',
':BuildSupportedReports: Adding supported report "%s" which is "%s".', $k, $v );
980 $report =
new XMLElement(
'report');
981 $reply->NSElement($report, $k );
982 $reports[] =
new XMLElement(
'supported-report', $report );
993 if ( isset($this->access_tickets) )
return;
994 $this->access_tickets = array();
997 'SELECT access_ticket.*, COALESCE( resource.dav_name, collection.dav_name) AS target_dav_name,
998 (access_ticket.expires < current_timestamp) AS expired,
999 dav_principal.dav_name AS principal_dav_name,
1000 EXTRACT( \'epoch\' FROM (access_ticket.expires - current_timestamp)) AS seconds,
1001 path_privs(access_ticket.dav_owner_id,collection.dav_name,:scan_depth) AS grantor_collection_privileges
1002 FROM access_ticket JOIN collection ON (target_collection_id = collection_id)
1003 JOIN dav_principal ON (dav_owner_id = principal_id)
1004 LEFT JOIN caldav_data resource ON (resource.dav_id = access_ticket.target_resource_id)
1005 WHERE target_collection_id = :collection_id ';
1006 $params = array(
':collection_id' => $this->collection->collection_id,
':scan_depth' => $c->permission_scan_depth);
1007 if ( $this->IsCollection() ) {
1008 $sql .=
'AND target_resource_id IS NULL';
1011 if ( !isset($this->exists) ) $this->FetchResource();
1012 $sql .=
'AND target_resource_id = :dav_id';
1013 $params[
':dav_id'] = $this->resource->dav_id;
1015 if ( isset($this->exists) && !$this->exists )
return;
1017 $qry =
new AwlQuery( $sql, $params );
1018 if ( $qry->Exec(
'DAVResource',__LINE__,__FILE__) && $qry->rows() ) {
1019 while( $ticket = $qry->Fetch() ) {
1020 $this->access_tickets[] = $ticket;
1037 global $session, $request;
1039 if ( !isset($this->access_tickets) ) $this->FetchTickets();
1041 $show_all = $this->HavePrivilegeTo(
'DAV::read-acl');
1042 foreach( $this->access_tickets AS $meh => $trow ) {
1043 if ( !$show_all && ( $trow->dav_owner_id == $session->principal_id || $request->ticket->id() == $trow->ticket_id ) )
continue;
1044 dbg_error_log(
'DAVResource',
':BuildTicketinfo: Adding access_ticket "%s" which is "%s".', $trow->ticket_id, $trow->privileges );
1045 $ticket =
new XMLElement( $reply->Tag(
'ticketinfo',
'http://www.xythos.com/namespaces/StorageServer',
'TKT' ) );
1046 $reply->NSElement($ticket,
'http://www.xythos.com/namespaces/StorageServer:id', $trow->ticket_id );
1047 $reply->NSElement($ticket,
'http://www.xythos.com/namespaces/StorageServer:owner', $reply->href( ConstructURL($trow->principal_dav_name)) );
1048 $reply->NSElement($ticket,
'http://www.xythos.com/namespaces/StorageServer:timeout', (isset($trow->seconds) ? sprintf(
'Seconds-%d', $trow->seconds) :
'infinity') );
1049 $reply->NSElement($ticket,
'http://www.xythos.com/namespaces/StorageServer:visits',
'infinity' );
1051 foreach( bits_to_privilege(bindec($trow->privileges) & bindec($trow->grantor_collection_privileges) ) AS $k => $v ) {
1052 $privs[] = $reply->NewXMLElement($v);
1054 $reply->NSElement($ticket,
'DAV::privilege', $privs );
1055 $tickets[] = $ticket;
1069 if ( !isset($this->_locks_found) ) {
1070 $this->_locks_found = array();
1074 $sql =
'SELECT * FROM locks WHERE :this_path::text ~ (\'^\'||dav_name||:match_end)::text';
1075 $qry =
new AwlQuery($sql, array(
':this_path' => $this->dav_name,
':match_end' => ($depth == DEPTH_INFINITY ?
'' :
'$') ) );
1076 if ( $qry->Exec(
'DAVResource',__LINE__,__FILE__) ) {
1077 while( $lock_row = $qry->Fetch() ) {
1078 $this->_locks_found[$lock_row->opaquelocktoken] = $lock_row;
1082 $this->DoResponse(500,i18n(
"Database Error"));
1087 foreach( $this->_locks_found AS $lock_token => $lock_row ) {
1088 if ( $lock_row->depth == DEPTH_INFINITY || $lock_row->dav_name == $this->dav_name ) {
1101 return $this->_is_collection;
1109 return $this->_is_collection && $this->_is_principal;
1117 return $this->_is_collection && $this->_is_calendar;
1126 if ( $this->_is_proxy_resource ) {
1127 return ($type ==
'any' || $type == $this->proxy_type);
1138 if ( $this->_is_collection && preg_match(
'{schedule-(inbox|outbox)}', $this->collection->type, $matches ) ) {
1139 return ($type ==
'any' || $type == $matches[1]);
1150 if ( !$this->_is_collection && preg_match(
'{schedule-(inbox|outbox)}', $this->collection->type, $matches ) ) {
1151 return ($type ==
'any' || $type == $matches[1]);
1161 return $this->_is_collection && $this->_is_addressbook;
1169 return $this->_is_binding;
1177 return $this->_is_external;
1185 if ( ! isset($this->exists) ) {
1186 if ( $this->IsPrincipal() ) {
1187 if ( !isset($this->principal) ) $this->FetchPrincipal();
1188 $this->exists = $this->principal->Exists();
1190 else if ( ! $this->IsCollection() ) {
1191 if ( !isset($this->resource) ) $this->FetchResource();
1195 return $this->exists;
1203 if ( $this->collection->dav_name != $this->dav_name ) {
1204 return $this->collection->exists;
1206 $parent = $this->GetParentContainer();
1207 return $parent->Exists();
1216 if ( !isset($this->dav_name) ) {
1217 throw Exception(
"What! How can dav_name not be set?");
1219 return ConstructURL($this->dav_name);
1228 if ( isset($this->dav_name) )
return $this->dav_name;
1238 if ( isset($this->bound_from) )
return $this->bound_from;
1239 return $this->dav_name();
1247 if ( !isset($this->bound_from) && isset($this->dav_name) ) {
1248 $this->bound_from = $this->dav_name;
1250 $this->dav_name = $new_dav_name;
1251 return $this->dav_name;
1259 if ( $this->IsCollection() ) {
1260 if ( !isset($this->collection) ) $this->FetchCollection();
1261 if ( !isset($this->collection->parent_container) ) {
1262 $this->collection->parent_container = preg_replace(
'{[^/]+/$}',
'', $this->bound_from());
1264 return $this->collection->parent_container;
1266 return preg_replace(
'{[^/]+$}',
'', $this->bound_from());
1275 if ( !isset($this->principal) ) $this->FetchPrincipal();
1276 return $this->principal->url();
1284 if ( !isset($this->principal) ) $this->FetchPrincipal();
1285 return $this->principal->user_no();
1293 if ( !isset($this->collection) ) $this->FetchCollection();
1294 return $this->collection->collection_id;
1302 if ( !isset($this->collection) ) $this->FetchCollection();
1303 return $this->collection->timezone;
1311 if ( !isset($this->resource) ) $this->FetchResource();
1312 return $this->resource;
1320 if ( isset($this->unique_tag) )
return $this->unique_tag;
1321 if ( $this->IsPrincipal() && !isset($this->principal) ) {
1322 $this->FetchPrincipal();
1323 $this->unique_tag = $this->principal->unique_tag();
1325 else if ( !$this->_is_collection && !isset($this->resource) ) $this->FetchResource();
1327 if ( $this->exists !==
true || !isset($this->unique_tag) ) $this->unique_tag =
'';
1329 return $this->unique_tag;
1337 if ( isset($this->resource_id) )
return $this->resource_id;
1338 if ( $this->IsPrincipal() && !isset($this->principal) ) $this->FetchPrincipal();
1339 else if ( !$this->_is_collection && !isset($this->resource) ) $this->FetchResource();
1341 if ( $this->exists !==
true || !isset($this->resource_id) ) $this->resource_id =
null;
1343 return $this->resource_id;
1351 dbg_error_log(
'DAVResource',
'Request for a%scached sync-token', ($cachedOK ?
' ' :
'n un') );
1352 if ( $this->IsPrincipal() )
return null;
1353 if ( $this->collection_id() == 0 )
return null;
1354 if ( !isset($this->sync_token) || !$cachedOK ) {
1355 $sql =
'SELECT new_sync_token( 0, :collection_id) AS sync_token';
1356 $params = array(
':collection_id' => $this->collection_id());
1357 $qry =
new AwlQuery($sql, $params );
1358 if ( !$qry->Exec() || !$row = $qry->Fetch() ) {
1359 if ( !$qry->QDo(
'SELECT new_sync_token( 0, :collection_id) AS sync_token', $params) )
throw new Exception(
'Problem with database query');
1360 $row = $qry->Fetch();
1362 $this->sync_token =
'data:,'.$row->sync_token;
1364 dbg_error_log(
'DAVResource',
'Returning sync token of "%s"', $this->sync_token );
1365 return $this->sync_token;
1372 return ( isset($this->collection->publicly_readable) && $this->collection->publicly_readable ==
't' );
1380 return ( isset($this->collection->publicly_events_only) && $this->collection->publicly_events_only ==
't' );
1388 if ( $this->IsPrincipal() )
return 'root';
1389 if ( !$this->IsCollection() )
return $this->collection->type;
1391 if ( ! isset($this->collection->parent_container) )
return null;
1393 if ( isset($this->parent_container_type) )
return $this->parent_container_type;
1395 if ( preg_match(
'#/[^/]+/#', $this->collection->parent_container) ) {
1396 $this->parent_container_type =
'principal';
1399 $qry =
new AwlQuery(
'SELECT * FROM collection WHERE dav_name = :parent_name',
1400 array(
':parent_name' => $this->collection->parent_container ) );
1401 if ( $qry->Exec(
'DAVResource') && $qry->rows() > 0 && $parent = $qry->Fetch() ) {
1402 if ( $parent->is_calendar ==
't' )
1403 $this->parent_container_type =
'calendar';
1404 else if ( $parent->is_addressbook ==
't' )
1405 $this->parent_container_type =
'addressbook';
1406 else if ( preg_match(
'#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
1407 $this->parent_container_type =
'schedule-'. $matches[3].
'box';
1409 $this->parent_container_type =
'collection';
1412 $this->parent_container_type =
null;
1414 return $this->parent_container_type;
1422 $privilege_names = bits_to_privilege($privs, ($this->_is_collection ? $this->collection->type :
'resource'));
1423 $privileges = array();
1424 foreach( $privilege_names AS $k ) {
1425 $privilege =
new XMLElement(
'privilege');
1426 if ( isset($xmldoc) )
1427 $xmldoc->NSElement($privilege,$k);
1429 $privilege->NewElement($k);
1430 $privileges[] = $privilege;
1432 $ace =
new XMLElement(
'ace', array(
1433 new XMLElement(
'principal', $principal),
1434 new XMLElement(
'grant', $privileges ) )
1443 if ( !isset($this->principal) ) $this->FetchPrincipal();
1444 $default_privs = $this->principal->default_privileges;
1445 if ( isset($this->collection->default_privileges) ) $default_privs = $this->collection->default_privileges;
1448 $acl[] = $this->BuildACE($xmldoc, pow(2,25) - 1,
new XMLElement(
'property',
new XMLElement(
'owner')) );
1450 $qry =
new AwlQuery(
'SELECT dav_principal.dav_name, grants.* FROM grants JOIN dav_principal ON (to_principal=principal_id) WHERE by_collection = :collection_id OR by_principal = :principal_id ORDER BY by_collection',
1451 array(
':collection_id' => $this->collection->collection_id,
1452 ':principal_id' => $this->principal->principal_id() ) );
1453 if ( $qry->Exec(
'DAVResource') && $qry->rows() > 0 ) {
1454 $by_collection =
null;
1455 while( $grant = $qry->Fetch() ) {
1456 if ( !isset($by_collection) ) $by_collection = isset($grant->by_collection);
1457 if ( $by_collection && !isset($grant->by_collection) )
break;
1458 $acl[] = $this->BuildACE($xmldoc, $grant->privileges, $xmldoc->href(ConstructURL($grant->dav_name)) );
1462 $acl[] = $this->BuildACE($xmldoc, $default_privs,
new XMLElement(
'authenticated') );
1477 case 'collection_id':
1478 return $this->collection_id();
1481 case 'principal_id':
1482 if ( !isset($this->principal) ) $this->FetchPrincipal();
1483 return $this->principal->principal_id();
1486 case 'resourcetype':
1487 if ( isset($this->resourcetypes) ) {
1488 $this->resourcetypes = preg_replace(
'{^\s*<(.*)/>\s*$}',
'$1', $this->resourcetypes);
1489 $type_list = preg_split(
'{(/>\s*<|\n)}', $this->resourcetypes);
1490 foreach( $type_list AS $k => $resourcetype ) {
1491 if ( preg_match(
'{^([^:]+):([^:]+) \s+ xmlns:([^=]+)="([^"]+)" \s* $}x', $resourcetype, $matches ) ) {
1492 $type_list[$k] = $matches[4] .
':' .$matches[2];
1494 else if ( preg_match(
'{^([^:]+) \s+ xmlns="([^"]+)" \s* $}x', $resourcetype, $matches ) ) {
1495 $type_list[$k] = $matches[2] .
':' .$matches[1];
1502 if ( !isset($this->resource) ) $this->FetchResource();
1503 return clone($this->resource);
1507 if ( !isset($this->resource) ) $this->FetchResource();
1508 dbg_error_log(
'DAVResource',
':GetProperty: dav-data: fetched resource does%s exist.', ($this->exists?
'':
' not') );
1509 return $this->resource->caldav_data;
1513 if ( !isset($this->principal) ) $this->FetchPrincipal();
1514 return clone($this->principal);
1518 if ( isset($this->{$name}) ) {
1519 if ( ! is_object($this->{$name}) )
return $this->{$name};
1520 return clone($this->{$name});
1522 if ( $this->_is_principal ) {
1523 if ( !isset($this->principal) ) $this->FetchPrincipal();
1524 if ( isset($this->principal->{$name}) )
return $this->principal->{$name};
1525 if ( isset($this->collection->{$name}) )
return $this->collection->{$name};
1527 else if ( $this->_is_collection ) {
1528 if ( isset($this->collection->{$name}) )
return $this->collection->{$name};
1529 if ( isset($this->principal->{$name}) )
return $this->principal->{$name};
1532 if ( !isset($this->resource) ) $this->FetchResource();
1533 if ( isset($this->resource->{$name}) )
return $this->resource->{$name};
1534 if ( !isset($this->principal) ) $this->FetchPrincipal();
1535 if ( isset($this->principal->{$name}) )
return $this->principal->{$name};
1536 if ( isset($this->collection->{$name}) )
return $this->collection->{$name};
1538 if ( isset($this->{$name}) ) {
1539 if ( ! is_object($this->{$name}) )
return $this->{$name};
1540 return clone($this->{$name});
1553 if ( !isset($this->dead_properties) ) $this->FetchDeadProperties();
1554 $allprop = array_merge( (isset($this->dead_properties)?array_keys($this->dead_properties):array()),
1555 (isset($include_properties)?$include_properties:array()),
1557 'DAV::getcontenttype',
'DAV::resourcetype',
'DAV::getcontentlength',
'DAV::displayname',
'DAV::getlastmodified',
1558 'DAV::creationdate',
'DAV::getetag',
'DAV::getcontentlanguage',
'DAV::supportedlock',
'DAV::lockdiscovery',
1559 'DAV::owner',
'DAV::principal-URL',
'DAV::current-user-principal',
1560 'urn:ietf:params:xml:ns:carddav:max-resource-size',
'urn:ietf:params:xml:ns:carddav:supported-address-data',
1561 'urn:ietf:params:xml:ns:carddav:addressbook-description',
'urn:ietf:params:xml:ns:carddav:addressbook-home-set'
1572 global $c, $session, $request;
1576 if ( $reply ===
null ) $reply = $GLOBALS[
'reply'];
1579 case 'DAV::allprop':
1580 $property_list = $this->DAV_AllProperties();
1581 $discarded = array();
1582 foreach( $property_list AS $k => $v ) {
1583 $this->ResourceProperty($v, $prop, $reply, $discarded);
1588 $prop->NewElement(
'href', ConstructURL($this->dav_name) );
1591 case 'DAV::resource-id':
1592 if ( $this->resource_id > 0 )
1593 $reply->DAVElement( $prop,
'resource-id', $reply->href(ConstructURL(
'/.resources/'.$this->resource_id) ) );
1598 case 'DAV::parent-set':
1600 SELECT b.parent_container FROM dav_binding b JOIN collection c ON (b.bound_source_id=c.collection_id)
1601 WHERE regexp_replace( b.dav_name,
'^.*/', c.dav_name ) = :bound_from
1603 $qry =
new AwlQuery($sql, array(
':bound_from' => $this->bound_from() ) );
1605 if ( $qry->Exec(
'DAVResource',__LINE__,__FILE__) && $qry->rows() > 0 ) {
1606 while( $row = $qry->Fetch() ) {
1607 $parents[$row->parent_container] =
true;
1610 $parents[preg_replace(
'{(?<=/)[^/]+/?$}',
'',$this->bound_from())] =
true;
1611 $parents[preg_replace(
'{(?<=/)[^/]+/?$}',
'',$this->dav_name())] =
true;
1613 $parent_set = $reply->DAVElement( $prop,
'parent-set' );
1614 foreach( $parents AS $parent => $v ) {
1615 if ( preg_match(
'{^(.*)?/([^/]+)/?$}', $parent, $matches ) ) {
1616 $reply->DAVElement($parent_set,
'parent', array(
1617 new XMLElement(
'href', ConstructURL($matches[1])),
1618 new XMLElement(
'segment', $matches[2])
1621 else if ( $parent ==
'/' ) {
1622 $reply->DAVElement($parent_set,
'parent', array(
1623 new XMLElement(
'href',
'/'),
1624 new XMLElement(
'segment', ( ConstructURL(
'/') ==
'/caldav.php/' ?
'caldav.php' :
''))
1630 case 'DAV::getcontenttype':
1631 if ( !isset($this->contenttype) && !$this->_is_collection && !isset($this->resource) ) $this->FetchResource();
1632 $prop->NewElement(
'getcontenttype', $this->contenttype );
1635 case 'DAV::resourcetype':
1636 $resourcetypes = $prop->NewElement(
'resourcetype' );
1637 if ( $this->_is_collection ) {
1638 $type_list = $this->GetProperty(
'resourcetype');
1639 if ( !is_array($type_list) )
return true;
1641 foreach( $type_list AS $k => $v ) {
1642 if ( $v ==
'' )
continue;
1643 $reply->NSElement( $resourcetypes, $v );
1645 if ( $this->_is_binding ) {
1646 $reply->NSElement( $resourcetypes,
'http://xmlns.davical.org/davical:webdav-binding' );
1651 case 'DAV::getlastmodified':
1653 $reply->NSElement($prop, $tag, ISODateToHTTPDate($this->GetProperty(
'modified')) );
1656 case 'DAV::creationdate':
1658 $reply->NSElement($prop, $tag, DateToISODate($this->GetProperty(
'created'),
true) );
1661 case 'DAV::getcontentlength':
1662 if ( $this->_is_collection )
return false;
1663 if ( !isset($this->resource) ) $this->FetchResource();
1664 if ( isset($this->resource) ) {
1665 $reply->NSElement($prop, $tag, strlen($this->resource->caldav_data) );
1669 case 'DAV::getcontentlanguage':
1670 $locale = (isset($c->current_locale) ? $c->current_locale :
'');
1671 if ( isset($this->locale) && $this->locale !=
'' ) $locale = $this->locale;
1672 $reply->NSElement($prop, $tag, $locale );
1675 case 'DAV::acl-restrictions':
1676 $reply->NSElement($prop, $tag, array(
new XMLElement(
'grant-only'),
new XMLElement(
'no-invert') ) );
1679 case 'DAV::inherited-acl-set':
1680 $inherited_acls = array();
1681 if ( ! $this->_is_collection ) {
1682 $inherited_acls[] = $reply->href(ConstructURL($this->collection->dav_name));
1684 $reply->NSElement($prop, $tag, $inherited_acls );
1689 if ( $this->IsExternal() ){
1690 $reply->DAVElement( $prop,
'owner', $reply->href( ConstructURL($this->collection->bound_from )) );
1693 $reply->DAVElement( $prop,
'owner', $reply->href( ConstructURL(DeconstructURL($this->principal_url())) ) );
1697 case 'DAV::add-member':
1698 if ( ! $this->_is_collection )
return false;
1699 if ( $this->_is_principal )
return false;
1700 if ( isset($c->post_add_member) && $c->post_add_member === false )
return false;
1701 $reply->DAVElement( $prop,
'add-member', $reply->href(ConstructURL(DeconstructURL($this->url())).
'?add_member') );
1706 case 'DAV::alternate-URI-set':
1707 $reply->NSElement($prop, $tag );
1710 case 'DAV::getetag':
1711 if ( $this->_is_collection )
return false;
1712 $reply->NSElement($prop, $tag, $this->unique_tag() );
1715 case 'http://calendarserver.org/ns/:getctag':
1716 if ( ! $this->_is_collection )
return false;
1717 $reply->NSElement($prop, $tag, $this->unique_tag() );
1720 case 'DAV::sync-token':
1721 if ( ! $this->_is_collection )
return false;
1722 $sync_token = $this->sync_token();
1723 if ( empty($sync_token) )
return false;
1724 $reply->NSElement($prop, $tag, $sync_token );
1727 case 'http://calendarserver.org/ns/:calendar-proxy-read-for':
1728 $proxy_type =
'read';
1729 case 'http://calendarserver.org/ns/:calendar-proxy-write-for':
1730 if ( isset($c->disable_caldav_proxy) && $c->disable_caldav_proxy )
return false;
1731 if ( !isset($proxy_type) ) $proxy_type =
'write';
1733 $this->FetchPrincipal();
1734 $reply->CalendarserverElement($prop,
'calendar-proxy-'.$proxy_type.
'-for', $reply->href( $this->principal->ProxyFor($proxy_type) ) );
1737 case 'http://calendarserver.org/ns/:group-member-set':
1738 case 'DAV::group-member-set':
1739 if ( $this->_is_proxy_resource ) {
1740 $this->FetchPrincipal();
1741 if ( $this->proxy_type ==
'read' ) {
1742 $reply->DAVElement( $prop,
'group-member-set', $reply->href( $this->principal->ReadProxyGroup() ) );
1744 $reply->DAVElement( $prop,
'group-member-set', $reply->href( $this->principal->WriteProxyGroup() ) );
1751 case 'http://calendarserver.org/ns/:group-membership':
1752 case 'DAV::group-membership':
1753 if ( $this->_is_proxy_resource ) {
1755 $reply->NSElement($prop, $tag );
1761 case 'DAV::current-user-privilege-set':
1762 if ( $this->HavePrivilegeTo(
'DAV::read-current-user-privilege-set') ) {
1763 $reply->NSElement($prop, $tag, $this->BuildPrivileges() );
1770 case 'urn:ietf:params:xml:ns:caldav:supported-calendar-data':
1771 if ( ! $this->IsCalendar() && ! $this->IsSchedulingCollection() )
return false;
1772 $reply->NSElement($prop, $tag,
'text/calendar' );
1775 case 'urn:ietf:params:xml:ns:caldav:supported-calendar-component-set':
1776 if ( ! $this->_is_collection )
return false;
1777 if ( $this->IsCalendar() ) {
1778 if ( !isset($this->dead_properties) ) $this->FetchDeadProperties();
1779 if ( isset($this->dead_properties[$tag]) ) {
1780 $set_of_components = $this->dead_properties[$tag];
1781 foreach( $set_of_components AS $k => $v ) {
1782 if ( preg_match(
'{(VEVENT|VTODO|VJOURNAL|VTIMEZONE|VFREEBUSY|VPOLL|VAVAILABILITY)}', $v, $matches) ) {
1783 $set_of_components[$k] = $matches[1];
1786 unset( $set_of_components[$k] );
1790 else if ( isset($c->default_calendar_components) && is_array($c->default_calendar_components) ) {
1791 $set_of_components = $c->default_calendar_components;
1794 $set_of_components = array(
'VEVENT',
'VTODO',
'VJOURNAL' );
1797 else if ( $this->IsSchedulingCollection() )
1798 $set_of_components = array(
'VEVENT',
'VTODO',
'VFREEBUSY' );
1800 $components = array();
1801 foreach( $set_of_components AS $v ) {
1802 $components[] = $reply->NewXMLElement(
'comp',
'', array(
'name' => $v),
'urn:ietf:params:xml:ns:caldav');
1804 $reply->CalDAVElement($prop,
'supported-calendar-component-set', $components );
1807 case 'DAV::supported-method-set':
1808 $prop->NewElement(
'supported-method-set', $this->BuildSupportedMethods() );
1811 case 'DAV::supported-report-set':
1812 $prop->NewElement(
'supported-report-set', $this->BuildSupportedReports( $reply ) );
1815 case 'DAV::supportedlock':
1816 $prop->NewElement(
'supportedlock',
1817 new XMLElement(
'lockentry',
1819 new XMLElement(
'lockscope',
new XMLElement(
'exclusive')),
1820 new XMLElement(
'locktype',
new XMLElement(
'write')),
1826 case 'DAV::supported-privilege-set':
1827 $prop->NewElement(
'supported-privilege-set', $request->BuildSupportedPrivileges($reply) );
1830 case 'DAV::principal-collection-set':
1831 $prop->NewElement(
'principal-collection-set', $reply->href( ConstructURL(
'/') ) );
1834 case 'DAV::current-user-principal':
1835 $prop->NewElement(
'current-user-principal', $reply->href( ConstructURL(DeconstructURL($session->principal->url())) ) );
1838 case 'SOME-DENIED-PROPERTY':
1839 $denied[] = $reply->Tag($tag);
1842 case 'urn:ietf:params:xml:ns:caldav:calendar-timezone':
1843 if ( ! $this->_is_collection )
return false;
1844 if ( !isset($this->collection->vtimezone) || $this->collection->vtimezone ==
'' )
return false;
1846 $cal =
new iCalComponent();
1848 $cal->AddComponent(
new iCalComponent($this->collection->vtimezone) );
1849 $reply->NSElement($prop, $tag, $cal->Render() );
1852 case 'urn:ietf:params:xml:ns:carddav:address-data':
1853 case 'urn:ietf:params:xml:ns:caldav:calendar-data':
1854 if ( $this->_is_collection )
return false;
1855 if ( !isset($c->sync_resource_data_ok) || $c->sync_resource_data_ok == false )
return false;
1856 if ( !isset($this->resource) ) $this->FetchResource();
1857 $reply->NSElement($prop, $tag, $this->resource->caldav_data );
1860 case 'urn:ietf:params:xml:ns:carddav:max-resource-size':
1861 if ( ! $this->_is_collection || !$this->_is_addressbook )
return false;
1862 $reply->NSElement($prop, $tag, $c->carddav_max_resource_size );
1865 case 'urn:ietf:params:xml:ns:carddav:supported-address-data':
1866 if ( ! $this->_is_collection || !$this->_is_addressbook )
return false;
1867 $address_data = $reply->NewXMLElement(
'address-data',
false,
1868 array(
'content-type' =>
'text/vcard',
'version' =>
'3.0'),
'urn:ietf:params:xml:ns:carddav');
1869 $reply->NSElement($prop, $tag, $address_data );
1873 if ( $this->HavePrivilegeTo(
'DAV::read-acl') ) {
1874 $reply->NSElement($prop, $tag, $this->GetACL( $reply ) );
1881 case 'http://www.xythos.com/namespaces/StorageServer:ticketdiscovery':
1882 case 'DAV::ticketdiscovery':
1883 $reply->NSElement($prop,
'http://www.xythos.com/namespaces/StorageServer:ticketdiscovery', $this->BuildTicketinfo($reply) );
1887 $property_value = $this->GetProperty(preg_replace(
'{^(DAV:|urn:ietf:params:xml:ns:ca(rd|l)dav):}',
'', $tag));
1888 if ( isset($property_value) ) {
1889 $reply->NSElement($prop, $tag, $property_value );
1892 if ( !isset($this->dead_properties) ) $this->FetchDeadProperties();
1893 if ( isset($this->dead_properties[$tag]) ) {
1894 $reply->NSElement($prop, $tag, $this->dead_properties[$tag] );
1917 dbg_error_log(
'DAVResource',
':GetPropStat: propstat for href "%s"', $this->dav_name );
1919 $prop =
new XMLElement(
'prop',
null,
null,
'DAV:');
1921 $not_found = array();
1922 foreach( $properties AS $k => $tag ) {
1923 if ( is_object($tag) ) {
1924 dbg_error_log(
'DAVResource',
':GetPropStat: "$properties" should be an array of text. Assuming this object is an XMLElement!.' );
1925 $tag = $tag->GetNSTag();
1927 $found = $this->ResourceProperty($tag, $prop, $reply, $denied );
1929 if ( !isset($this->principal) ) $this->FetchPrincipal();
1930 $found = $this->principal->PrincipalProperty( $tag, $prop, $reply, $denied );
1934 $not_found[] = $tag;
1937 if ( $props_only )
return $prop;
1939 $status =
new XMLElement(
'status',
'HTTP/1.1 200 OK',
null,
'DAV:' );
1941 $elements = array(
new XMLElement(
'propstat', array($prop,$status),
null,
'DAV:' ) );
1943 if ( count($denied) > 0 ) {
1944 $status =
new XMLElement(
'status',
'HTTP/1.1 403 Forbidden',
null,
'DAV:' );
1945 $noprop =
new XMLElement(
'prop',
null,
null,
'DAV:');
1946 foreach( $denied AS $k => $v ) {
1947 $reply->NSElement($noprop, $v);
1949 $elements[] =
new XMLElement(
'propstat', array( $noprop, $status),
null,
'DAV:' );
1952 if ( !$request->PreferMinimal() && count($not_found) > 0 ) {
1953 $status =
new XMLElement(
'status',
'HTTP/1.1 404 Not Found',
null,
'DAV:' );
1954 $noprop =
new XMLElement(
'prop',
null,
null,
'DAV:');
1955 foreach( $not_found AS $k => $v ) {
1956 $reply->NSElement($noprop,$v);
1958 $elements[] =
new XMLElement(
'propstat', array( $noprop, $status),
null,
'DAV:' );
1972 function RenderAsXML( $properties, &$reply, $bound_parent_path =
null ) {
1973 dbg_error_log(
'DAVResource',
':RenderAsXML: Resource "%s" exists(%d)', $this->dav_name, $this->Exists() );
1975 if ( !$this->Exists() )
return null;
1977 $elements = $this->GetPropStat( $properties, $reply );
1978 if ( isset($bound_parent_path) ) {
1979 $dav_name = str_replace( $this->parent_path(), $bound_parent_path, $this->dav_name );
1982 $dav_name = $this->dav_name;
1985 array_unshift( $elements, $reply->href(ConstructURL($dav_name)));
1987 $response =
new XMLElement(
'response', $elements,
null,
'DAV:' );