From eff60f67c3cb46ba6ac1f5b641039ca5ae030d80 Mon Sep 17 00:00:00 2001 From: Andrew McMillan Date: Mon, 2 Nov 2009 22:21:32 +1300 Subject: [PATCH] Untested, but functionally sufficient for MOVE method now. --- inc/DAVResource.php | 209 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 186 insertions(+), 23 deletions(-) diff --git a/inc/DAVResource.php b/inc/DAVResource.php index e7fd4817..5a6ac908 100644 --- a/inc/DAVResource.php +++ b/inc/DAVResource.php @@ -24,8 +24,8 @@ function privilege_to_bits( $raw_privs ) { if ( gettype($raw_privs) == 'string' ) $raw_privs = array( $raw_privs ); foreach( $raw_privs AS $priv ) { - $priv = trim(strtolower(preg_replace( '/^.*:/', '', $priv))); - switch( $priv ) { + $trim_priv = trim(strtolower(preg_replace( '/^.*:/', '', $priv))); + switch( $trim_priv ) { case 'read' : $out_priv &= 4609; break; // 1 + 512 + 4096 case 'write' : $out_priv &= 198; break; // 2 + 4 + 64 + 128 case 'write-properties' : $out_priv &= 2; break; @@ -45,6 +45,9 @@ function privilege_to_bits( $raw_privs ) { case 'schedule-send-invite' : $out_priv &= 8192; break; case 'schedule-send-reply' : $out_priv &= 16384; break; case 'schedule-send-freebusy' : $out_priv &= 32768; break; + default: + dbg_error_log( 'ERROR', 'Cannot convert privilege of "%s" into bits.', $priv ); + } } return $out_priv; @@ -112,18 +115,13 @@ class DAVResource */ protected $exists; - /** - * @var The principal URL of the owner of the resource - */ - protected $principal_url; - /** * @var The unique etag associated with the current version of the resource */ protected $unique_tag; /** - * @var The actual resource content + * @var The actual resource content, if it exists and is not a collection */ protected $content; @@ -142,6 +140,11 @@ class DAVResource */ private $collection; + /** + * @var An object which is the principal for this resource, or would be if it existed. + */ + private $principal; + /** * @var A bit mask representing the current user's privileges towards this DAVResource */ @@ -167,6 +170,21 @@ class DAVResource */ private $_is_addressbook; + /** + * @var An array of the methods we support on this resource. + */ + private $supported_methods; + + /** + * @var An array of the reports we support on this resource. + */ + private $supported_reports; + + /** + * @var An array of the component types we support on this resource. + */ + private $supported_components; + /** * Constructor * @param mixed $parameters If null, an empty Resourced is created. @@ -176,9 +194,10 @@ class DAVResource function __construct( $parameters = null ) { $this->exists = null; $this->dav_name = null; - $this->principal_url = null; $this->unique_tag = null; $this->content = null; + $this->collection = null; + $this->principal = null; $this->resourcetype = null; $this->contenttype = null; $this->privileges = null; @@ -289,16 +308,21 @@ class DAVResource $qry = new AwlQuery( $sql, $params ); if ( $qry->Exec('DAVResource') && $qry->rows == 1 && ($row = $qry->Fetch()) ) { $this->collection = $row; - if ( $row->is_calendar == 't' ) $this->collection->type = 'calendar'; - else if ( $row->is_addressbook == 't' ) $this->collection->type = 'addressbook'; - else $this->collection->type = 'collection'; + if ( $row->is_calendar == 't' ) + $this->collection->type = 'calendar'; + else if ( $row->is_addressbook == 't' ) + $this->collection->type = 'addressbook'; + else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) ) + $this->collection->type = 'schedule-'. $matches[3]. 'box'; + else + $this->collection->type = 'collection'; } else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) ) { // The request is for a scheduling inbox or outbox (or something inside one) and we should auto-create it $params = array( ':user_no' => $session->user_no, ':parent_container' => $matches[2], ':dav_name' => $matches[1] ); $params['displayname'] = $session->fullname . ($matches[3] == 'in' ? ' Inbox' : ' Outbox'); - $this->collection_type = 'schedule-'. $matches[3]. 'box'; - $params['resourcetypes'] = sprintf('', $this->collection_type ); + $this->collection->type = 'schedule-'. $matches[3]. 'box'; + $params['resourcetypes'] = sprintf('', $this->collection->type ); $sql = <<Exec('DAVResource') && $qry->rows == 1 && ($row = $qry->Fetch()) ) { $this->collection = $row; - $this->collection->type = $this->collection_type; } } else if ( preg_match( '#^((/[^/]+/)calendar-proxy-(read|write))/?[^/]*$#', $this->dav_name, $matches ) ) { @@ -349,6 +372,15 @@ EOSQL; } + /** + * Find the principal associated with this resource. + */ + function FetchPrincipal() { + global $c, $session; + $this->principal = new CalDAVPrincipal( array( "path" => $this->dav_name ) ); + } + + /** * Retrieve the actual resource. */ @@ -364,8 +396,15 @@ SELECT * FROM caldav_data LEFT JOIN calendar_item USING (collection_id,dav_id) WHERE caldav_data.dav_name = :dav_name EOQRY; $params = array( ':dav_name' => $this->dav_name ); - $qry = new AwlQuery( $sql, $params ); + $qry = new AwlQuery( $sql, $params ); + if ( $qry->Exec('DAVResource') && $qry->rows > 0 ) { + $this->exists = true; + $this->resource = $qry->Fetch(); + } + else { + $this->exists = false; + } } @@ -407,7 +446,7 @@ EOQRY; /** * Returns the array of privilege names converted into XMLElements */ - function RenderPrivileges( $privilege_names=null, $xmldoc=null ) { + function BuildPrivileges( $privilege_names=null, $xmldoc=null ) { if ( $privilege_names == null ) { if ( !isset($this->privileges) ) $this->FetchPrivileges(); $privilege_names = bits_to_privilege($this->privileges); @@ -427,6 +466,76 @@ EOQRY; } + /** + * Returns the array of supported methods + */ + function FetchSupportedMethods( ) { + if ( isset($this->supported_methods) ) return $this->supported_methods; + if ( !isset($this->collection) ) $this->FetchCollection(); + + $this->supported_methods = array( + 'OPTIONS' => '', + 'PROPFIND' => '', + 'REPORT' => '', + 'DELETE' => '', + 'LOCK' => '', + 'UNLOCK' => '' + ); + if ( $this->IsCollection() ) { +/* if ( $this->IsPrincipal() ) { + $this->supported_methods['MKCALENDAR'] = ''; + $this->supported_methods['MKCOL'] = ''; + } */ + switch ( $this->collection->type ) { + case 'root': + case 'email': + // We just override the list completely here. + $this->supported_methods = array( + 'OPTIONS' => '', + 'PROPFIND' => '', + 'REPORT' => '' + ); + break; + case 'schedule-inbox': + case 'schedule-outbox': + $this->supported_methods = array_merge( + $this->supported_methods, + array( + 'POST' => '', 'GET' => '', 'PUT' => '', 'HEAD' => '', 'PROPPATCH' => '' + ) + ); + break; + case 'calendar': + $this->supported_methods['GET'] = ''; + $this->supported_methods['PUT'] = ''; + $this->supported_methods['HEAD'] = ''; + break; + case 'collection': + case 'principal': + $this->supported_methods['GET'] = ''; + $this->supported_methods['PUT'] = ''; + $this->supported_methods['HEAD'] = ''; + $this->supported_methods['MKCOL'] = ''; + $this->supported_methods['MKCALENDAR'] = ''; + $this->supported_methods['PROPPATCH'] = ''; + break; + } + } + else { + $this->supported_methods = array_merge( + $this->supported_methods, + array( + 'GET' => '', + 'HEAD' => '', + 'PUT' => '' + ) + ); + } + + return $this->supported_methods; + } + + /** * Checks whether the resource is locked, returning any lock token, or false * @@ -475,14 +584,68 @@ EOQRY; } + /** + * Returns the dav_name of the resource in our internal namespace + */ + function dav_name() { + if ( isset($this->dav_name) ) return $this->dav_name; + return null; + } + + + /** + * Returns the principal-URL for this resource + */ + function principal_url() { + if ( !isset($this->principal) ) $this->FetchPrincipal(); + if ( $this->principal->Exists() ) { + return $this->principal->principal_url; + } + return null; + } + + /** * Checks whether the target collection is publicly_readable */ function IsPublic() { - if ( isset($this->collection) && isset($this->collection->publicly_readable) && $this->collection->publicly_readable == 't' ) { - return true; + if ( !isset($this->collection) ) $this->FetchCollection(); + return ( isset($this->collection->publicly_readable) && $this->collection->publicly_readable == 't' ) { + } + + + /** + * Return the type of whatever contains this resource, or would if it existed. + */ + function ContainerType() { + if ( !isset($this->collection) ) $this->FetchCollection(); + if ( $this->IsPrincipal() ) return 'root'; + if ( !$this->IsCollection() ) return $this->collection->type; + + if ( ! isset($this->collection->parent_container) ) return null; + + if ( isset($this->parent_container_type) ) return $this->parent_container_type; + + if ( preg_match('#/[^/]+/#', $this->collection->parent_container) ) { + $this->parent_container_type = 'principal'; } - return false; + else { + $qry = new AwlQuery('SELECT * FROM collection WHERE dav_name = :parent_name', + array( ':parent_name' => $this->collection->parent_container ) ); + if ( $qry->Exec('DAVResource') && $qry->rows > 0 && $parent = $qry->Fetch() ) { + if ( $parent->is_calendar == 't' ) + $this->parent_container_type = 'calendar'; + else if ( $parent->is_addressbook == 't' ) + $this->parent_container_type = 'addressbook'; + else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) ) + $this->parent_container_type = 'schedule-'. $matches[3]. 'box'; + else + $this->parent_container_type = 'collection'; + } + else + $this->parent_container_type = null; + } + return $this->parent_container_type; } @@ -529,7 +692,7 @@ EOQRY; case 'DAV::owner': // After a careful reading of RFC3744 we see that this must be the principal-URL of the owner - $reply->DAVElement( $prop, 'owner', $reply->href( $this->principal_url) ); + $reply->DAVElement( $prop, 'owner', $reply->href( $this->principal_url() ) ); break; // Empty tag responses. @@ -553,7 +716,7 @@ EOQRY; case 'http://calendarserver.org/ns/:getctag': if ( $this->_is_collection ) { - $prop->NewElement('http://calendarserver.org/ns/:getctag', $this->etag ); + $prop->NewElement('http://calendarserver.org/ns/:getctag', $this->unique_tag ); } else { $not_found[] = $reply->Tag($tag); @@ -639,7 +802,7 @@ EOQRY; $not_found = array(); foreach( $properties AS $k => $tag ) { if ( ! $this->ResourceProperty($tag, $prop, $reply) ) { - dbg_error_log( 'principal', 'Request for unsupported property "%s" of principal "%s".', $tag, $this->username ); + dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of principal "%s".', $tag, $this->username ); $not_found[] = $reply->Tag($tag); } }