Getting bindings to work as seamlessly as possible.

This commit is contained in:
Andrew McMillan 2010-03-15 00:31:16 +13:00
parent 45f9a0039d
commit 4d674c4f92
7 changed files with 44 additions and 22 deletions

View File

@ -319,6 +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,
dav_owner_id INT8 NOT NULL REFERENCES principal(principal_id) ON UPDATE CASCADE ON DELETE CASCADE,
parent_container TEXT NOT NULL,
dav_name TEXT UNIQUE NOT NULL,
dav_displayname TEXT

View File

@ -24,6 +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,
dav_owner_id INT8 NOT NULL REFERENCES principal(principal_id) ON UPDATE CASCADE ON DELETE CASCADE,
parent_container TEXT NOT NULL,
dav_name TEXT UNIQUE NOT NULL,
dav_displayname TEXT

View File

@ -389,10 +389,12 @@ EOSQL;
$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
time_zone.tz_spec, dav_binding.access_ticket_id, dav_binding.parent_container AS bind_parent_container,
dav_binding.dav_displayname, owner.dav_name AS bind_owner_url
FROM dav_binding
LEFT JOIN collection ON (collection.collection_id=bound_source_id)
LEFT JOIN principal p USING (user_no)
LEFT JOIN dav_principal owner ON (dav_binding.dav_owner_id=owner.principal_id)
LEFT JOIN time_zone ON (collection.timezone=time_zone.tz_id)
WHERE dav_binding.dav_name = :raw_path
EOSQL;
@ -959,6 +961,15 @@ EOQRY;
return $this->dav_name();
}
/**
* Returns the dav_name of the resource in our internal namespace
*/
function parent_path() {
if ( $this->IsCollection() ) return $this->collection->parent_container;
return preg_replace( '{[^/]+$}', '', $this->bound_from());
}
/**
* Returns the principal-URL for this resource
@ -1223,7 +1234,8 @@ 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() ) );
$owner_url = ( isset($this->_is_binding) && $this->_is_binding ? $this->collection->bind_owner_url : $this->principal_url() );
$reply->DAVElement( $prop, 'owner', $reply->href( $owner_url ) );
break;
// Empty tag responses.
@ -1433,24 +1445,24 @@ EOQRY;
*
* @param array $properties The requested properties for this principal
* @param reference $reply A reference to the XMLDocument being used for the reply
* @param boolean $props_only Default false. If true will only return the fragment with the properties, not a full response fragment.
*
* @return string An XML fragment with the requested properties for this principal
*/
function RenderAsXML( $properties, &$reply, $props_only = false ) {
function RenderAsXML( $properties, &$reply, $bound_parent_path = null ) {
global $session, $c, $request;
dbg_error_log('DAVResource',':RenderAsXML: Resource "%s" exists(%d)', $this->dav_name, $this->Exists() );
if ( !$this->Exists() ) return null;
if ( $props_only ) {
dbg_error_log('LOG WARNING','DAVResource::RenderAsXML Called misguidedly - should be call to DAVResource::GetPropStat' );
return $this->GetPropStat( $properties, $reply, true );
}
$elements = $this->GetPropStat( $properties, $reply );
array_unshift( $elements, $reply->href(ConstructURL($this->dav_name)));
if ( isset($bound_parent_path) ) {
$dav_name = str_replace( $this->parent_path(), $bound_parent_path, $this->dav_name );
}
else
$dav_name = $this->dav_name;
array_unshift( $elements, $reply->href(ConstructURL($dav_name)));
$response = new XMLElement( 'response', $elements );

View File

@ -65,12 +65,13 @@ if ( $source->IsPrincipal() || !$source->IsCollection() ) {
dav_displayname TEXT
*/
$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 )';
$sql = 'INSERT INTO dav_binding ( bound_source_id, access_ticket_id, dav_owner_id, parent_container, dav_name, dav_displayname )
VALUES( :target_id, :ticket_id, :session_principal, :parent_container, :dav_name, :displayname )';
$params = array(
':target_id' => $source->GetProperty('collection_id'),
':ticket_id' => (isset($request->ticket) ? $request->ticket->id() : null),
':parent_container' => $parent->dav_name(),
':session_principal' => $session->principal_id,
':dav_name' => $destination_path,
':displayname' => 'Bind to '.$source->GetProperty('displayname')
);

View File

@ -99,10 +99,11 @@ function add_proxy_response( $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, $collection ) {
function get_collection_contents( $depth, $collection, $parent_path = null ) {
global $c, $session, $request, $reply, $property_list;
$bound_from = $collection->bound_from();
if ( !isset($parent_path) ) $parent_path = $collection->dav_name();
dbg_error_log('PROPFIND','Getting collection contents: Depth %d, Path: %s, Bound from: %s', $depth, $collection->dav_name(), $bound_from );
$date_format = iCalendar::HttpDateFormat();
@ -132,7 +133,7 @@ function get_collection_contents( $depth, $collection ) {
if ( $resource->HavePrivilegeTo('DAV::read') ) {
$responses[] = $resource->RenderAsXML($property_list, $reply);
if ( $depth > 0 ) {
$responses = array_merge($responses, get_collection_contents( $depth - 1, $resource ) );
$responses = array_merge($responses, get_collection_contents( $depth - 1, $resource, $binding->dav_name ) );
}
}
}
@ -152,7 +153,8 @@ function get_collection_contents( $depth, $collection ) {
$resource = new DAVResource($subcollection);
$responses[] = $resource->RenderAsXML($property_list, $reply);
if ( $depth > 0 ) {
$responses = array_merge($responses, get_collection_contents( $depth - 1, $resource ) );
$responses = array_merge($responses, get_collection_contents( $depth - 1, $resource,
str_replace($resource->parent_path(), $parent_path, $resource->dav_name() ) ) );
}
}
}
@ -195,7 +197,7 @@ function get_collection_contents( $depth, $collection ) {
if( $qry->Exec('PROPFIND',__LINE__,__FILE__) && $qry->rows() > 0 ) {
while( $item = $qry->Fetch() ) {
$resource = new DAVResource($item);
$responses[] = $resource->RenderAsXML($property_list, $reply);
$responses[] = $resource->RenderAsXML($property_list, $reply, $parent_path );
}
}
}

View File

@ -56,6 +56,9 @@ switch( $proptype ) {
$properties[$propertyname] = 1;
}
$collection = new DAVResource($request->path);
$bound_from = $collection->bound_from();
/**
* Build the href list for the IN ( href, href, href, ... ) clause.
*/
@ -67,13 +70,13 @@ foreach( $mg_hrefs AS $k => $v ) {
* anything up to the matching request->path (which will include any http...) and then
* put the $request->path back on.
*/
$href = $request->path . preg_replace( "#^.*$request->path#", '', rawurldecode($v->GetContent()) );
$href = $bound_from . preg_replace( "{^.*\E$request->path\Q}", '', rawurldecode($v->GetContent()) );
dbg_error_log("REPORT", "Reporting on href '%s'", $href );
$href_in .= ($href_in == '' ? '' : ', ');
$href_in .= qpg($href);
}
$where = " WHERE caldav_data.dav_name ~ ".qpg("^".$request->path)." ";
$where = " WHERE caldav_data.dav_name ~ ".qpg("^".$bound_from)." ";
$where .= "AND caldav_data.dav_name IN ( $href_in ) ";
$where .= "AND (calendar_item.class != 'PRIVATE' OR calendar_item.class IS NULL ";
$where .= "OR (uprivs($session->user_no::int8,calendar_item.user_no,$c->permission_scan_depth::int) = privilege_to_bits('all')) ) ";
@ -86,6 +89,9 @@ if ( isset($c->strict_result_ordering) && $c->strict_result_ordering ) $where .=
$qry = new PgQuery( "SELECT caldav_data.*,calendar_item.* FROM caldav_data INNER JOIN calendar_item USING(dav_id, user_no, dav_name, collection_id) LEFT JOIN collection USING(collection_id)". $where );
if ( $qry->Exec('REPORT',__LINE__,__FILE__) && $qry->rows > 0 ) {
while( $calendar_object = $qry->Fetch() ) {
if ( $bound_from != $collection->dav_name() ) {
$calendar_object->dav_name = str_replace( $bound_from, $collection->dav_name(), $calendar_object->dav_name);
}
if ( $need_expansion ) {
$ics = new iCalComponent($calendar_object->caldav_data);
$expanded = expand_event_instances($ics, $expand_range_start, $expand_range_end);

View File

@ -23,10 +23,9 @@ if ( ! ini_get('open_basedir') && (isset($c->dbg['ALL']) || (isset($c->dbg['repo
}
}
if ( ! ($request->AllowedTo('read') || $request->AllowedTo('freebusy')) ) {
// The specification states that a lack of privileges MUST result in a 404. RFC4791, Section 7.10
$request->DoResponse( 404 );
}
$target = new DAVResource($request->path);
$target->NeedPrivilege( array('DAV::read', 'urn:ietf:params:xml:ns:caldav:read-free-busy') );
if ( !isset($request->xml_tags) ) {
$request->DoResponse( 406, translate("REPORT body contains no XML data!") );