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;
153 $this->exists = null;
158 $this->collection = null;
159 $this->principal = null;
160 $this->parent = null;
161 $this->resourcetypes = null;
162 $this->contenttype = null;
163 $this->privileges = null;
164 $this->dead_properties = null;
165 $this->supported_methods = null;
166 $this->supported_reports = null;
168 $this->_is_collection =
false;
169 $this->_is_principal =
false;
170 $this->_is_calendar =
false;
171 $this->_is_binding =
false;
172 $this->_is_external =
false;
173 $this->_is_addressbook =
false;
174 $this->_is_proxy_resource =
false;
175 if ( isset($parameters) && is_object($parameters) ) {
178 else if ( isset($parameters) && is_array($parameters) ) {
179 if ( isset($parameters[
'path']) ) {
180 $this->
FromPath($parameters[
'path']);
183 else if ( isset($parameters) && is_string($parameters) ) {
196 if ( $row == null )
return;
198 $this->exists =
true;
200 $this->
bound_from = (isset($row->bound_from)? $row->bound_from : $row->dav_name);
201 $this->_is_collection = preg_match(
'{/$}', $this->
dav_name );
203 if ( $this->_is_collection ) {
204 $this->contenttype =
'httpd/unix-directory';
205 $this->collection = (object) array();
208 $this->_is_principal = preg_match(
'{^/[^/]+/$}', $this->
dav_name );
209 if ( preg_match(
'#^(/principals/[^/]+/[^/]+)/?$#', $this->
dav_name, $matches) ) {
210 $this->collection->dav_name = $matches[1].
'/';
211 $this->collection->type =
'principal_link';
212 $this->_is_principal =
true;
217 if ( isset($row->dav_id) ) $this->
resource_id = $row->dav_id;
220 dbg_error_log(
'DAVResource',
':FromRow: Named "%s" is%s a collection.', $this->
dav_name, ($this->_is_collection?
'':
' not') );
222 foreach( $row AS $k => $v ) {
223 if ( $this->_is_collection )
224 $this->collection->{$k} = $v;
233 case 'resourcetypes':
234 if ( $this->_is_collection ) $this->{$k} = $v;
244 if ( $this->_is_collection ) {
245 if ( !isset( $this->collection->type ) || $this->collection->type ==
'collection' ) {
246 if ( $this->_is_principal )
247 $this->collection->type =
'principal';
248 else if ( $row->is_calendar ==
't' ) {
249 $this->collection->type =
'calendar';
251 else if ( $row->is_addressbook ==
't' ) {
252 $this->collection->type =
'addressbook';
254 else if ( isset($row->is_proxy) && $row->is_proxy ==
't' ) {
255 $this->collection->type =
'proxy';
257 else if ( preg_match(
'#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->
dav_name, $matches ) )
258 $this->collection->type =
'schedule-'. $matches[3].
'box';
260 $this->collection->type =
'root';
262 $this->collection->type =
'collection';
265 $this->_is_calendar = ($this->collection->is_calendar ==
't');
266 $this->_is_addressbook = ($this->collection->is_addressbook ==
't');
267 $this->_is_proxy_resource = ($this->collection->type ==
'proxy');
268 if ( $this->_is_principal && !isset($this->resourcetypes) ) {
269 $this->resourcetypes =
'<DAV::collection/><DAV::principal/>';
271 else if ( $this->_is_proxy_resource ) {
272 $this->resourcetypes = $this->collection->resourcetypes;
273 preg_match(
'#^/[^/]+/calendar-proxy-(read|write)/?[^/]*$#', $this->
dav_name, $matches );
274 $this->proxy_type = $matches[1];
276 if ( isset($this->collection->dav_displayname) ) $this->collection->displayname = $this->collection->dav_displayname;
279 $this->resourcetypes =
'';
280 if ( isset($this->
resource->caldav_data) ) {
282 if ( strtoupper(substr($this->
resource->caldav_data,0,15)) ==
'BEGIN:VCALENDAR' ) {
283 $this->contenttype =
'text/calendar';
284 if ( isset($this->
resource->caldav_type) ) $this->contenttype .=
"; component=" . strtolower($this->
resource->caldav_type);
286 $vcal =
new iCalComponent($this->
resource->caldav_data);
287 $confidential = $vcal->CloneConfidential();
288 $this->
resource->caldav_data = $confidential->Render();
289 $this->
resource->displayname = $this->
resource->summary = translate(
'Busy');
290 $this->
resource->description = null;
295 if ( isset($this->
resource->class) && strtoupper($this->
resource->class)==
'CONFIDENTIAL' && !$this->HavePrivilegeTo(
'all') && $session->user_no != $this->
resource->user_no ) {
296 $vcal =
new iCalComponent($this->
resource->caldav_data);
297 $confidential = $vcal->CloneConfidential();
298 $this->
resource->caldav_data = $confidential->Render();
300 if ( isset($c->hide_alarm) && $c->hide_alarm && !$this->HavePrivilegeTo(
'write') ) {
301 $vcal1 =
new iCalComponent($this->
resource->caldav_data);
302 $comps = $vcal1->GetComponents();
303 $vcal2 =
new iCalComponent();
305 foreach( $comps AS $comp ) {
306 $comp->ClearComponents(
'VALARM');
307 $vcal2->AddComponent($comp);
309 $this->
resource->displayname = $this->
resource->summary = $vcal2->GetPValue(
'SUMMARY');
310 $this->
resource->caldav_data = $vcal2->Render();
314 else if ( strtoupper(substr($this->
resource->caldav_data,0,11)) ==
'BEGIN:VCARD' ) {
315 $this->contenttype =
'text/vcard';
317 else if ( strtoupper(substr($this->
resource->caldav_data,0,11)) ==
'BEGIN:VLIST' ) {
318 $this->contenttype =
'text/x-vlist';
332 $this->
dav_name = DeconstructURL($inpath);
335 if ( $this->_is_collection ) {
336 if ( $this->_is_principal || $this->collection->type ==
'principal' ) $this->
FetchPrincipal();
341 dbg_error_log(
'DAVResource',
':FromPath: Path "%s" is%s a collection%s.',
342 $this->
dav_name, ($this->_is_collection?
' '.$this->resourcetypes:
' not'), ($this->_is_principal?
' and a principal':
'') );
346 private function ReadCollectionFromDatabase() {
349 $this->collection = (object) array(
350 'collection_id' => -1,
351 'type' =>
'nonexistent',
352 'is_calendar' =>
false,
'is_principal' =>
false,
'is_addressbook' =>
false 355 $base_sql =
'SELECT collection.*, path_privs(:session_principal::int8, collection.dav_name,:scan_depth::int), ';
356 $base_sql .=
'p.principal_id, p.type_id AS principal_type_id, ';
357 $base_sql .=
'p.displayname AS principal_displayname, p.default_privileges AS principal_default_privileges, ';
358 $base_sql .=
'timezones.vtimezone ';
359 $base_sql .=
'FROM collection LEFT JOIN principal p USING (user_no) ';
360 $base_sql .=
'LEFT JOIN timezones ON (collection.timezone=timezones.tzid) ';
361 $base_sql .=
'WHERE ';
362 $sql = $base_sql .
'collection.dav_name = :raw_path ';
363 $params = array(
':raw_path' => $this->
dav_name,
':session_principal' => $session->principal_id,
':scan_depth' => $c->permission_scan_depth );
364 if ( !preg_match(
'#/$#', $this->
dav_name ) ) {
365 $sql .=
' OR collection.dav_name = :up_to_slash OR collection.dav_name = :plus_slash ';
366 $params[
':up_to_slash'] = preg_replace(
'#[^/]*$#',
'', $this->
dav_name);
367 $params[
':plus_slash'] = $this->
dav_name.
'/';
369 $sql .=
'ORDER BY LENGTH(collection.dav_name) DESC LIMIT 1';
370 $qry =
new AwlQuery( $sql, $params );
371 if ( $qry->Exec(
'DAVResource') && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
372 $this->collection = $row;
373 $this->collection->exists =
true;
374 if ( $row->is_calendar ==
't' )
375 $this->collection->type =
'calendar';
376 else if ( $row->is_addressbook ==
't' )
377 $this->collection->type =
'addressbook';
378 else if ( preg_match(
'#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->
dav_name, $matches ) )
379 $this->collection->type =
'schedule-'. $matches[3].
'box';
381 $this->collection->type =
'collection';
383 else if ( preg_match(
'{^( ( / ([^/]+) / ) \.(in|out)/ ) [^/]*$}x', $this->
dav_name, $matches ) ) {
385 $params = array(
':username' => $matches[3],
':parent_container' => $matches[2],
':dav_name' => $matches[1] );
386 $params[
':boxname'] = ($matches[4] ==
'in' ?
' Inbox' :
' Outbox');
387 $this->collection_type =
'schedule-'. $matches[4].
'box';
388 $params[
':resourcetypes'] = sprintf(
'<DAV::collection/><urn:ietf:params:xml:ns:caldav:%s/>', $this->collection_type );
390 INSERT INTO collection (
user_no, parent_container,
dav_name, dav_displayname, is_calendar, created, modified, dav_etag, resourcetypes )
391 VALUES( (SELECT
user_no FROM usr WHERE username = text(:username)),
393 (SELECT fullname FROM usr WHERE username = text(:username)) || :boxname,
394 FALSE, current_timestamp, current_timestamp,
'1', :resourcetypes )
396 $qry =
new AwlQuery( $sql, $params );
397 $qry->Exec(
'DAVResource');
398 dbg_error_log(
'DAVResource',
'Created new collection as "%s".', trim($params[
':boxname']) );
400 $params = array(
':raw_path' => $this->
dav_name,
':session_principal' => $session->principal_id,
':scan_depth' => $c->permission_scan_depth );
401 $qry =
new AwlQuery( $base_sql .
' dav_name = :raw_path', $params );
402 if ( $qry->Exec(
'DAVResource') && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
403 $this->collection = $row;
404 $this->collection->exists =
true;
405 $this->collection->type = $this->collection_type;
408 else if ( preg_match(
'#^(/([^/]+)/calendar-proxy-(read|write))/?[^/]*$#', $this->
dav_name, $matches ) ) {
409 $this->collection->type =
'proxy';
410 $this->_is_proxy_resource =
true;
411 $this->proxy_type = $matches[3];
412 $this->collection->dav_name = $this->dav_name;
413 $this->collection->dav_displayname = sprintf(
'%s proxy %s', $matches[2], $matches[3] );
414 $this->collection->exists =
true;
415 $this->collection->parent_container =
'/' . $matches[2] .
'/';
417 else if ( preg_match(
'#^(/[^/]+)/?$#', $this->
dav_name, $matches)
418 || preg_match(
'#^((/principals/[^/]+/)[^/]+)/?$#', $this->
dav_name, $matches) ) {
419 $this->_is_principal =
true;
421 $this->collection->is_principal =
true;
422 $this->collection->type =
'principal';
424 else if ( $this->
dav_name ==
'/' ) {
425 $this->collection->dav_name =
'/';
426 $this->collection->type =
'root';
427 $this->collection->exists =
true;
428 $this->collection->displayname = $c->system_name;
429 $this->collection->default_privileges = (1 | 16 | 32);
430 $this->collection->parent_container =
'/';
434 SELECT collection.*, path_privs(:session_principal::int8, collection.dav_name,:scan_depth::int), p.principal_id,
435 p.type_id AS principal_type_id, p.displayname AS principal_displayname, p.default_privileges AS principal_default_privileges,
436 timezones.vtimezone, dav_binding.access_ticket_id, dav_binding.parent_container AS bind_parent_container,
437 dav_binding.dav_displayname, owner.dav_name AS bind_owner_url, dav_binding.dav_name AS bound_to,
438 dav_binding.external_url AS external_url, dav_binding.type AS external_type, dav_binding.bind_id AS bind_id
440 LEFT JOIN collection ON (collection.collection_id=bound_source_id)
441 LEFT JOIN principal p USING (
user_no)
442 LEFT JOIN dav_principal owner ON (dav_binding.dav_owner_id=owner.principal_id)
443 LEFT JOIN timezones ON (collection.timezone=timezones.tzid)
444 WHERE dav_binding.dav_name = :raw_path
446 $params = array(
':raw_path' => $this->
dav_name,
':session_principal' => $session->principal_id,
':scan_depth' => $c->permission_scan_depth );
447 if ( !preg_match(
'#/$#', $this->
dav_name ) ) {
448 $sql .=
' OR dav_binding.dav_name = :up_to_slash OR collection.dav_name = :plus_slash OR dav_binding.dav_name = :plus_slash ';
449 $params[
':up_to_slash'] = preg_replace(
'#[^/]*$#',
'', $this->
dav_name);
450 $params[
':plus_slash'] = $this->
dav_name.
'/';
452 $sql .=
' ORDER BY LENGTH(dav_binding.dav_name) DESC LIMIT 1';
453 $qry =
new AwlQuery( $sql, $params );
454 if ( $qry->Exec(
'DAVResource',__LINE__,__FILE__) && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
455 $this->collection = $row;
456 $this->collection->exists =
true;
457 $this->collection->parent_set = $row->parent_container;
458 $this->collection->parent_container = $row->bind_parent_container;
459 $this->collection->bound_from = $row->dav_name;
460 $this->collection->dav_name = $row->bound_to;
461 if ( $row->is_calendar ==
't' )
462 $this->collection->type =
'calendar';
463 else if ( $row->is_addressbook ==
't' )
464 $this->collection->type =
'addressbook';
465 else if ( preg_match(
'#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->
dav_name, $matches ) )
466 $this->collection->type =
'schedule-'. $matches[3].
'box';
468 $this->collection->type =
'collection';
469 if ( strlen($row->external_url) > 8 ) {
470 $this->_is_external =
true;
471 if ( $row->external_type ==
'calendar' )
472 $this->collection->type =
'calendar';
473 else if ( $row->external_type ==
'addressbook' )
474 $this->collection->type =
'addressbook';
476 $this->collection->type =
'collection';
478 $this->_is_binding =
true;
479 $this->
bound_from = str_replace( $row->bound_to, $row->dav_name, $this->dav_name);
480 if ( isset($row->access_ticket_id) ) {
481 if ( !isset($this->tickets) ) $this->tickets = array();
482 $this->tickets[] =
new DAVTicket($row->access_ticket_id);
486 dbg_error_log(
'DAVResource',
'No collection for path "%s".', $this->
dav_name );
487 $this->collection->exists =
false;
488 $this->collection->dav_name = preg_replace(
'{/[^/]*$}',
'/', $this->
dav_name);
511 dbg_error_log(
'DAVResource',
':FetchCollection: Looking for collection for "%s".', $this->
dav_name );
514 $cache = getCacheInstance();
515 $cache_ns =
'collection-'.preg_replace(
'{/[^/]*$}',
'/', $this->
dav_name);
516 $cache_key =
'dav_resource'.$session->user_no;
517 $this->collection = $cache->get( $cache_ns, $cache_key );
518 if ( $this->collection ===
false ) {
519 $this->ReadCollectionFromDatabase();
520 if ( $this->collection->type !=
'principal' ) {
521 $cache_ns =
'collection-'.$this->collection->dav_name;
522 @dbg_error_log(
'Cache',
':FetchCollection: Setting cache ns "%s" key "%s". Type: %s', $cache_ns, $cache_key, $this->collection->type );
523 $cache->set( $cache_ns, $cache_key, $this->collection );
525 @dbg_error_log(
'DAVResource',
':FetchCollection: Found collection named "%s" of type "%s".', $this->collection->dav_name, $this->collection->type );
528 @dbg_error_log(
'Cache',
':FetchCollection: Got cache ns "%s" key "%s". Type: %s', $cache_ns, $cache_key, $this->collection->type );
529 if ( preg_match(
'#^(/[^/]+)/?$#', $this->
dav_name, $matches)
530 || preg_match(
'#^((/principals/[^/]+/)[^/]+)/?$#', $this->
dav_name, $matches) ) {
531 $this->_is_principal =
true;
533 $this->collection->is_principal =
true;
534 $this->collection->type =
'principal';
536 @dbg_error_log(
'DAVResource',
':FetchCollection: Read cached collection named "%s" of type "%s".', $this->collection->dav_name, $this->collection->type );
539 if ( isset($this->collection->bound_from) ) {
540 $this->_is_binding =
true;
541 $this->
bound_from = str_replace( $this->collection->bound_to, $this->collection->bound_from, $this->dav_name);
542 if ( isset($this->collection->access_ticket_id) ) {
543 if ( !isset($this->tickets) ) $this->tickets = array();
544 $this->tickets[] =
new DAVTicket($this->collection->access_ticket_id);
548 $this->_is_collection = ( $this->_is_principal || $this->collection->dav_name == $this->
dav_name || $this->collection->dav_name == $this->
dav_name.
'/' );
549 if ( $this->_is_collection ) {
550 $this->
dav_name = $this->collection->dav_name;
551 $this->
resource_id = $this->collection->collection_id;
552 $this->_is_calendar = ($this->collection->type ==
'calendar');
553 $this->_is_addressbook = ($this->collection->type ==
'addressbook');
554 $this->contenttype =
'httpd/unix-directory';
555 if ( !isset($this->exists) && isset($this->collection->exists) ) {
557 $this->exists = $this->collection->exists;
559 if ( $this->exists ) {
560 if ( isset($this->collection->dav_etag) ) $this->
unique_tag =
'"'.$this->collection->dav_etag.
'"';
561 if ( isset($this->collection->created) ) $this->created = $this->collection->created;
562 if ( isset($this->collection->modified) ) $this->modified = $this->collection->modified;
563 if ( isset($this->collection->dav_displayname) ) $this->collection->displayname = $this->collection->dav_displayname;
567 $this->
user_no = $this->parent->GetProperty(
'user_no');
569 if ( isset($this->collection->resourcetypes) )
570 $this->resourcetypes = $this->collection->resourcetypes;
572 $this->resourcetypes =
'<DAV::collection/>';
573 if ( $this->_is_principal ) $this->resourcetypes .=
'<DAV::principal/>';
574 if ( $this->_is_addressbook ) $this->resourcetypes .=
'<urn:ietf:params:xml:ns:carddav:addressbook/>';
575 if ( $this->_is_calendar ) $this->resourcetypes .=
'<urn:ietf:params:xml:ns:caldav:calendar/>';
585 if ( isset($this->principal) )
return;
587 if ( $this->_is_principal ) {
588 $this->exists = $this->principal->Exists();
589 $this->collection->dav_name = $this->
dav_name();
590 $this->collection->type =
'principal';
591 if ( $this->exists ) {
592 $this->collection = $this->principal->AsCollection();
593 $this->displayname = $this->principal->GetProperty(
'displayname');
594 $this->
user_no = $this->principal->user_no();
595 $this->
resource_id = $this->principal->principal_id();
596 $this->created = $this->principal->created;
597 $this->modified = $this->principal->modified;
598 $this->resourcetypes = $this->principal->resourcetypes;
608 if ( isset($this->exists) )
return;
609 if ( $this->_is_collection )
return;
612 SELECT calendar_item.*, addressbook_resource.*, caldav_data.*
613 FROM caldav_data LEFT OUTER JOIN calendar_item USING (
collection_id,dav_id)
614 LEFT OUTER JOIN addressbook_resource USING (dav_id)
615 WHERE caldav_data.dav_name = :
dav_name 617 $params = array(
':dav_name' => $this->
bound_from() );
619 $qry =
new AwlQuery( $sql, $params );
620 if ( $qry->Exec(
'DAVResource') && $qry->rows() > 0 ) {
621 $this->exists =
true;
622 $row = $qry->Fetch();
626 $this->exists =
false;
635 if ( isset($this->dead_properties) )
return;
637 $this->dead_properties = array();
638 if ( !$this->exists || !$this->_is_collection )
return;
640 $qry =
new AwlQuery(
'SELECT property_name, property_value FROM property WHERE dav_name= :dav_name', array(
':dav_name' => $this->
dav_name) );
641 if ( $qry->Exec(
'DAVResource') ) {
642 while ( $property = $qry->Fetch() ) {
643 $this->dead_properties[$property->property_name] = self::BuildDeadPropertyXML($property->property_name,$property->property_value);
655 if ( !preg_match(
'{^\s*<.*>\s*$}s', $raw_string) )
return $raw_string;
657 if ( preg_match(
'{^(.*):([^:]+)$}', $property_name, $matches) ) {
658 $xmlns = $matches[1];
659 $property_name = $matches[2];
661 $xml = sprintf(
'<%s%s>%s</%s>', $property_name, (isset($xmlns)?
' xmlns="'.$xmlns.
'"':
''), $raw_string, $property_name);
662 $xml_parser = xml_parser_create_ns(
'UTF-8');
664 xml_parser_set_option ( $xml_parser, XML_OPTION_SKIP_WHITE, 1 );
665 xml_parser_set_option ( $xml_parser, XML_OPTION_CASE_FOLDING, 0 );
666 $rc = xml_parse_into_struct( $xml_parser, $xml, $xml_tags );
667 if ( $rc ==
false ) {
668 $errno = xml_get_error_code($xml_parser);
669 dbg_error_log(
'ERROR',
'XML parsing error: %s (%d) at line %d, column %d',
670 xml_error_string($errno), $errno,
671 xml_get_current_line_number($xml_parser), xml_get_current_column_number($xml_parser) );
672 dbg_error_log(
'ERROR',
"Error occurred in:\n%s\n",$xml);
673 if ($errno >= 200 && $errno < 300 && count($xml_tags) >= 3) {
675 dbg_error_log(
'ERROR',
'XML namespace error but tags extracted, trying to continue');
680 xml_parser_free($xml_parser);
682 $xmltree = BuildXMLTree( $xml_tags, $position);
683 return $xmltree->GetContent();
690 global $session, $request;
692 if ( $this->
dav_name ==
'/' || $this->
dav_name ==
'' || $this->_is_external ) {
693 $this->privileges = (1 | 16 | 32);
694 dbg_error_log(
'DAVResource',
':FetchPrivileges: Read permissions for user accessing /' );
698 if ( $session->AllowedTo(
'Admin') ) {
699 $this->privileges = privilege_to_bits(
'all');
700 dbg_error_log(
'DAVResource',
':FetchPrivileges: Full permissions for an administrator.' );
706 $this->privileges = $this->principal->Privileges();
707 dbg_error_log(
'DAVResource',
':FetchPrivileges: Privileges of "%s" for user accessing principal "%s"', $this->privileges, $this->principal->username() );
712 $this->privileges = 0;
713 if ( !isset($this->collection->path_privs) ) {
716 $this->collection->path_privs = $this->parent->Privileges();
717 $this->collection->user_no = $this->parent->GetProperty(
'user_no');
718 $this->collection->principal_id = $this->parent->GetProperty(
'principal_id');
721 $this->privileges = $this->collection->path_privs;
722 if ( is_string($this->privileges) ) $this->privileges = bindec( $this->privileges );
724 dbg_error_log(
'DAVResource',
':FetchPrivileges: Privileges of "%s" for user "%s" accessing "%s"',
725 decbin($this->privileges), $session->username, $this->dav_name() );
727 if ( isset($request->ticket) && $request->ticket->MatchesPath($this->
bound_from()) ) {
728 $this->privileges |= $request->ticket->privileges();
729 dbg_error_log(
'DAVResource',
':FetchPrivileges: Applying permissions for ticket "%s" now: %s', $request->ticket->id(), decbin($this->privileges) );
732 if ( isset($this->tickets) ) {
734 foreach( $this->tickets AS $k => $ticket ) {
735 if ( $ticket->MatchesResource($this->resource_id()) || $ticket->MatchesPath($this->bound_from()) ) {
736 $this->privileges |= $ticket->privileges();
737 dbg_error_log(
'DAVResource',
':FetchPrivileges: Applying permissions for ticket "%s" now: %s', $ticket->id(), decbin($this->privileges) );
748 if ( $this->
dav_name ==
'/' )
return null;
749 if ( !isset($this->parent) ) {
750 if ( $this->_is_collection ) {
751 dbg_error_log(
'DAVResource',
'Retrieving "%s" - parent of "%s" (dav_name: %s)', $this->
parent_path(), $this->collection->dav_name, $this->dav_name() );
755 dbg_error_log(
'DAVResource',
'Retrieving "%s" - parent of "%s" (dav_name: %s)', $this->
parent_path(), $this->collection->dav_name, $this->dav_name() );
756 $this->parent =
new DAVResource($this->collection->dav_name);
759 return $this->parent;
768 deprecated(
'DAVResource::FetchParentContainer');
778 return $this->privileges;
790 if ( !isset($any) ) $any = ($do_what !=
'all');
791 $test_bits = privilege_to_bits( $do_what );
792 dbg_error_log(
'DAVResource',
'Testing %s privileges of "%s" (%s) against allowed "%s" => "%s" (%s)', ($any?
'any':
'exactly'),
793 $do_what, decbin($test_bits), decbin($this->privileges), ($this->privileges & $test_bits), decbin($this->privileges & $test_bits) );
795 return ($this->privileges & $test_bits) > 0;
798 return ($this->privileges & $test_bits) == $test_bits;
817 $request->NeedPrivilege( $privilege, $this->
dav_name );
826 if ( $privilege_names == null ) {
828 $privilege_names = bits_to_privilege($this->privileges, ($this->_is_collection ? $this->collection->type : null ) );
830 return privileges_to_XML( $privilege_names, $xmldoc);
838 if ( isset($this->supported_methods) )
return $this->supported_methods;
840 $this->supported_methods = array(
854 switch ( $this->collection->type ) {
858 $this->supported_methods = array(
865 case 'schedule-outbox':
866 $this->supported_methods = array_merge(
867 $this->supported_methods,
869 'POST' =>
'',
'PROPPATCH' =>
'',
'MKTICKET' =>
'',
'DELTICKET' =>
'' 873 case 'schedule-inbox':
875 $this->supported_methods[
'GET'] =
'';
876 $this->supported_methods[
'PUT'] =
'';
877 $this->supported_methods[
'HEAD'] =
'';
878 $this->supported_methods[
'MKTICKET'] =
'';
879 $this->supported_methods[
'DELTICKET'] =
'';
880 $this->supported_methods[
'ACL'] =
'';
883 $this->supported_methods[
'MKTICKET'] =
'';
884 $this->supported_methods[
'DELTICKET'] =
'';
885 $this->supported_methods[
'BIND'] =
'';
886 $this->supported_methods[
'ACL'] =
'';
888 $this->supported_methods[
'GET'] =
'';
889 $this->supported_methods[
'HEAD'] =
'';
890 $this->supported_methods[
'MKCOL'] =
'';
891 $this->supported_methods[
'MKCALENDAR'] =
'';
892 $this->supported_methods[
'PROPPATCH'] =
'';
893 $this->supported_methods[
'BIND'] =
'';
894 $this->supported_methods[
'ACL'] =
'';
899 $this->supported_methods = array_merge(
900 $this->supported_methods,
902 'GET' =>
'',
'HEAD' =>
'',
'PUT' =>
'',
'MKTICKET' =>
'',
'DELTICKET' =>
'' 907 return $this->supported_methods;
917 foreach( $this->supported_methods AS $k => $v ) {
919 $methods[] =
new XMLElement(
'supported-method', null, array(
'name' => $k) );
929 if ( isset($this->supported_reports) )
return $this->supported_reports;
931 $this->supported_reports = array(
932 'DAV::principal-property-search' =>
'',
933 'DAV::principal-search-property-set' =>
'',
934 'DAV::expand-property' =>
'',
935 'DAV::principal-match' =>
'',
936 'DAV::sync-collection' =>
'' 941 if ( $this->collection->is_calendar ) {
942 $this->supported_reports = array_merge(
943 $this->supported_reports,
945 'urn:ietf:params:xml:ns:caldav:calendar-query' =>
'',
946 'urn:ietf:params:xml:ns:caldav:calendar-multiget' =>
'',
947 'urn:ietf:params:xml:ns:caldav:free-busy-query' =>
'' 951 if ( $this->collection->is_addressbook ) {
952 $this->supported_reports = array_merge(
953 $this->supported_reports,
955 'urn:ietf:params:xml:ns:carddav:addressbook-query' =>
'',
956 'urn:ietf:params:xml:ns:carddav:addressbook-multiget' =>
'' 960 return $this->supported_reports;
970 foreach( $this->supported_reports AS $k => $v ) {
971 dbg_error_log(
'DAVResource',
':BuildSupportedReports: Adding supported report "%s" which is "%s".', $k, $v );
972 $report =
new XMLElement(
'report');
973 $reply->NSElement($report, $k );
974 $reports[] =
new XMLElement(
'supported-report', $report );
985 if ( isset($this->access_tickets) )
return;
986 $this->access_tickets = array();
989 'SELECT access_ticket.*, COALESCE( resource.dav_name, collection.dav_name) AS target_dav_name, 990 (access_ticket.expires < current_timestamp) AS expired, 991 dav_principal.dav_name AS principal_dav_name, 992 EXTRACT( \'epoch\' FROM (access_ticket.expires - current_timestamp)) AS seconds, 993 path_privs(access_ticket.dav_owner_id,collection.dav_name,:scan_depth) AS grantor_collection_privileges 994 FROM access_ticket JOIN collection ON (target_collection_id = collection_id) 995 JOIN dav_principal ON (dav_owner_id = principal_id) 996 LEFT JOIN caldav_data resource ON (resource.dav_id = access_ticket.target_resource_id) 997 WHERE target_collection_id = :collection_id ';
998 $params = array(
':collection_id' => $this->collection->collection_id,
':scan_depth' => $c->permission_scan_depth);
1000 $sql .=
'AND target_resource_id IS NULL';
1004 $sql .=
'AND target_resource_id = :dav_id';
1005 $params[
':dav_id'] = $this->
resource->dav_id;
1007 if ( isset($this->exists) && !$this->exists )
return;
1009 $qry =
new AwlQuery( $sql, $params );
1010 if ( $qry->Exec(
'DAVResource',__LINE__,__FILE__) && $qry->rows() ) {
1011 while( $ticket = $qry->Fetch() ) {
1012 $this->access_tickets[] = $ticket;
1029 global $session, $request;
1031 if ( !isset($this->access_tickets) ) $this->
FetchTickets();
1034 foreach( $this->access_tickets AS $meh => $trow ) {
1035 if ( !$show_all && ( $trow->dav_owner_id == $session->principal_id || $request->ticket->id() == $trow->ticket_id ) )
continue;
1036 dbg_error_log(
'DAVResource',
':BuildTicketinfo: Adding access_ticket "%s" which is "%s".', $trow->ticket_id, $trow->privileges );
1037 $ticket =
new XMLElement( $reply->Tag(
'ticketinfo',
'http://www.xythos.com/namespaces/StorageServer',
'TKT' ) );
1038 $reply->NSElement($ticket,
'http://www.xythos.com/namespaces/StorageServer:id', $trow->ticket_id );
1039 $reply->NSElement($ticket,
'http://www.xythos.com/namespaces/StorageServer:owner', $reply->href( ConstructURL($trow->principal_dav_name)) );
1040 $reply->NSElement($ticket,
'http://www.xythos.com/namespaces/StorageServer:timeout', (isset($trow->seconds) ? sprintf(
'Seconds-%d', $trow->seconds) :
'infinity') );
1041 $reply->NSElement($ticket,
'http://www.xythos.com/namespaces/StorageServer:visits',
'infinity' );
1043 foreach( bits_to_privilege(bindec($trow->privileges) & bindec($trow->grantor_collection_privileges) ) AS $k => $v ) {
1044 $privs[] = $reply->NewXMLElement($v);
1046 $reply->NSElement($ticket,
'DAV::privilege', $privs );
1047 $tickets[] = $ticket;
1061 if ( !isset($this->_locks_found) ) {
1062 $this->_locks_found = array();
1066 $sql =
'SELECT * FROM locks WHERE :this_path::text ~ (\'^\'||dav_name||:match_end)::text';
1067 $qry =
new AwlQuery($sql, array(
':this_path' => $this->
dav_name,
':match_end' => ($depth == DEPTH_INFINITY ?
'' :
'$') ) );
1068 if ( $qry->Exec(
'DAVResource',__LINE__,__FILE__) ) {
1069 while( $lock_row = $qry->Fetch() ) {
1070 $this->_locks_found[$lock_row->opaquelocktoken] = $lock_row;
1074 $this->DoResponse(500,i18n(
"Database Error"));
1079 foreach( $this->_locks_found AS $lock_token => $lock_row ) {
1080 if ( $lock_row->depth == DEPTH_INFINITY || $lock_row->dav_name == $this->dav_name ) {
1093 return $this->_is_collection;
1101 return $this->_is_collection && $this->_is_principal;
1109 return $this->_is_collection && $this->_is_calendar;
1118 if ( $this->_is_proxy_resource ) {
1119 return ($type ==
'any' || $type == $this->proxy_type);
1130 if ( $this->_is_collection && preg_match(
'{schedule-(inbox|outbox)}', $this->collection->type, $matches ) ) {
1131 return ($type ==
'any' || $type == $matches[1]);
1142 if ( !$this->_is_collection && preg_match(
'{schedule-(inbox|outbox)}', $this->collection->type, $matches ) ) {
1143 return ($type ==
'any' || $type == $matches[1]);
1153 return $this->_is_collection && $this->_is_addressbook;
1161 return $this->_is_binding;
1169 return $this->_is_external;
1177 if ( ! isset($this->exists) ) {
1180 $this->exists = $this->principal->Exists();
1187 return $this->exists;
1195 if ( $this->collection->dav_name != $this->dav_name ) {
1196 return $this->collection->exists;
1199 return $parent->Exists();
1209 throw Exception(
"What! How can dav_name not be set?");
1211 return ConstructURL($this->
dav_name);
1220 if ( isset($this->
dav_name) )
return $this->dav_name;
1230 if ( isset($this->
bound_from) )
return $this->bound_from;
1243 return $this->dav_name;
1253 if ( !isset($this->collection->parent_container) ) {
1254 $this->collection->parent_container = preg_replace(
'{[^/]+/$}',
'', $this->
bound_from());
1256 return $this->collection->parent_container;
1258 return preg_replace(
'{[^/]+$}',
'', $this->
bound_from());
1268 return $this->principal->url();
1277 return $this->principal->user_no();
1286 return $this->collection->collection_id;
1295 return $this->resource;
1303 if ( isset($this->
unique_tag) )
return $this->unique_tag;
1304 if ( $this->
IsPrincipal() && !isset($this->principal) ) {
1306 $this->
unique_tag = $this->principal->unique_tag();
1312 return $this->unique_tag;
1320 if ( isset($this->
resource_id) )
return $this->resource_id;
1326 return $this->resource_id;
1334 dbg_error_log(
'DAVResource',
'Request for a%scached sync-token', ($cachedOK ?
' ' :
'n un') );
1337 if ( !isset($this->
sync_token) || !$cachedOK ) {
1338 $sql =
'SELECT new_sync_token( 0, :collection_id) AS sync_token';
1339 $params = array(
':collection_id' => $this->
collection_id());
1340 $qry =
new AwlQuery($sql, $params );
1341 if ( !$qry->Exec() || !$row = $qry->Fetch() ) {
1342 if ( !$qry->QDo(
'SELECT new_sync_token( 0, :collection_id) AS sync_token', $params) )
throw new Exception(
'Problem with database query');
1343 $row = $qry->Fetch();
1345 $this->
sync_token =
'data:,'.$row->sync_token;
1347 dbg_error_log(
'DAVResource',
'Returning sync token of "%s"', $this->
sync_token );
1348 return $this->sync_token;
1355 return ( isset($this->collection->publicly_readable) && $this->collection->publicly_readable ==
't' );
1363 return ( isset($this->collection->publicly_events_only) && $this->collection->publicly_events_only ==
't' );
1372 if ( !$this->
IsCollection() )
return $this->collection->type;
1374 if ( ! isset($this->collection->parent_container) )
return null;
1376 if ( isset($this->parent_container_type) )
return $this->parent_container_type;
1378 if ( preg_match(
'#/[^/]+/#', $this->collection->parent_container) ) {
1379 $this->parent_container_type =
'principal';
1382 $qry =
new AwlQuery(
'SELECT * FROM collection WHERE dav_name = :parent_name',
1383 array(
':parent_name' => $this->collection->parent_container ) );
1384 if ( $qry->Exec(
'DAVResource') && $qry->rows() > 0 && $parent = $qry->Fetch() ) {
1385 if ( $parent->is_calendar ==
't' )
1386 $this->parent_container_type =
'calendar';
1387 else if ( $parent->is_addressbook ==
't' )
1388 $this->parent_container_type =
'addressbook';
1389 else if ( preg_match(
'#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->
dav_name, $matches ) )
1390 $this->parent_container_type =
'schedule-'. $matches[3].
'box';
1392 $this->parent_container_type =
'collection';
1395 $this->parent_container_type = null;
1397 return $this->parent_container_type;
1405 $privilege_names = bits_to_privilege($privs, ($this->_is_collection ? $this->collection->type :
'resource'));
1406 $privileges = array();
1407 foreach( $privilege_names AS $k ) {
1408 $privilege =
new XMLElement(
'privilege');
1409 if ( isset($xmldoc) )
1410 $xmldoc->NSElement($privilege,$k);
1412 $privilege->NewElement($k);
1413 $privileges[] = $privilege;
1415 $ace =
new XMLElement(
'ace', array(
1416 new XMLElement(
'principal', $principal),
1417 new XMLElement(
'grant', $privileges ) )
1427 $default_privs = $this->principal->default_privileges;
1428 if ( isset($this->collection->default_privileges) ) $default_privs = $this->collection->default_privileges;
1431 $acl[] = $this->
BuildACE($xmldoc, pow(2,25) - 1,
new XMLElement(
'property',
new XMLElement(
'owner')) );
1433 $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',
1434 array(
':collection_id' => $this->collection->collection_id,
1435 ':principal_id' => $this->principal->principal_id() ) );
1436 if ( $qry->Exec(
'DAVResource') && $qry->rows() > 0 ) {
1437 $by_collection = null;
1438 while( $grant = $qry->Fetch() ) {
1439 if ( !isset($by_collection) ) $by_collection = isset($grant->by_collection);
1440 if ( $by_collection && !isset($grant->by_collection) )
break;
1441 $acl[] = $this->
BuildACE($xmldoc, $grant->privileges, $xmldoc->href(ConstructURL($grant->dav_name)) );
1445 $acl[] = $this->
BuildACE($xmldoc, $default_privs,
new XMLElement(
'authenticated') );
1460 case 'collection_id':
1464 case 'principal_id':
1466 return $this->principal->principal_id();
1469 case 'resourcetype':
1470 if ( isset($this->resourcetypes) ) {
1471 $this->resourcetypes = preg_replace(
'{^\s*<(.*)/>\s*$}',
'$1', $this->resourcetypes);
1472 $type_list = preg_split(
'{(/>\s*<|\n)}', $this->resourcetypes);
1473 foreach( $type_list AS $k => $resourcetype ) {
1474 if ( preg_match(
'{^([^:]+):([^:]+) \s+ xmlns:([^=]+)="([^"]+)" \s* $}x', $resourcetype, $matches ) ) {
1475 $type_list[$k] = $matches[4] .
':' .$matches[2];
1477 else if ( preg_match(
'{^([^:]+) \s+ xmlns="([^"]+)" \s* $}x', $resourcetype, $matches ) ) {
1478 $type_list[$k] = $matches[2] .
':' .$matches[1];
1491 dbg_error_log(
'DAVResource',
':GetProperty: dav-data: fetched resource does%s exist.', ($this->exists?
'':
' not') );
1492 return $this->
resource->caldav_data;
1497 return clone($this->principal);
1501 if ( isset($this->{$name}) ) {
1502 if ( ! is_object($this->{$name}) )
return $this->{$name};
1503 return clone($this->{$name});
1505 if ( $this->_is_principal ) {
1507 if ( isset($this->principal->{$name}) )
return $this->principal->{$name};
1508 if ( isset($this->collection->{$name}) )
return $this->collection->{$name};
1510 else if ( $this->_is_collection ) {
1511 if ( isset($this->collection->{$name}) )
return $this->collection->{$name};
1512 if ( isset($this->principal->{$name}) )
return $this->principal->{$name};
1518 if ( isset($this->principal->{$name}) )
return $this->principal->{$name};
1519 if ( isset($this->collection->{$name}) )
return $this->collection->{$name};
1521 if ( isset($this->{$name}) ) {
1522 if ( ! is_object($this->{$name}) )
return $this->{$name};
1523 return clone($this->{$name});
1537 $allprop = array_merge( (isset($this->dead_properties)?$this->dead_properties:array()),
1538 (isset($include_properties)?$include_properties:array()),
1540 'DAV::getcontenttype',
'DAV::resourcetype',
'DAV::getcontentlength',
'DAV::displayname',
'DAV::getlastmodified',
1541 'DAV::creationdate',
'DAV::getetag',
'DAV::getcontentlanguage',
'DAV::supportedlock',
'DAV::lockdiscovery',
1542 'DAV::owner',
'DAV::principal-URL',
'DAV::current-user-principal',
1543 'urn:ietf:params:xml:ns:carddav:max-resource-size',
'urn:ietf:params:xml:ns:carddav:supported-address-data',
1544 'urn:ietf:params:xml:ns:carddav:addressbook-description',
'urn:ietf:params:xml:ns:carddav:addressbook-home-set' 1555 global $c, $session, $request;
1559 if ( $reply === null ) $reply = $GLOBALS[
'reply'];
1562 case 'DAV::allprop':
1564 $discarded = array();
1565 foreach( $property_list AS $k => $v ) {
1571 $prop->NewElement(
'href', ConstructURL($this->
dav_name) );
1574 case 'DAV::resource-id':
1576 $reply->DAVElement( $prop,
'resource-id', $reply->href(ConstructURL(
'/.resources/'.$this->
resource_id) ) );
1581 case 'DAV::parent-set':
1583 SELECT b.parent_container FROM dav_binding b JOIN collection c ON (b.bound_source_id=c.collection_id)
1584 WHERE regexp_replace( b.dav_name,
'^.*/', c.dav_name ) = :
bound_from 1586 $qry =
new AwlQuery($sql, array(
':bound_from' => $this->
bound_from() ) );
1588 if ( $qry->Exec(
'DAVResource',__LINE__,__FILE__) && $qry->rows() > 0 ) {
1589 while( $row = $qry->Fetch() ) {
1590 $parents[$row->parent_container] =
true;
1593 $parents[preg_replace(
'{(?<=/)[^/]+/?$}',
'',$this->
bound_from())] =
true;
1594 $parents[preg_replace(
'{(?<=/)[^/]+/?$}',
'',$this->
dav_name())] =
true;
1596 $parent_set = $reply->DAVElement( $prop,
'parent-set' );
1597 foreach( $parents AS $parent => $v ) {
1598 if ( preg_match(
'{^(.*)?/([^/]+)/?$}', $parent, $matches ) ) {
1599 $reply->DAVElement($parent_set,
'parent', array(
1600 new XMLElement(
'href', ConstructURL($matches[1])),
1601 new XMLElement(
'segment', $matches[2])
1604 else if ( $parent ==
'/' ) {
1605 $reply->DAVElement($parent_set,
'parent', array(
1606 new XMLElement(
'href',
'/'),
1607 new XMLElement(
'segment', ( ConstructURL(
'/') ==
'/caldav.php/' ?
'caldav.php' :
''))
1613 case 'DAV::getcontenttype':
1614 if ( !isset($this->contenttype) && !$this->_is_collection && !isset($this->
resource) ) $this->
FetchResource();
1615 $prop->NewElement(
'getcontenttype', $this->contenttype );
1618 case 'DAV::resourcetype':
1619 $resourcetypes = $prop->NewElement(
'resourcetype' );
1620 if ( $this->_is_collection ) {
1622 if ( !is_array($type_list) )
return true;
1624 foreach( $type_list AS $k => $v ) {
1625 if ( $v ==
'' )
continue;
1626 $reply->NSElement( $resourcetypes, $v );
1628 if ( $this->_is_binding ) {
1629 $reply->NSElement( $resourcetypes,
'http://xmlns.davical.org/davical:webdav-binding' );
1634 case 'DAV::getlastmodified':
1636 $reply->NSElement($prop, $tag, ISODateToHTTPDate($this->
GetProperty(
'modified')) );
1639 case 'DAV::creationdate':
1641 $reply->NSElement($prop, $tag, DateToISODate($this->
GetProperty(
'created'),
true) );
1644 case 'DAV::getcontentlength':
1645 if ( $this->_is_collection )
return false;
1648 $reply->NSElement($prop, $tag, strlen($this->
resource->caldav_data) );
1652 case 'DAV::getcontentlanguage':
1653 $locale = (isset($c->current_locale) ? $c->current_locale :
'');
1654 if ( isset($this->locale) && $this->locale !=
'' ) $locale = $this->locale;
1655 $reply->NSElement($prop, $tag, $locale );
1658 case 'DAV::acl-restrictions':
1659 $reply->NSElement($prop, $tag, array(
new XMLElement(
'grant-only'),
new XMLElement(
'no-invert') ) );
1662 case 'DAV::inherited-acl-set':
1663 $inherited_acls = array();
1664 if ( ! $this->_is_collection ) {
1665 $inherited_acls[] = $reply->href(ConstructURL($this->collection->dav_name));
1667 $reply->NSElement($prop, $tag, $inherited_acls );
1673 $reply->DAVElement( $prop,
'owner', $reply->href( ConstructURL($this->collection->bound_from )) );
1676 $reply->DAVElement( $prop,
'owner', $reply->href( ConstructURL(DeconstructURL($this->
principal_url())) ) );
1680 case 'DAV::add-member':
1681 if ( ! $this->_is_collection )
return false;
1682 if ( $this->_is_principal )
return false;
1683 if ( isset($c->post_add_member) && $c->post_add_member === false )
return false;
1684 $reply->DAVElement( $prop,
'add-member', $reply->href(ConstructURL(DeconstructURL($this->
url())).
'?add_member') );
1689 case 'DAV::alternate-URI-set':
1690 $reply->NSElement($prop, $tag );
1693 case 'DAV::getetag':
1694 if ( $this->_is_collection )
return false;
1695 $reply->NSElement($prop, $tag, $this->
unique_tag() );
1698 case 'http://calendarserver.org/ns/:getctag':
1699 if ( ! $this->_is_collection )
return false;
1700 $reply->NSElement($prop, $tag, $this->
unique_tag() );
1703 case 'DAV::sync-token':
1704 if ( ! $this->_is_collection )
return false;
1706 if ( empty($sync_token) )
return false;
1707 $reply->NSElement($prop, $tag, $sync_token );
1710 case 'http://calendarserver.org/ns/:calendar-proxy-read-for':
1711 $proxy_type =
'read';
1712 case 'http://calendarserver.org/ns/:calendar-proxy-write-for':
1713 if ( isset($c->disable_caldav_proxy) && $c->disable_caldav_proxy )
return false;
1714 if ( !isset($proxy_type) ) $proxy_type =
'write';
1717 $reply->CalendarserverElement($prop,
'calendar-proxy-'.$proxy_type.
'-for', $reply->href( $this->principal->ProxyFor($proxy_type) ) );
1720 case 'http://calendarserver.org/ns/:group-member-set':
1721 case 'DAV::group-member-set':
1722 if ( $this->_is_proxy_resource ) {
1724 if ( $this->proxy_type ==
'read' ) {
1725 $reply->DAVElement( $prop,
'group-member-set', $reply->href( $this->principal->ReadProxyGroup() ) );
1727 $reply->DAVElement( $prop,
'group-member-set', $reply->href( $this->principal->WriteProxyGroup() ) );
1734 case 'http://calendarserver.org/ns/:group-membership':
1735 case 'DAV::group-membership':
1736 if ( $this->_is_proxy_resource ) {
1738 $reply->NSElement($prop, $tag );
1744 case 'DAV::current-user-privilege-set':
1745 if ( $this->
HavePrivilegeTo(
'DAV::read-current-user-privilege-set') ) {
1753 case 'urn:ietf:params:xml:ns:caldav:supported-calendar-data':
1755 $reply->NSElement($prop, $tag,
'text/calendar' );
1758 case 'urn:ietf:params:xml:ns:caldav:supported-calendar-component-set':
1759 if ( ! $this->_is_collection )
return false;
1762 if ( isset($this->dead_properties[$tag]) ) {
1763 $set_of_components = $this->dead_properties[$tag];
1764 foreach( $set_of_components AS $k => $v ) {
1765 if ( preg_match(
'{(VEVENT|VTODO|VJOURNAL|VTIMEZONE|VFREEBUSY|VPOLL|VAVAILABILITY)}', $v, $matches) ) {
1766 $set_of_components[$k] = $matches[1];
1769 unset( $set_of_components[$k] );
1773 else if ( isset($c->default_calendar_components) && is_array($c->default_calendar_components) ) {
1774 $set_of_components = $c->default_calendar_components;
1777 $set_of_components = array(
'VEVENT',
'VTODO',
'VJOURNAL' );
1781 $set_of_components = array(
'VEVENT',
'VTODO',
'VFREEBUSY' );
1783 $components = array();
1784 foreach( $set_of_components AS $v ) {
1785 $components[] = $reply->NewXMLElement(
'comp',
'', array(
'name' => $v),
'urn:ietf:params:xml:ns:caldav');
1787 $reply->CalDAVElement($prop,
'supported-calendar-component-set', $components );
1790 case 'DAV::supported-method-set':
1794 case 'DAV::supported-report-set':
1798 case 'DAV::supportedlock':
1799 $prop->NewElement(
'supportedlock',
1800 new XMLElement(
'lockentry',
1802 new XMLElement(
'lockscope',
new XMLElement(
'exclusive')),
1803 new XMLElement(
'locktype',
new XMLElement(
'write')),
1809 case 'DAV::supported-privilege-set':
1810 $prop->NewElement(
'supported-privilege-set', $request->BuildSupportedPrivileges($reply) );
1813 case 'DAV::principal-collection-set':
1814 $prop->NewElement(
'principal-collection-set', $reply->href( ConstructURL(
'/') ) );
1817 case 'DAV::current-user-principal':
1818 $prop->NewElement(
'current-user-principal', $reply->href( ConstructURL(DeconstructURL($session->principal->url())) ) );
1821 case 'SOME-DENIED-PROPERTY':
1822 $denied[] = $reply->Tag($tag);
1825 case 'urn:ietf:params:xml:ns:caldav:calendar-timezone':
1826 if ( ! $this->_is_collection )
return false;
1827 if ( !isset($this->collection->vtimezone) || $this->collection->vtimezone ==
'' )
return false;
1829 $cal =
new iCalComponent();
1831 $cal->AddComponent(
new iCalComponent($this->collection->vtimezone) );
1832 $reply->NSElement($prop, $tag, $cal->Render() );
1835 case 'urn:ietf:params:xml:ns:carddav:address-data':
1836 case 'urn:ietf:params:xml:ns:caldav:calendar-data':
1837 if ( $this->_is_collection )
return false;
1838 if ( !isset($c->sync_resource_data_ok) || $c->sync_resource_data_ok == false )
return false;
1840 $reply->NSElement($prop, $tag, $this->
resource->caldav_data );
1843 case 'urn:ietf:params:xml:ns:carddav:max-resource-size':
1844 if ( ! $this->_is_collection || !$this->_is_addressbook )
return false;
1845 $reply->NSElement($prop, $tag, 65500 );
1848 case 'urn:ietf:params:xml:ns:carddav:supported-address-data':
1849 if ( ! $this->_is_collection || !$this->_is_addressbook )
return false;
1850 $address_data = $reply->NewXMLElement(
'address-data',
false,
1851 array(
'content-type' =>
'text/vcard',
'version' =>
'3.0'),
'urn:ietf:params:xml:ns:carddav');
1852 $reply->NSElement($prop, $tag, $address_data );
1857 $reply->NSElement($prop, $tag, $this->
GetACL( $reply ) );
1864 case 'http://www.xythos.com/namespaces/StorageServer:ticketdiscovery':
1865 case 'DAV::ticketdiscovery':
1866 $reply->NSElement($prop,
'http://www.xythos.com/namespaces/StorageServer:ticketdiscovery', $this->
BuildTicketinfo($reply) );
1870 $property_value = $this->
GetProperty(preg_replace(
'{^(DAV:|urn:ietf:params:xml:ns:ca(rd|l)dav):}',
'', $tag));
1871 if ( isset($property_value) ) {
1872 $reply->NSElement($prop, $tag, $property_value );
1876 if ( isset($this->dead_properties[$tag]) ) {
1877 $reply->NSElement($prop, $tag, $this->dead_properties[$tag] );
1900 dbg_error_log(
'DAVResource',
':GetPropStat: propstat for href "%s"', $this->
dav_name );
1902 $prop =
new XMLElement(
'prop', null, null,
'DAV:');
1904 $not_found = array();
1905 foreach( $properties AS $k => $tag ) {
1906 if ( is_object($tag) ) {
1907 dbg_error_log(
'DAVResource',
':GetPropStat: "$properties" should be an array of text. Assuming this object is an XMLElement!.' );
1908 $tag = $tag->GetNSTag();
1913 $found = $this->principal->PrincipalProperty( $tag, $prop, $reply, $denied );
1917 $not_found[] = $tag;
1920 if ( $props_only )
return $prop;
1922 $status =
new XMLElement(
'status',
'HTTP/1.1 200 OK', null,
'DAV:' );
1924 $elements = array(
new XMLElement(
'propstat', array($prop,$status), null,
'DAV:' ) );
1926 if ( count($denied) > 0 ) {
1927 $status =
new XMLElement(
'status',
'HTTP/1.1 403 Forbidden', null,
'DAV:' );
1928 $noprop =
new XMLElement(
'prop', null, null,
'DAV:');
1929 foreach( $denied AS $k => $v ) {
1930 $reply->NSElement($noprop, $v);
1932 $elements[] =
new XMLElement(
'propstat', array( $noprop, $status), null,
'DAV:' );
1935 if ( !$request->PreferMinimal() && count($not_found) > 0 ) {
1936 $status =
new XMLElement(
'status',
'HTTP/1.1 404 Not Found', null,
'DAV:' );
1937 $noprop =
new XMLElement(
'prop', null, null,
'DAV:');
1938 foreach( $not_found AS $k => $v ) {
1939 $reply->NSElement($noprop,$v);
1941 $elements[] =
new XMLElement(
'propstat', array( $noprop, $status), null,
'DAV:' );
1955 function RenderAsXML( $properties, &$reply, $bound_parent_path = null ) {
1956 dbg_error_log(
'DAVResource',
':RenderAsXML: Resource "%s" exists(%d)', $this->
dav_name, $this->
Exists() );
1958 if ( !$this->
Exists() )
return null;
1960 $elements = $this->
GetPropStat( $properties, $reply );
1961 if ( isset($bound_parent_path) ) {
1965 $dav_name = $this->dav_name;
1968 array_unshift( $elements, $reply->href(ConstructURL($dav_name)));
1970 $response =
new XMLElement(
'response', $elements, null,
'DAV:' );
IsProxyCollection( $type='any')
IsSchedulingCollection( $type='any')
__construct( $parameters=null)
sync_token( $cachedOK=true)
BuildSupportedReports(&$reply)
NeedPrivilege( $privilege, $any=null)
BuildACE(&$xmldoc, $privs, $principal)
RenderAsXML( $properties, &$reply, $bound_parent_path=null)
static BuildDeadPropertyXML($property_name, $raw_string)
set_bind_location( $new_dav_name)
BuildPrivileges( $privilege_names=null, &$xmldoc=null)
HavePrivilegeTo( $do_what, $any=null)
GetPropStat( $properties, &$reply, $props_only=false)
ResourceProperty( $tag, $prop, &$reply, &$denied)
IsInSchedulingCollection( $type='any')