From 1f5961e1b14deaa853c23927b1e3103e5a203e88 Mon Sep 17 00:00:00 2001 From: Andrew McMillan Date: Mon, 26 Oct 2009 00:10:18 +1300 Subject: [PATCH] Under development. --- inc/DAVResource.php | 139 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 134 insertions(+), 5 deletions(-) diff --git a/inc/DAVResource.php b/inc/DAVResource.php index faf64bf1..81a3bf9f 100644 --- a/inc/DAVResource.php +++ b/inc/DAVResource.php @@ -6,9 +6,12 @@ * @subpackage Resource * @author Andrew McMillan * @copyright Morphoss Ltd -* @license http://gnu.org/copyleft/gpl.html GNU GPL v2 or later +* @license http://gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +require_once('AwlQuery.php'); + + /** * A class for things to do with a DAV Resource * @@ -56,6 +59,16 @@ class DAVResource */ private $_is_principal; + /** + * @var True if this resource is a calendar collection + */ + private $_is_calendar; + + /** + * @var True if this resource is an addressbook collection + */ + private $_is_addressbook; + /** * Constructor * @param mixed $parameters If null, an empty Resourced is created. @@ -65,10 +78,15 @@ class DAVResource function __construct( $parameters = null ) { $this->_is_principal = false; $this->_is_collection = false; + $this->_is_calendar = false; + $this->_is_addressbook = false; if ( isset($parameters) && is_object($parameters) ) { $this->FromRow($parameters); } else if ( isset($parameters) && is_array($parameters) ) { + if ( isset($parameters['path']) ) { + $this->FromPath($parameters['path']); + } } } @@ -94,6 +112,117 @@ class DAVResource } + /** + * Initialise from a path + * @param object $inpath The path to populate the resource data from + */ + function FromPath($inpath) { + global $c; + + $this->path = rawurldecode($inpath); + + /** Allow a path like .../username/calendar.ics to translate into the calendar URL */ + if ( preg_match( '#^(/[^/]+/[^/]+).ics$#', $this->path, $matches ) ) { + $this->path = $matches[1]. '/'; + } + + /** strip doubled slashes */ + if ( strstr($this->path,'//') ) $this->path = preg_replace( '#//+#', '/', $this->path); + + $this->FindCollection(); + } + + + /** + * Find the collection associated with this resource. + */ + function FindCollection() { + /** + * RFC4918, 8.3: Identifiers for collections SHOULD end in '/' + * - also discussed at more length in 5.2 + * + * So we look for a collection which matches one of the following URLs: + * - The exact request. + * - If the exact request, doesn't end in '/', then the request URL with a '/' appended + * - The request URL truncated to the last '/' + * The collection URL for this request is therefore the longest row in the result, so we + * can "... ORDER BY LENGTH(dav_name) DESC LIMIT 1" + */ + $this->collection = (object) array( + 'collection_id' => -1, + 'type' => 'unknown', + 'is_calendar' => false, 'is_principal' => false, 'is_addressbook' => false, 'resourcetypes' => '', + + ); + + $sql = "SELECT * FROM collection WHERE dav_name = :raw_path "; + $params = array( ':raw_path' => $this->path); + if ( !preg_match( '#/$#', $this->path ) ) { + $sql .= ' OR dav_name = :up_to_slash OR dav_name = :plus_slash' + $params[':up_to_slash'] = preg_replace( '#[^/]*$#', '', $this->path); + $params[':plus_slash'] = $this->path."/"; + } + $sql .= "ORDER BY LENGTH(dav_name) DESC LIMIT 1"; + $qry = new AwlQuery( $sql, $params ); + if ( $qry->Exec('caldav') && $qry->rows == 1 && ($row = $qry->Fetch()) ) { + $this->collection = $row; + $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'; + } + else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->path, $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 ); + $sql = <<Exec('caldav'); + dbg_error_log( "caldav", "Created new collection as '$displayname'." ); + + $qry = new AwlQuery( "SELECT * FROM collection WHERE user_no = :user_no AND dav_name = :dav_name", $params ); + if ( $qry->Exec('caldav') && $qry->rows == 1 && ($row = $qry->Fetch()) ) { + $this->collection = $row; + $this->collection->type = $this->collection_type; + } + } + else if ( preg_match( '#^((/[^/]+/)calendar-proxy-(read|write))/?[^/]*$#', $this->path, $matches ) ) { + $this->collection_type = 'proxy'; + $this->_is_proxy_request = true; + $this->proxy_type = $matches[3]; + $this->collection_path = $matches[1].'/'; + } + else if ( $this->options['allow_by_email'] && preg_match( '#^/(\S+@\S+[.]\S+)/?$#', $this->path) ) { + /** @TODO: we should deprecate this now that Evolution 2.27 can do scheduling extensions */ + $this->collection_id = -1; + $this->collection_type = 'email'; + $this->collection_path = $this->path; + $this->_is_principal = true; + } + else if ( preg_match( '#^(/[^/]+)/?$#', $this->path, $matches) || preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->path, $matches) ) { + $this->collection_id = -1; + $this->collection_path = $matches[1].'/'; // Enforce trailling '/' + $this->collection_type = 'principal'; + $this->_is_principal = true; + if ( preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->path, $matches) ) { + // Force a depth of 0 on these, which are at the wrong URL. + $this->depth = 0; + } + } + else if ( $this->path == '/' ) { + $this->collection_id = -1; + $this->collection_path = '/'; + $this->collection_type = 'root'; + } + + } + + /** * Return general server-related properties for this URL */ @@ -107,11 +236,11 @@ class DAVResource switch( $tag ) { case 'DAV::href': $prop->NewElement('href', ConstructURL($this->dav_name) ); - break; + break; case 'DAV::getcontenttype': $prop->NewElement('getcontenttype', $this->contenttype ); - break; + break; case 'DAV::resourcetype': $prop->NewElement('resourcetype', $this->resourcetype ); @@ -139,7 +268,7 @@ class DAVResource // // 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) ) ); // break; - + // Empty tag responses. case 'DAV::alternate-URI-set': case 'DAV::getcontentlength': @@ -226,7 +355,7 @@ class DAVResource } return $elements; } - + /** * Render XML for this resource