mirror of
https://gitlab.com/davical-project/davical.git
synced 2026-08-21 17:06:22 +00:00
Working BIND and PROPFIND of bound resources.
This commit is contained in:
parent
92355af6f6
commit
ed055722e9
@ -38,6 +38,7 @@ GRANT SELECT,INSERT,UPDATE,DELETE
|
||||
ON grants
|
||||
ON dav_principal
|
||||
ON access_ticket
|
||||
ON dav_binding
|
||||
|
||||
GRANT SELECT,UPDATE
|
||||
ON relationship_type_rt_id_seq
|
||||
|
||||
@ -319,7 +319,7 @@ CREATE TABLE dav_binding (
|
||||
bind_id INT8 DEFAULT nextval('dav_id_seq') PRIMARY KEY,
|
||||
bound_source_id INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
access_ticket_id TEXT REFERENCES access_ticket(ticket_id) ON UPDATE CASCADE ON DELETE SET NULL,
|
||||
parent_id INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
parent_container TEXT NOT NULL,
|
||||
dav_name TEXT UNIQUE NOT NULL,
|
||||
dav_displayname TEXT
|
||||
);
|
||||
|
||||
@ -24,7 +24,7 @@ CREATE TABLE dav_binding (
|
||||
bind_id INT8 DEFAULT nextval('dav_id_seq') PRIMARY KEY,
|
||||
bound_source_id INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
access_ticket_id TEXT REFERENCES access_ticket(ticket_id) ON UPDATE CASCADE ON DELETE SET NULL,
|
||||
parent_id INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
parent_container TEXT NOT NULL,
|
||||
dav_name TEXT UNIQUE NOT NULL,
|
||||
dav_displayname TEXT
|
||||
);
|
||||
|
||||
@ -76,6 +76,7 @@ switch ( $request->method ) {
|
||||
case 'UNLOCK': include('caldav-LOCK.php'); break;
|
||||
case 'MKTICKET': include('caldav-MKTICKET.php'); break;
|
||||
case 'DELTICKET': include('caldav-DELTICKET.php'); break;
|
||||
case 'BIND': include('caldav-BIND.php'); break;
|
||||
|
||||
case 'TESTRRULE': include('test-RRULE-v2.php'); break;
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
*/
|
||||
|
||||
require_once('AwlQuery.php');
|
||||
require_once('DAVTicket.php');
|
||||
|
||||
|
||||
/**
|
||||
@ -20,7 +21,7 @@ require_once('AwlQuery.php');
|
||||
class DAVResource
|
||||
{
|
||||
/**
|
||||
* @var The partial URL of the resource within our namespace
|
||||
* @var The partial URL of the resource within our namespace, which this resource is being retrieved as
|
||||
*/
|
||||
protected $dav_name;
|
||||
|
||||
@ -49,15 +50,10 @@ class DAVResource
|
||||
*/
|
||||
protected $contenttype;
|
||||
|
||||
/**
|
||||
* @var The name which this resource is being retrieved as
|
||||
*/
|
||||
protected $bound_dav_name;
|
||||
|
||||
/**
|
||||
* @var The canonical name which this resource exists at
|
||||
*/
|
||||
protected $real_dav_name;
|
||||
protected $bound_from;
|
||||
|
||||
/**
|
||||
* @var An object which is the collection record for this resource, or for it's container
|
||||
@ -89,6 +85,11 @@ class DAVResource
|
||||
*/
|
||||
private $_is_calendar;
|
||||
|
||||
/**
|
||||
* @var True if this resource is a binding to another resource
|
||||
*/
|
||||
private $_is_binding;
|
||||
|
||||
/**
|
||||
* @var True if this resource is an addressbook collection
|
||||
*/
|
||||
@ -119,6 +120,11 @@ class DAVResource
|
||||
*/
|
||||
private $supported_components;
|
||||
|
||||
/**
|
||||
* @var An array of DAVTicket objects if any apply to this resource, such as via a bind.
|
||||
*/
|
||||
private $tickets;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param mixed $parameters If null, an empty Resourced is created.
|
||||
@ -127,8 +133,8 @@ class DAVResource
|
||||
*/
|
||||
function __construct( $parameters = null ) {
|
||||
$this->exists = null;
|
||||
$this->bound_dav_name = null;
|
||||
$this->real_dav_name = null;
|
||||
$this->bound_from = null;
|
||||
$this->dav_name = null;
|
||||
$this->unique_tag = null;
|
||||
$this->resource = null;
|
||||
$this->collection = null;
|
||||
@ -143,6 +149,7 @@ class DAVResource
|
||||
$this->_is_collection = false;
|
||||
$this->_is_principal = false;
|
||||
$this->_is_calendar = false;
|
||||
$this->_is_binding = false;
|
||||
$this->_is_addressbook = false;
|
||||
$this->_is_proxy = false;
|
||||
if ( isset($parameters) && is_object($parameters) ) {
|
||||
@ -169,16 +176,17 @@ class DAVResource
|
||||
if ( $row == null ) return;
|
||||
|
||||
$this->exists = true;
|
||||
$this->real_dav_name = $row->dav_name;
|
||||
$this->bound_dav_name = (isset($row->bound_dav_name)? $row->bound_dav_name : $row->dav_name);
|
||||
$this->_is_collection = preg_match( '{/$}', $this->real_dav_name );
|
||||
$this->dav_name = $row->dav_name;
|
||||
$this->bound_from = (isset($row->bound_from)? $row->bound_from : $row->dav_name);
|
||||
$this->_is_collection = preg_match( '{/$}', $this->dav_name );
|
||||
|
||||
if ( $this->_is_collection ) {
|
||||
$this->contenttype = 'httpd/unix-directory';
|
||||
$this->collection = (object) array();
|
||||
$this->resource_id = $row->collection_id;
|
||||
|
||||
$this->_is_principal = preg_match( '{^/[^/]+/$}', $this->real_dav_name );
|
||||
if ( preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->real_dav_name, $matches) ) {
|
||||
$this->_is_principal = preg_match( '{^/[^/]+/$}', $this->dav_name );
|
||||
if ( preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->dav_name, $matches) ) {
|
||||
$this->collection->dav_name = $matches[1].'/';
|
||||
$this->collection->type = 'principal_link';
|
||||
$this->_is_principal = true;
|
||||
@ -186,9 +194,10 @@ class DAVResource
|
||||
}
|
||||
else {
|
||||
$this->resource = (object) array();
|
||||
if ( isset($row->dav_id) ) $this->resource_id = $row->dav_id;
|
||||
}
|
||||
|
||||
dbg_error_log( 'DAVResource', ':FromRow: Named "%s" is%s a collection.', $this->real_dav_name, ($this->_is_collection?'':' not') );
|
||||
dbg_error_log( 'DAVResource', ':FromRow: Named "%s" is%s a collection.', $this->dav_name, ($this->_is_collection?'':' not') );
|
||||
|
||||
foreach( $row AS $k => $v ) {
|
||||
if ( $this->_is_collection )
|
||||
@ -217,9 +226,9 @@ class DAVResource
|
||||
$this->collection->type = 'calendar';
|
||||
else if ( $row->is_addressbook == 't' )
|
||||
$this->collection->type = 'addressbook';
|
||||
else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->real_dav_name, $matches ) )
|
||||
else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
|
||||
$this->collection->type = 'schedule-'. $matches[3]. 'box';
|
||||
else if ( $this->real_dav_name == '/' )
|
||||
else if ( $this->dav_name == '/' )
|
||||
$this->collection->type = 'root';
|
||||
else
|
||||
$this->collection->type = 'collection';
|
||||
@ -249,7 +258,7 @@ class DAVResource
|
||||
function FromPath($inpath) {
|
||||
global $c;
|
||||
|
||||
$this->real_dav_name = DeconstructURL($inpath);
|
||||
$this->dav_name = DeconstructURL($inpath);
|
||||
|
||||
$this->FetchCollection();
|
||||
if ( $this->_is_collection ) {
|
||||
@ -259,7 +268,7 @@ class DAVResource
|
||||
$this->FetchResource();
|
||||
}
|
||||
dbg_error_log( 'DAVResource', ':FromPath: Path "%s" is%s a collection%s.',
|
||||
$this->real_dav_name, ($this->_is_collection?' '.$this->resourcetypes:' not'), ($this->_is_principal?' and a principal':'') );
|
||||
$this->dav_name, ($this->_is_collection?' '.$this->resourcetypes:' not'), ($this->_is_principal?' and a principal':'') );
|
||||
}
|
||||
|
||||
|
||||
@ -267,7 +276,7 @@ class DAVResource
|
||||
* Find the collection associated with this resource.
|
||||
*/
|
||||
function FetchCollection() {
|
||||
global $c, $session;
|
||||
global $c, $session, $request;
|
||||
/**
|
||||
* RFC4918, 8.3: Identifiers for collections SHOULD end in '/'
|
||||
* - also discussed at more length in 5.2
|
||||
@ -279,7 +288,7 @@ class DAVResource
|
||||
* 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"
|
||||
*/
|
||||
dbg_error_log( 'DAVResource', ':FetchCollection: Looking for collection for "%s".', $this->real_dav_name );
|
||||
dbg_error_log( 'DAVResource', ':FetchCollection: Looking for collection for "%s".', $this->dav_name );
|
||||
|
||||
$this->collection = (object) array(
|
||||
'collection_id' => -1,
|
||||
@ -295,11 +304,11 @@ class DAVResource
|
||||
$base_sql .= 'LEFT JOIN time_zone ON (collection.timezone=time_zone.tz_id) ';
|
||||
$base_sql .= 'WHERE ';
|
||||
$sql = $base_sql .'collection.dav_name = :raw_path ';
|
||||
$params = array( ':raw_path' => $this->real_dav_name, ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
|
||||
if ( !preg_match( '#/$#', $this->real_dav_name ) ) {
|
||||
$params = array( ':raw_path' => $this->dav_name, ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
|
||||
if ( !preg_match( '#/$#', $this->dav_name ) ) {
|
||||
$sql .= ' OR collection.dav_name = :up_to_slash OR collection.dav_name = :plus_slash ';
|
||||
$params[':up_to_slash'] = preg_replace( '#[^/]*$#', '', $this->real_dav_name);
|
||||
$params[':plus_slash'] = $this->real_dav_name.'/';
|
||||
$params[':up_to_slash'] = preg_replace( '#[^/]*$#', '', $this->dav_name);
|
||||
$params[':plus_slash'] = $this->dav_name.'/';
|
||||
}
|
||||
$sql .= 'ORDER BY LENGTH(collection.dav_name) DESC LIMIT 1';
|
||||
$qry = new AwlQuery( $sql, $params );
|
||||
@ -310,12 +319,12 @@ class DAVResource
|
||||
$this->collection->type = 'calendar';
|
||||
else if ( $row->is_addressbook == 't' )
|
||||
$this->collection->type = 'addressbook';
|
||||
else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->real_dav_name, $matches ) )
|
||||
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->real_dav_name, $matches ) ) {
|
||||
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');
|
||||
@ -329,7 +338,7 @@ EOSQL;
|
||||
$qry->Exec('DAVResource');
|
||||
dbg_error_log( 'DAVResource', 'Created new collection as "$displayname".' );
|
||||
|
||||
$params = array( ':raw_path' => $this->real_dav_name, ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
|
||||
$params = array( ':raw_path' => $this->dav_name, ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
|
||||
$qry = new AwlQuery( $base_sql . ' dav_name = :raw_path', $params );
|
||||
if ( $qry->Exec('DAVResource') && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
|
||||
$this->collection = $row;
|
||||
@ -337,7 +346,7 @@ EOSQL;
|
||||
$this->collection->type = $this->collection_type;
|
||||
}
|
||||
}
|
||||
else if ( preg_match( '#^(/([^/]+)/calendar-proxy-(read|write))/?[^/]*$#', $this->real_dav_name, $matches ) ) {
|
||||
else if ( preg_match( '#^(/([^/]+)/calendar-proxy-(read|write))/?[^/]*$#', $this->dav_name, $matches ) ) {
|
||||
$this->collection->type = 'proxy';
|
||||
$this->_is_proxy_request = true;
|
||||
$this->proxy_type = $matches[3];
|
||||
@ -345,17 +354,17 @@ EOSQL;
|
||||
$this->collection->dav_displayname = sprintf( '%s proxy %s', $matches[2], $matches[3] );
|
||||
$this->collection->exists = true;
|
||||
}
|
||||
else if ( preg_match( '#^(/[^/]+)/?$#', $this->real_dav_name, $matches) ) {
|
||||
else if ( preg_match( '#^(/[^/]+)/?$#', $this->dav_name, $matches) ) {
|
||||
$this->collection->dav_name = $matches[1].'/';
|
||||
$this->collection->type = 'principal';
|
||||
$this->_is_principal = true;
|
||||
}
|
||||
else if ( preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->real_dav_name, $matches) ) {
|
||||
else if ( preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $this->dav_name, $matches) ) {
|
||||
$this->collection->dav_name = $matches[1].'/';
|
||||
$this->collection->type = 'principal_link';
|
||||
$this->_is_principal = true;
|
||||
}
|
||||
else if ( $this->real_dav_name == '/' ) {
|
||||
else if ( $this->dav_name == '/' ) {
|
||||
$this->collection->dav_name = '/';
|
||||
$this->collection->type = 'root';
|
||||
$this->collection->exists = true;
|
||||
@ -363,16 +372,55 @@ EOSQL;
|
||||
$this->collection->default_privileges = (1 | 16 | 32);
|
||||
}
|
||||
else {
|
||||
dbg_error_log( 'DAVResource', 'No collection for path "%s".', $this->real_dav_name );
|
||||
$this->collection->exists = false;
|
||||
$this->collection->dav_name = preg_replace('{/[^/]*$}', '/', $this->real_dav_name);
|
||||
$bind_path = $this->dav_name;
|
||||
if ( !preg_match( '{/$}', $bind_path ) ) $bind_path .= '/';
|
||||
$sql = <<<EOSQL
|
||||
SELECT collection.*, path_privs(:session_principal::int8, collection.dav_name,:scan_depth::int), p.principal_id,
|
||||
p.type_id AS principal_type_id, p.displayname AS principal_displayname, p.default_privileges AS principal_default_privileges,
|
||||
time_zone.tz_spec, dav_binding.access_ticket_id, dav_binding.parent_container AS bind_parent_container, dav_binding.dav_displayname
|
||||
FROM dav_binding
|
||||
LEFT JOIN collection ON (collection.collection_id=bound_source_id)
|
||||
LEFT JOIN principal p USING (user_no)
|
||||
LEFT JOIN time_zone ON (collection.timezone=time_zone.tz_id)
|
||||
WHERE dav_binding.dav_name = :raw_path
|
||||
EOSQL;
|
||||
$params = array( ':raw_path' => $bind_path, ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
|
||||
$qry = new AwlQuery( $sql, $params );
|
||||
if ( $qry->Exec('DAVResource',__LINE__,__FILE__) && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
|
||||
$this->collection = $row;
|
||||
$this->collection->exists = true;
|
||||
$this->_is_binding = true;
|
||||
$this->collection->parent_set = $row->parent_container;
|
||||
$this->collection->parent_container = $row->bind_parent_container;
|
||||
$this->bound_from = $row->dav_name;
|
||||
$this->collection->bound_from = $row->dav_name;
|
||||
$this->collection->dav_name = $this->dav_name;
|
||||
if ( isset($row->access_ticket_id) ) {
|
||||
if ( !isset($this->tickets) ) $this->tickets = array();
|
||||
$this->tickets[] = new DAVTicket($row->access_ticket_id);
|
||||
}
|
||||
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 {
|
||||
dbg_error_log( 'DAVResource', 'No collection for path "%s".', $this->dav_name );
|
||||
$this->collection->exists = false;
|
||||
$this->collection->dav_name = preg_replace('{/[^/]*$}', '/', $this->dav_name);
|
||||
}
|
||||
}
|
||||
|
||||
dbg_error_log( 'DAVResource', ':FetchCollection: Found collection named "%s" of type "%s".', $this->collection->dav_name, $this->collection->type );
|
||||
|
||||
$this->_is_collection = ( $this->collection->dav_name == $this->real_dav_name || $this->collection->dav_name == $this->real_dav_name.'/' );
|
||||
$this->_is_collection = ( $this->collection->dav_name == $this->dav_name || $this->collection->dav_name == $this->dav_name.'/' );
|
||||
if ( $this->_is_collection ) {
|
||||
$this->real_dav_name = $this->collection->dav_name;
|
||||
$this->dav_name = $this->collection->dav_name;
|
||||
$this->resource_id = $this->collection->collection_id;
|
||||
$this->_is_calendar = ($this->collection->type == 'calendar');
|
||||
$this->_is_addressbook = ($this->collection->type == 'addressbook');
|
||||
$this->contenttype = 'httpd/unix-directory';
|
||||
@ -401,7 +449,7 @@ EOSQL;
|
||||
*/
|
||||
function FetchPrincipal() {
|
||||
global $c, $session;
|
||||
$this->principal = new CalDAVPrincipal( array( "path" => $this->real_dav_name ) );
|
||||
$this->principal = new CalDAVPrincipal( array( "path" => $this->dav_name ) );
|
||||
if ( $this->_is_principal && $this->principal->Exists() ) {
|
||||
// $this->contenttype = 'httpd/unix-directory';
|
||||
$this->exists = true;
|
||||
@ -409,6 +457,7 @@ EOSQL;
|
||||
$this->created = $this->principal->created;
|
||||
$this->modified = $this->principal->modified;
|
||||
$this->resourcetypes = '<DAV::collection/><DAV::principal/>';
|
||||
$this->resource_id = $this->principal->principal_id;
|
||||
}
|
||||
}
|
||||
|
||||
@ -426,7 +475,7 @@ EOSQL;
|
||||
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->real_dav_name );
|
||||
$params = array( ':dav_name' => $this->dav_name );
|
||||
|
||||
$qry = new AwlQuery( $sql, $params );
|
||||
if ( $qry->Exec('DAVResource') && $qry->rows() > 0 ) {
|
||||
@ -435,6 +484,7 @@ EOQRY;
|
||||
$this->unique_tag = $this->resource->dav_etag;
|
||||
$this->created = $this->resource->created;
|
||||
$this->modified = $this->resource->modified;
|
||||
$this->resource_id = $this->resource->dav_id;
|
||||
if ( substr($this->resource->caldav_data,0,15) == 'BEGIN:VCALENDAR' ) {
|
||||
$this->contenttype = 'text/calendar';
|
||||
}
|
||||
@ -453,7 +503,7 @@ EOQRY;
|
||||
if ( isset($this->dead_properties) ) return;
|
||||
|
||||
$this->dead_properties = array();
|
||||
$qry = new AwlQuery('SELECT property_name, property_value FROM property WHERE dav_name= :dav_name', array(':dav_name' => $this->real_dav_name) );
|
||||
$qry = new AwlQuery('SELECT property_name, property_value FROM property WHERE dav_name= :dav_name', array(':dav_name' => $this->dav_name) );
|
||||
if ( $qry->Exec('DAVResource') ) {
|
||||
while ( $property = $qry->Fetch() ) {
|
||||
$this->dead_properties[$property->property_name] = $property->property_value;
|
||||
@ -468,7 +518,7 @@ EOQRY;
|
||||
function FetchPrivileges() {
|
||||
global $session, $request;
|
||||
|
||||
if ( $this->real_dav_name == '/' || $this->real_dav_name == '' ) {
|
||||
if ( $this->dav_name == '/' || $this->dav_name == '' ) {
|
||||
$this->privileges = (1 | 16 | 32); // read + read-acl + read-current-user-privilege-set
|
||||
dbg_error_log( 'DAVResource', 'Read permissions for user accessing /' );
|
||||
return;
|
||||
@ -487,12 +537,11 @@ EOQRY;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if ( ! isset($this->collection) ) $this->FetchCollection();
|
||||
$this->privileges = 0;
|
||||
if ( !isset($this->collection->path_privs) ) {
|
||||
$parent_path = preg_replace('{/[^/]*/$}', '/', $this->collection->dav_name );
|
||||
dbg_error_log( 'DAVResource', 'Checking privileges of "%s" - parent of "%s"', $parent_path, $this->collection->dav_name );
|
||||
$parent = new DAVResource( $parent_path );
|
||||
dbg_error_log( 'DAVResource', 'Checking privileges of "%s" - parent of "%s" (dav_name: %s)', $this->collection->parent_container, $this->collection->dav_name, $this->dav_name() );
|
||||
$parent = new DAVResource( $this->collection->parent_container );
|
||||
|
||||
$this->collection->path_privs = $parent->Privileges();
|
||||
$this->collection->user_no = $parent->GetProperty('user_no');
|
||||
@ -502,10 +551,19 @@ EOQRY;
|
||||
$this->privileges = $this->collection->path_privs;
|
||||
if ( is_string($this->privileges) ) $this->privileges = bindec( $this->privileges );
|
||||
|
||||
if ( isset($request->ticket) && $request->ticket->MatchesPath($this->real_dav_name) ) {
|
||||
if ( isset($request->ticket) && $request->ticket->MatchesPath($this->dav_name) ) {
|
||||
$this->privileges |= $request->ticket->privileges();
|
||||
dbg_error_log( 'DAVResource', 'Applying permissions for ticket "%s" now: %s', $request->ticket->id(), decbin($this->privileges) );
|
||||
}
|
||||
|
||||
if ( isset($this->tickets) ) {
|
||||
foreach( $this->tickets AS $k => $ticket ) {
|
||||
if ( $ticket->MatchesResource($this->resource_id) || $ticket->MatchesPath($this->dav_name) ) {
|
||||
$this->privileges |= $ticket->privileges();
|
||||
dbg_error_log( 'DAVResource', 'Applying permissions for ticket "%s" now: %s', $ticket->id(), decbin($this->privileges) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -526,7 +584,7 @@ EOQRY;
|
||||
function HavePrivilegeTo( $do_what ) {
|
||||
if ( !isset($this->privileges) ) $this->FetchPrivileges();
|
||||
$test_bits = privilege_to_bits( $do_what );
|
||||
// dbg_error_log( 'DAVResource', 'Testing privileges of "%s" (%s) against allowed "%s" => "%s" (%s)', $do_what, decbin($test_bits), decbin($this->privileges), ($this->privileges & $test_bits), decbin($this->privileges & $test_bits) );
|
||||
dbg_error_log( 'DAVResource', 'Testing privileges of "%s" (%s) against allowed "%s" => "%s" (%s)', $do_what, decbin($test_bits), decbin($this->privileges), ($this->privileges & $test_bits), decbin($this->privileges & $test_bits) );
|
||||
return ($this->privileges & $test_bits) > 0;
|
||||
}
|
||||
|
||||
@ -541,7 +599,7 @@ EOQRY;
|
||||
|
||||
if ( $this->HavePrivilegeTo($privilege) ) return;
|
||||
|
||||
$request->NeedPrivilege( $privilege, $this->real_dav_name );
|
||||
$request->NeedPrivilege( $privilege, $this->dav_name );
|
||||
exit(0); // Unecessary, but might clarify things
|
||||
}
|
||||
|
||||
@ -777,7 +835,7 @@ EOQRY;
|
||||
* Find the locks that might apply and load them into an array
|
||||
*/
|
||||
$sql = 'SELECT * FROM locks WHERE :this_path::text ~ (\'^\'||dav_name||:match_end)::text';
|
||||
$qry = new AwlQuery($sql, array( ':this_path' => $this->real_dav_name, ':match_end' => ($depth == DEPTH_INFINITY ? '' : '$') ) );
|
||||
$qry = new AwlQuery($sql, array( ':this_path' => $this->dav_name, ':match_end' => ($depth == DEPTH_INFINITY ? '' : '$') ) );
|
||||
if ( $qry->Exec('DAVResource',__LINE__,__FILE__) ) {
|
||||
while( $lock_row = $qry->Fetch() ) {
|
||||
$this->_locks_found[$lock_row->opaquelocktoken] = $lock_row;
|
||||
@ -790,7 +848,7 @@ EOQRY;
|
||||
}
|
||||
|
||||
foreach( $this->_locks_found AS $lock_token => $lock_row ) {
|
||||
if ( $lock_row->depth == DEPTH_INFINITY || $lock_row->dav_name == $this->real_dav_name ) {
|
||||
if ( $lock_row->depth == DEPTH_INFINITY || $lock_row->dav_name == $this->dav_name ) {
|
||||
return $lock_token;
|
||||
}
|
||||
}
|
||||
@ -843,6 +901,14 @@ EOQRY;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether this resource is a bind to another resource
|
||||
*/
|
||||
function IsBinding() {
|
||||
return $this->_is_binding;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether this resource actually exists, in the virtual sense, within the hierarchy
|
||||
*/
|
||||
@ -856,7 +922,7 @@ EOQRY;
|
||||
if ( !isset($this->resource) ) $this->FetchResource();
|
||||
}
|
||||
}
|
||||
// dbg_error_log('DAVResource',' Checking whether "%s" exists. It would appear %s.', $this->real_dav_name, ($this->exists ? 'so' : 'not') );
|
||||
// dbg_error_log('DAVResource',' Checking whether "%s" exists. It would appear %s.', $this->dav_name, ($this->exists ? 'so' : 'not') );
|
||||
return $this->exists;
|
||||
}
|
||||
|
||||
@ -865,11 +931,20 @@ EOQRY;
|
||||
* Returns the dav_name of the resource in our internal namespace
|
||||
*/
|
||||
function dav_name() {
|
||||
if ( isset($this->real_dav_name) ) return $this->real_dav_name;
|
||||
if ( isset($this->dav_name) ) return $this->dav_name;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the dav_name of the resource we are bound to, within our internal namespace
|
||||
*/
|
||||
function bound_from() {
|
||||
if ( isset($this->bound_from) ) return $this->bound_from;
|
||||
return $this->dav_name();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the principal-URL for this resource
|
||||
*/
|
||||
@ -926,7 +1001,7 @@ EOQRY;
|
||||
$this->parent_container_type = 'calendar';
|
||||
else if ( $parent->is_addressbook == 't' )
|
||||
$this->parent_container_type = 'addressbook';
|
||||
else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->real_dav_name, $matches ) )
|
||||
else if ( preg_match( '#^((/[^/]+/)\.(in|out)/)[^/]*$#', $this->dav_name, $matches ) )
|
||||
$this->parent_container_type = 'schedule-'. $matches[3]. 'box';
|
||||
else
|
||||
$this->parent_container_type = 'collection';
|
||||
@ -1028,7 +1103,7 @@ EOQRY;
|
||||
if ( isset($this->principal->{$name}) ) return $this->principal->{$name};
|
||||
if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
|
||||
}
|
||||
// dbg_error_log( 'DAVResource', ':GetProperty: Failed to find property "%s" on "%s".', $name, $this->real_dav_name );
|
||||
// dbg_error_log( 'DAVResource', ':GetProperty: Failed to find property "%s" on "%s".', $name, $this->dav_name );
|
||||
}
|
||||
|
||||
return $value;
|
||||
@ -1060,7 +1135,7 @@ EOQRY;
|
||||
function ResourceProperty( $tag, $prop, &$reply, &$denied ) {
|
||||
global $c, $session, $request;
|
||||
|
||||
// dbg_error_log( 'DAVResource', 'Processing "%s" on "%s".', $tag, $this->real_dav_name );
|
||||
// dbg_error_log( 'DAVResource', 'Processing "%s" on "%s".', $tag, $this->dav_name );
|
||||
|
||||
if ( $reply === null ) $reply = $GLOBALS['reply'];
|
||||
|
||||
@ -1074,7 +1149,11 @@ EOQRY;
|
||||
break;
|
||||
|
||||
case 'DAV::href':
|
||||
$prop->NewElement('href', ConstructURL($this->real_dav_name) );
|
||||
$prop->NewElement('href', ConstructURL($this->dav_name) );
|
||||
break;
|
||||
|
||||
case 'DAV::resource_id':
|
||||
$prop->NewElement('href', ConstructURL('.resources/'.$this->resource_id) );
|
||||
break;
|
||||
|
||||
case 'DAV::getcontenttype':
|
||||
@ -1267,7 +1346,7 @@ EOQRY;
|
||||
$reply->NSElement($prop, $tag, $this->dead_properties[$tag] );
|
||||
}
|
||||
else {
|
||||
// dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of path "%s".', $tag, $this->real_dav_name );
|
||||
// dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of path "%s".', $tag, $this->dav_name );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1287,7 +1366,7 @@ EOQRY;
|
||||
function GetPropStat( $properties, &$reply, $props_only = false ) {
|
||||
global $request;
|
||||
|
||||
dbg_error_log('DAVResource',':GetPropStat: propstat for href "%s"', $this->real_dav_name );
|
||||
dbg_error_log('DAVResource',':GetPropStat: propstat for href "%s"', $this->dav_name );
|
||||
|
||||
$prop = new XMLElement('prop');
|
||||
$denied = array();
|
||||
@ -1303,7 +1382,7 @@ EOQRY;
|
||||
$found = $this->principal->PrincipalProperty( $tag, $prop, $reply, $denied );
|
||||
}
|
||||
if ( ! $found ) {
|
||||
// dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of resource "%s".', $tag, $this->real_dav_name );
|
||||
// dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of resource "%s".', $tag, $this->dav_name );
|
||||
$not_found[] = $reply->Tag($tag);
|
||||
}
|
||||
}
|
||||
@ -1346,7 +1425,7 @@ EOQRY;
|
||||
function RenderAsXML( $properties, &$reply, $props_only = false ) {
|
||||
global $session, $c, $request;
|
||||
|
||||
dbg_error_log('DAVResource',':RenderAsXML: Resource "%s"', $this->real_dav_name );
|
||||
dbg_error_log('DAVResource',':RenderAsXML: Resource "%s"', $this->dav_name );
|
||||
|
||||
if ( !$this->Exists() ) return null;
|
||||
|
||||
@ -1356,7 +1435,7 @@ EOQRY;
|
||||
}
|
||||
|
||||
$elements = $this->GetPropStat( $properties, $reply );
|
||||
array_unshift( $elements, $reply->href(ConstructURL($this->real_dav_name)));
|
||||
array_unshift( $elements, $reply->href(ConstructURL($this->dav_name)));
|
||||
|
||||
$response = new XMLElement( 'response', $elements );
|
||||
|
||||
|
||||
@ -13,13 +13,15 @@ require_once('AwlQuery.php');
|
||||
|
||||
$request->NeedPrivilege('DAV::bind');
|
||||
|
||||
if ( ! $request->IsCollection() || ! $request->Exists() ) {
|
||||
if ( ! $request->IsCollection() ) {
|
||||
$request->PreconditionFailed(403,'DAV::bind-into-collection',translate('The BIND Request-URI MUST identify a collection.'));
|
||||
}
|
||||
$parent_container = $request->path;
|
||||
if ( preg_match( '{[^/]$}', $parent_container ) ) $parent_container .= '/';
|
||||
|
||||
require_once('DAVResource.php');
|
||||
$parent = new DAVResource( $parent_container );
|
||||
if ( $parent->IsSchedulingCollection() ) {
|
||||
if ( ! $parent->Exists() || $parent->IsSchedulingCollection() ) {
|
||||
$request->PreconditionFailed(403, 'DAV::method-not-allowed',translate('The BIND method is not allowed at that location.') );
|
||||
}
|
||||
|
||||
@ -32,16 +34,14 @@ $xmltree = BuildXMLTree( $request->xml_tags, $position);
|
||||
$segment = $xmltree->GetElements('DAV::segment');
|
||||
$segment = $segment[0]->GetContent();
|
||||
|
||||
if ( preg_match( '{[/\\]}', $segment ) ) {
|
||||
if ( preg_match( '{[/\\\\]}', $segment ) ) {
|
||||
$request->PreconditionFailed(403, 'DAV::name-allowed',translate('That destination name contains invalid characters.') );
|
||||
}
|
||||
|
||||
$href = $xmltree->GetElements('DAV::href')[0]->GetContent();
|
||||
// $href = $href[0]->GetContent();
|
||||
$href = $xmltree->GetElements('DAV::href');
|
||||
$href = $href[0]->GetContent();
|
||||
|
||||
$destination_path = $request->path;
|
||||
if ( preg_match( '{[^/]$}', $destination_path ) ) $destination_path .= '/';
|
||||
$destination_path .= $segment;
|
||||
$destination_path = $parent_container . $segment .'/';
|
||||
$destination = new DAVResource( $destination_path );
|
||||
if ( $destination->Exists() ) {
|
||||
$request->PreconditionFailed(403,'DAV::can-overwrite',translate('A resource already exists at the destination.'));
|
||||
@ -60,19 +60,25 @@ if ( $source->IsPrincipal() || !$source->IsCollection() ) {
|
||||
bind_id INT8 DEFAULT nextval('dav_id_seq') PRIMARY KEY,
|
||||
bound_source_id INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
access_ticket_id TEXT REFERENCES access_ticket(ticket_id) ON UPDATE CASCADE ON DELETE SET NULL,
|
||||
parent_id INT8 REFERENCES collection(collection_id) ON UPDATE CASCADE ON DELETE CASCADE,
|
||||
parent_container TEXT NOT NULL,
|
||||
dav_name TEXT UNIQUE NOT NULL,
|
||||
dav_displayname TEXT
|
||||
*/
|
||||
|
||||
$sql = 'INSERT INTO dav_binding ( bound_source_id, access_ticket_id, parent_id, dav_name, dav_displayname )
|
||||
VALUES( :target_id, :ticket_id, :container_id, :dav_name, :displayname )';
|
||||
$sql = 'INSERT INTO dav_binding ( bound_source_id, access_ticket_id, parent_container, dav_name, dav_displayname )
|
||||
VALUES( :target_id, :ticket_id, :parent_container, :dav_name, :displayname )';
|
||||
$params = array(
|
||||
':target_id' => $source->GetProperty('collection_id'),
|
||||
':ticket_id' => (isset($request->ticket) ? $request->ticket->id() : null),
|
||||
':container_id' => $destination->GetProperty('collection_id'),
|
||||
':parent_container' => $parent->dav_name(),
|
||||
':dav_name' => $destination_path,
|
||||
':displayname' => 'Bind to '.$source->GetProperty('DAV::displayname')
|
||||
':displayname' => 'Bind to '.$source->GetProperty('displayname')
|
||||
);
|
||||
$qry = new AwlQuery( $sql, $params );
|
||||
|
||||
if ( $qry->Exec('BIND',__LINE__,__FILE__) ) {
|
||||
header('Location: '. ConstructURL($destination_path) );
|
||||
$request->DoResponse(201);
|
||||
}
|
||||
else {
|
||||
$request->DoResponse(500,translate('Database Error'));
|
||||
}
|
||||
@ -96,10 +96,11 @@ function add_proxy_response( &$responses, $which, $parent_path ) {
|
||||
* If '/' is requested, a list of visible users is given, otherwise
|
||||
* a list of calendars for the user which are parented by this path.
|
||||
*/
|
||||
function get_collection_contents( $depth, $user_no, $collection ) {
|
||||
function get_collection_contents( $depth, $collection ) {
|
||||
global $c, $session, $request, $reply, $property_list;
|
||||
|
||||
dbg_error_log('PROPFIND','Getting collection contents: Depth %d, User: %d, Path: %s', $depth, $user_no, $collection->dav_name() );
|
||||
$bound_from = $collection->bound_from();
|
||||
dbg_error_log('PROPFIND','Getting collection contents: Depth %d, Path: %s, Bound from: %s', $depth, $collection->dav_name(), $bound_from );
|
||||
|
||||
$date_format = iCalendar::HttpDateFormat();
|
||||
$responses = array();
|
||||
@ -108,7 +109,7 @@ function get_collection_contents( $depth, $user_no, $collection ) {
|
||||
* Calendar/Addressbook collections may not contain collections, so we won't look
|
||||
*/
|
||||
$params = array( ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
|
||||
if ( $collection->dav_name() == '/' ) {
|
||||
if ( $bound_from == '/' ) {
|
||||
$sql = "SELECT usr.*, '/' || username || '/' AS dav_name, md5(username || updated::text) AS dav_etag, ";
|
||||
$sql .= "to_char(joined at time zone 'GMT',$date_format) AS created, ";
|
||||
$sql .= "to_char(updated at time zone 'GMT',$date_format) AS modified, ";
|
||||
@ -120,11 +121,26 @@ function get_collection_contents( $depth, $user_no, $collection ) {
|
||||
$sql .= 'ORDER BY usr.user_no';
|
||||
}
|
||||
else {
|
||||
$sql = 'SELECT principal.*, collection.*, \'collection\' AS type FROM collection LEFT JOIN principal USING (user_no) ';
|
||||
$qry = new AwlQuery('SELECT * FROM dav_binding WHERE dav_binding.parent_container = :this_dav_name ORDER BY bind_id',
|
||||
array(':this_dav_name' => $bound_from));
|
||||
if( $qry->Exec('PROPFIND',__LINE__,__FILE__) && $qry->rows() > 0 ) {
|
||||
while( $binding = $qry->Fetch() ) {
|
||||
$resource = new DAVResource($binding->dav_name);
|
||||
if ( $resource->HavePrivilegeTo('DAV::read') ) {
|
||||
$responses[] = $resource->RenderAsXML($property_list, $reply);
|
||||
if ( $depth > 0 ) {
|
||||
$responses = array_merge($responses, get_collection_contents( $depth - 1, $resource ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sql = 'SELECT principal.*, collection.*, \'collection\' AS type ';
|
||||
$sql .= 'FROM collection LEFT JOIN principal USING (user_no) ';
|
||||
$sql .= 'WHERE parent_container = :this_dav_name ';
|
||||
$sql .= "AND (path_privs(:session_principal::int8,collection.dav_name,:scan_depth::int) & 1::BIT(24))::INT4::BOOLEAN ";
|
||||
$sql .= ' ORDER BY collection_id';
|
||||
$params[':this_dav_name'] = $collection->dav_name();
|
||||
$params[':this_dav_name'] = $bound_from;
|
||||
}
|
||||
$qry = new AwlQuery($sql, $params);
|
||||
|
||||
@ -132,24 +148,27 @@ function get_collection_contents( $depth, $user_no, $collection ) {
|
||||
while( $subcollection = $qry->Fetch() ) {
|
||||
$resource = new DAVResource($subcollection);
|
||||
$responses[] = $resource->RenderAsXML($property_list, $reply);
|
||||
if ( $depth > 0 ) {
|
||||
$responses = array_merge($responses, get_collection_contents( $depth - 1, $resource ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $collection->IsPrincipal() == 't' ) {
|
||||
// Caldav Proxy: 5.1 par. 2: Add child resources calendar-proxy-(read|write)
|
||||
dbg_error_log('PROPFIND','Adding calendar-proxy-read and write. Path: %s', $collection->dav_name() );
|
||||
add_proxy_response($responses, 'read', $collection->dav_name() );
|
||||
add_proxy_response($responses, 'write', $collection->dav_name() );
|
||||
dbg_error_log('PROPFIND','Adding calendar-proxy-read and write. Path: %s', $bound_from );
|
||||
add_proxy_response($responses, 'read', $bound_from );
|
||||
add_proxy_response($responses, 'write', $bound_from );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* freebusy permission is not allowed to see the items in a collection. Must have at least read permission.
|
||||
*/
|
||||
if ( $request->HavePrivilegeTo('DAV::read') ) {
|
||||
dbg_error_log('PROPFIND','Getting collection items: Depth %d, User: %d, Path: %s', $depth, $user_no, $collection->dav_name() );
|
||||
if ( $collection->HavePrivilegeTo('DAV::read') ) {
|
||||
dbg_error_log('PROPFIND','Getting collection items: Depth %d, Path: %s', $depth, $bound_from );
|
||||
$privacy_clause = ' ';
|
||||
if ( ! $request->HavePrivilegeTo('all') ) {
|
||||
if ( ! $collection->HavePrivilegeTo('all') ) {
|
||||
$privacy_clause = " AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL) ";
|
||||
}
|
||||
|
||||
@ -167,7 +186,7 @@ function get_collection_contents( $depth, $user_no, $collection ) {
|
||||
$sql .= 'LEFT JOIN collection USING(collection_id,user_no) LEFT JOIN principal USING(user_no) ';
|
||||
$sql .= 'WHERE collection.dav_name = :collection_dav_name '.$time_limit_clause.' '.$privacy_clause;
|
||||
if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $sql .= " ORDER BY caldav_data.dav_id";
|
||||
$qry = new AwlQuery( $sql, array( ':collection_dav_name' => $collection->dav_name()) );
|
||||
$qry = new AwlQuery( $sql, array( ':collection_dav_name' => $bound_from) );
|
||||
if( $qry->Exec('PROPFIND',__LINE__,__FILE__) && $qry->rows() > 0 ) {
|
||||
while( $item = $qry->Fetch() ) {
|
||||
$resource = new DAVResource($item);
|
||||
@ -180,40 +199,6 @@ function get_collection_contents( $depth, $user_no, $collection ) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get XML response for a single collection. If Depth is >0 then
|
||||
* subsidiary collections will also be got up to $depth
|
||||
*/
|
||||
function get_collection( $depth, $user_no, $collection_path ) {
|
||||
global $session, $c, $request, $property_list, $reply;
|
||||
$responses = array();
|
||||
|
||||
dbg_error_log('PROPFIND','Getting collection: Depth %d, User: %d, Path: %s', $depth, $user_no, $collection_path );
|
||||
|
||||
if (preg_match('#/[^/]+/calendar-proxy-(read|write)/?#',$collection_path, $match) ) {
|
||||
// this should be a direct query to /<somewhere>/calendar-proxy-<something>
|
||||
dbg_error_log('PROPFIND','Simulating calendar-proxy-read or write. Path: %s', $collection_path);
|
||||
add_proxy_response($responses, $match[1], $collection_path);
|
||||
}
|
||||
|
||||
if ( $collection_path == null || $collection_path == '' ) $collection_path = '/';
|
||||
|
||||
$resource = new DAVResource($collection_path);
|
||||
if ( !$resource->Exists() ) return $responses;
|
||||
|
||||
$responses[] = $resource->RenderAsXML($property_list, $reply);
|
||||
|
||||
if ( $depth > 0 ) {
|
||||
dbg_error_log('PROPFIND','Getting collection contents of path: "%s"', $collection_path );
|
||||
$responses = array_merge($responses, get_collection_contents( $depth-1, $user_no, $resource ) );
|
||||
}
|
||||
else {
|
||||
dbg_error_log('PROPFIND','Not Getting collection contents of path: "%s"', $collection_path );
|
||||
}
|
||||
|
||||
return $responses;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Something that we can handle, at least roughly correctly.
|
||||
@ -221,23 +206,22 @@ function get_collection( $depth, $user_no, $collection_path ) {
|
||||
$responses = array();
|
||||
if ( $request->IsProxyRequest() ) {
|
||||
add_proxy_response($responses, $request->proxy_type, '/' . $request->principal->username . '/' );
|
||||
/** Nothing inside these, as yet. */
|
||||
}
|
||||
elseif ( $request->IsCollection() ) {
|
||||
$responses = get_collection( $request->depth, $request->user_no, $request->path );
|
||||
}
|
||||
elseif ( $request->HavePrivilegeTo('DAV::read') ) {
|
||||
else {
|
||||
$resource = new DAVResource($request->path);
|
||||
$response = $resource->RenderAsXML($property_list, $reply);
|
||||
if ( isset($response) ) $responses[] = $response;
|
||||
}
|
||||
|
||||
if ( count($responses) < 1 ) {
|
||||
if ( $request->HavePrivilegeTo('DAV::read') ) {
|
||||
$resource->NeedPrivilege('DAV::read');
|
||||
if ( ! $resource->Exists() ) {
|
||||
$request->DoResponse( 404, translate('That resource is not present on this server.') );
|
||||
}
|
||||
else {
|
||||
$request->DoResponse( 403, translate('You do not have appropriate rights to view that resource.') );
|
||||
if ( $resource->IsCollection() ) {
|
||||
dbg_error_log('PROPFIND','Getting collection contents: Depth %d, Path: %s', $request->depth, $resource->dav_name() );
|
||||
$responses[] = $resource->RenderAsXML($property_list, $reply);
|
||||
if ( $request->depth > 0 ) {
|
||||
$responses = array_merge($responses, get_collection_contents( $request->depth - 1, $resource ) );
|
||||
}
|
||||
}
|
||||
elseif ( $request->HavePrivilegeTo('DAV::read') ) {
|
||||
$responses[] = $resource->RenderAsXML($property_list, $reply);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user