Various changes preparing to switch PROPFIND implementation.

This commit is contained in:
Andrew McMillan 2009-11-12 00:39:18 +13:00
parent c83873c32a
commit 865a2e499c
11 changed files with 514 additions and 313 deletions

View File

@ -417,7 +417,7 @@ class AwlQuery
if ( ! $success ) {
// query failed
$this->errorstring = sprintf( 'SQL error "%s" - %s"', $this->error_info[0], $this->error_info[2]);
$this->errorstring = sprintf( 'SQL error "%s" - %s"', $this->error_info[0], (isset($this->error_info[2]) ? $this->error_info[2] : ''));
$this->_log_query( $this->location, 'QF', $this->errorstring, $line, $file );
}
elseif ( $this->execution_time > $this->query_time_warning ) {

View File

@ -60,7 +60,12 @@ class CalDAVPrincipal
/**
* @var RFC3744: The principals that are direct members of this group.
*/
var $group_member_set;
protected $_is_group;
/**
* @var RFC3744: The principals that are direct members of this group.
*/
protected $group_member_set;
/**
* @var RFC3744: The groups in which the principal is directly a member.
@ -160,11 +165,13 @@ class CalDAVPrincipal
foreach( $usr AS $k => $v ) {
$this->{$k} = $v;
}
if ( !isset($this->modified) ) $this->modified = ISODateToHTTPDate($this->updated);
if ( !isset($this->created) ) $this->created = ISODateToHTTPDate($this->joined);
if ( !isset($this->modified) ) $this->modified = $this->updated;
if ( !isset($this->created) ) $this->created = $this->joined;
$this->dav_etag = md5($this->username . $this->updated);
$this->_is_group = (isset($usr->type_id) && $usr->type_id == 3);
$this->by_email = false;
$this->principal_url = ConstructURL( '/'.$this->username.'/', true );
$this->url = $this->principal_url;
@ -188,15 +195,24 @@ class CalDAVPrincipal
$this->xmpp_server = $c->notifications_server['host'];
}
$this->group_member_set = array();
$qry = new PgQuery('SELECT * FROM relationship LEFT JOIN usr ON (from_user = usr.user_no) LEFT JOIN role_member ON (to_user = role_member.user_no) LEFT JOIN roles USING (role_no) WHERE to_user = ? AND role_name = '."'Group'", $this->user_no );
if ( $qry->Exec('CalDAVPrincipal') && $qry->rows > 0 ) {
while( $membership = $qry->Fetch() ) {
$this->group_member_set[] = ConstructURL( '/'. $membership->username . '/', true);
if ( $this->_is_group ) {
$this->group_member_set = array();
$qry = new PgQuery('SELECT * FROM group_member JOIN principal ON (principal_id=member_id) JOIN usr USING(user_no) WHERE group_id = ?', $this->principal_id );
if ( $qry->Exec('CalDAVPrincipal') && $qry->rows > 0 ) {
while( $member = $qry->Fetch() ) {
$this->group_member_set[] = ConstructURL( '/'. $member->username . '/', true);
}
}
}
$this->group_membership = array();
$qry = new PgQuery('SELECT * FROM group_member JOIN principal ON (principal_id=group_id) JOIN usr USING(user_no) WHERE member_id = ?', $this->principal_id );
if ( $qry->Exec('CalDAVPrincipal') && $qry->rows > 0 ) {
while( $group = $qry->Fetch() ) {
$this->group_membership[] = ConstructURL( '/'. $group->username . '/', true);
}
}
$this->group_membership = null;
$this->read_proxy_group = null;
$this->write_proxy_group = null;
$this->write_proxy_for = null;
@ -206,7 +222,7 @@ class CalDAVPrincipal
* calendar-free-busy-set has been dropped from draft 5 of the scheduling extensions for CalDAV
* but we'll keep replying to it for a while longer since iCal appears to want it...
*/
$qry = new PgQuery('SELECT dav_name FROM collection WHERE user_no = ? AND is_calendar', $this->user_no);
$qry = new PgQuery('SELECT dav_name FROM collection WHERE user_no = ? AND is_calendar ORDER BY user_no, collection_id', $this->user_no);
$this->calendar_free_busy_set = array();
if( $qry->Exec('CalDAVPrincipal',__LINE__,__FILE__) && $qry->rows > 0 ) {
while( $calendar = $qry->Fetch() ) {
@ -224,14 +240,6 @@ class CalDAVPrincipal
function FetchProxyGroups() {
global $c;
$this->group_membership = array();
$qry = new PgQuery('SELECT * FROM relationship LEFT JOIN usr ON (to_user = user_no) LEFT JOIN role_member USING (user_no) LEFT JOIN roles USING (role_no) WHERE from_user = ? AND role_name = '."'Group'", $this->user_no );
if ( $qry->Exec('CalDAVPrincipal') && $qry->rows > 0 ) {
while( $membership = $qry->Fetch() ) {
$this->group_membership[] = ConstructURL( '/'. $membership->username . '/', true);
}
}
$this->read_proxy_group = array();
$this->write_proxy_group = array();
$this->write_proxy_for = array();
@ -252,14 +260,12 @@ class CalDAVPrincipal
if ($relationship->confers == 'R') {
if ($relationship->from_user_no == $this->user_no) {
// spec says without trailing slash, CalServ does it with slash, and so do we.
$this->group_membership[] = ConstructURL( '/'. $relationship->to_username . '/calendar-proxy-read/', true);
$this->read_proxy_for[] = ConstructURL( '/'. $relationship->to_username . '/', true);
} else /* ($relationship->to_user_no == $this->user_no) */ {
$this->read_proxy_group[] = ConstructURL( '/'. $relationship->from_username . '/', true);
}
} else if (preg_match('/[WA]/', $relationship->confers)) {
if ($relationship->from_user_no == $this->user_no) {
$this->group_membership[] = ConstructURL( '/'. $relationship->to_username . '/calendar-proxy-write/', true);
$this->write_proxy_for[] = ConstructURL( '/'. $relationship->to_username . '/', true);
}
else /* ($relationship->to_user_no == $this->user_no) */ {
@ -291,29 +297,30 @@ class CalDAVPrincipal
/**
* Accessor for the read proxy for
* Accessor for read or write proxy
* @param string read/write - which sort of proxy list is requested.
*/
function ReadProxyFor() {
function ProxyFor( $type ) {
if ( !isset($this->read_proxy_for) ) $this->FetchProxyGroups();
if ( $type == 'write' ) return $this->write_proxy_for;
return $this->read_proxy_for;
}
/**
* Accessor for the write proxy for
* Accessor for the group membership - the groups this principal is a member of
*/
function WriteProxyFor() {
if ( !isset($this->write_proxy_for) ) $this->FetchProxyGroups();
return $this->write_proxy_for;
function GroupMembership() {
return $this->group_membership;
}
/**
* Accessor for the group membership
* Accessor for the group member set - the members of this group
*/
function GroupMembership() {
if ( !isset($this->group_membership) ) $this->FetchProxyGroups();
return $this->group_membership;
function GroupMemberSet() {
if ( ! $this->_is_group ) return null;
return $this->group_member_set;
}
@ -345,7 +352,7 @@ class CalDAVPrincipal
$username = $user->username;
}
}
elseif( $user = getUserByName( $username, 'principal') ) {
elseif( $user = getUserByName( $username) ) {
$user_no = $user->user_no;
}
return $username;
@ -376,6 +383,15 @@ class CalDAVPrincipal
}
/**
* Is this a group principal?
* @return boolean Whether this is a group principal
*/
function IsGroup() {
return $this->_is_group;
}
/**
* Return the privileges bits for the current session user to this resource
*/
@ -391,6 +407,7 @@ class CalDAVPrincipal
$collection = (object) array(
'collection_id' => (isset($this->collection_id) ? $this->collection_id : 0),
'is_calendar' => 'f',
'is_addressbook' => 'f',
'is_principal' => 't',
'user_no' => (isset($this->user_no) ? $this->user_no : 0),
'username' => (isset($this->username) ? $this->username : 0),
@ -405,6 +422,117 @@ class CalDAVPrincipal
return $collection;
}
/**
* Returns properties which are specific to this principal
*/
function PrincipalProperty( $tag, $prop, &$reply, &$denied ) {
dbg_error_log('principal',': RenderAsXML: Principal Property "%s"', $tag );
switch( $tag ) {
case 'DAV::getcontenttype':
$prop->NewElement('getcontenttype', 'httpd/unix-directory' );
break;
case 'DAV::resourcetype':
$prop->NewElement('resourcetype', array( new XMLElement('principal'), new XMLElement('collection')) );
break;
case 'DAV::displayname':
$prop->NewElement('displayname', $this->fullname );
break;
case 'DAV::principal-URL':
$prop->NewElement('principal-URL', $reply->href($this->principal_url) );
break;
case 'DAV::getlastmodified':
$prop->NewElement('getlastmodified', ISODateToHTTPDate($this->modified) );
break;
case 'DAV::creationdate':
$prop->NewElement('creationdate', DateToISODate($this->created) );
break;
case 'DAV::getcontentlanguage':
/** Use the principal's locale by preference, otherwise system default */
$locale = (isset($c->current_locale) ? $c->current_locale : '');
if ( isset($this->locale) && $this->locale != '' ) $locale = $this->locale;
$prop->NewElement('getcontentlanguage', $locale );
break;
case 'DAV::group-member-set':
if ( ! $this->_is_group ) return false;
$prop->NewElement('group-member-set', $reply->href($this->group_member_set) );
break;
case 'DAV::group-membership':
$prop->NewElement('group-membership', $reply->href($this->GroupMembership()) );
break;
case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL':
$reply->CalDAVElement($prop, 'schedule-inbox-URL', $reply->href($this->schedule_inbox_url) );
break;
case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL':
$reply->CalDAVElement($prop, 'schedule-outbox-URL', $reply->href($this->schedule_outbox_url) );
break;
case 'http://calendarserver.org/ns/:dropbox-home-URL':
$reply->CalendarserverElement($prop, 'dropbox-home-URL', $reply->href($this->dropbox_url) );
break;
case 'http://calendarserver.org/ns/:notifications-URL':
$reply->CalendarserverElement($prop, 'notifications-URL', $reply->href($this->notifications_url) );
break;
case 'http://calendarserver.org/ns/:xmpp-server':
if ( ! isset( $this->xmpp_uri ) ) return false;
$reply->CalendarserverElement($prop, 'xmpp-server', $this->xmpp_server );
break;
case 'http://calendarserver.org/ns/:xmpp-uri':
if ( ! isset( $this->xmpp_uri ) ) return false;
$reply->CalendarserverElement($prop, 'xmpp-uri', $this->xmpp_uri );
break;
case 'urn:ietf:params:xml:ns:caldav:calendar-home-set':
$reply->CalDAVElement($prop, 'calendar-home-set', $reply->href( $this->calendar_home_set ) );
break;
case 'urn:ietf:params:xml:ns:caldav:calendar-free-busy-set':
$reply->CalDAVElement( $prop, 'calendar-free-busy-set', $reply->href( $this->calendar_free_busy_set ) );
break;
case 'urn:ietf:params:xml:ns:caldav:calendar-user-address-set':
$reply->CalDAVElement($prop, 'calendar-user-address-set', $reply->href($this->user_address_set) );
break;
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 ) );
break;
case 'DAV::principal-collection-set':
$reply->DAVElement( $prop, 'principal-collection-set', $reply->href( ConstructURL('/') ) );
break;
// Empty tag responses.
case 'DAV::alternate-URI-set':
$prop->NewElement( $reply->Tag($tag));
break;
case 'SOME-DENIED-PROPERTY': /** @todo indicating the style for future expansion */
$denied[] = $reply->Tag($tag);
break;
default:
return false;
break;
}
return true;
}
/**
* Render XML for a single Principal (user) from the DB
@ -416,7 +544,7 @@ class CalDAVPrincipal
* @return string An XML fragment with the requested properties for this principal
*/
function RenderAsXML( $properties, &$reply, $props_only = false ) {
global $session, $c, $request;
global $request;
dbg_error_log('principal',': RenderAsXML: Principal "%s"', $this->username );
@ -424,117 +552,9 @@ class CalDAVPrincipal
$denied = array();
$not_found = array();
foreach( $properties AS $k => $tag ) {
dbg_error_log('principal',': RenderAsXML: Principal Property "%s"', $tag );
switch( $tag ) {
case 'DAV::getcontenttype':
$prop->NewElement('getcontenttype', 'httpd/unix-directory' );
break;
case 'DAV::resourcetype':
$prop->NewElement('resourcetype', array( new XMLElement('principal'), new XMLElement('collection')) );
break;
case 'DAV::displayname':
$prop->NewElement('displayname', $this->fullname );
break;
case 'DAV::principal-URL':
$prop->NewElement('principal-URL', $reply->href($this->principal_url) );
break;
case 'DAV::getlastmodified':
$prop->NewElement('getlastmodified', $this->modified );
break;
case 'DAV::creationdate':
$prop->NewElement('creationdate', $this->created );
break;
case 'DAV::getcontentlanguage':
/** Use the principal's locale by preference, otherwise system default */
$locale = (isset($c->current_locale) ? $c->current_locale : '');
if ( isset($this->locale) && $this->locale != '' ) $locale = $this->locale;
$prop->NewElement('getcontentlanguage', $locale );
break;
case 'DAV::group-member-set':
$prop->NewElement('group-member-set', $reply->href($this->group_member_set) );
break;
case 'DAV::group-membership':
$prop->NewElement('group-membership', $reply->href($this->GroupMembership()) );
break;
case 'urn:ietf:params:xml:ns:caldav:schedule-inbox-URL':
$reply->CalDAVElement($prop, 'schedule-inbox-URL', $reply->href($this->schedule_inbox_url) );
break;
case 'urn:ietf:params:xml:ns:caldav:schedule-outbox-URL':
$reply->CalDAVElement($prop, 'schedule-outbox-URL', $reply->href($this->schedule_outbox_url) );
break;
case 'http://calendarserver.org/ns/:dropbox-home-URL':
$reply->CalendarserverElement($prop, 'dropbox-home-URL', $reply->href($this->dropbox_url) );
break;
case 'http://calendarserver.org/ns/:notifications-URL':
$reply->CalendarserverElement($prop, 'notifications-URL', $reply->href($this->notifications_url) );
break;
case 'http://calendarserver.org/ns/:xmpp-server':
if ( isset ( $this->xmpp_uri ) )
$reply->CalendarserverElement($prop, 'xmpp-server', $this->xmpp_server );
else
$not_found[] = $reply->Tag($tag);
break;
case 'http://calendarserver.org/ns/:xmpp-uri':
if ( isset ( $this->xmpp_uri ) )
$reply->CalendarserverElement($prop, 'xmpp-uri', $this->xmpp_uri );
else
$not_found[] = $reply->Tag($tag);
break;
case 'urn:ietf:params:xml:ns:caldav:calendar-home-set':
$reply->CalDAVElement($prop, 'calendar-home-set', $reply->href( $this->calendar_home_set ) );
break;
case 'urn:ietf:params:xml:ns:caldav:calendar-user-address-set':
$reply->CalDAVElement($prop, 'calendar-user-address-set', $reply->href($this->user_address_set) );
break;
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 ) );
break;
case 'DAV::principal-collection-set':
$reply->DAVElement( $prop, 'principal-collection-set', $reply->href( ConstructURL('/') ) );
break;
// Empty tag responses.
case 'DAV::alternate-URI-set':
case 'DAV::getcontentlength':
$prop->NewElement( $reply->Tag($tag));
break;
case 'SOME-DENIED-PROPERTY': /** @todo indicating the style for future expansion */
$denied[] = $reply->Tag($tag);
break;
case 'http://calendarserver.org/ns/:getctag':
case 'DAV::getetag':
case 'urn:ietf:params:xml:ns:caldav:supported-calendar-component-set':
// These will 404 on a Principal, since they don't apply
$not_found[] = $reply->Tag($tag);
break;
default:
if ( ! $request->ServerProperty( $tag, $prop, $reply ) ) {
dbg_error_log( 'principal', 'Request for unsupported property "%s" of principal "%s".', $tag, $this->username );
$not_found[] = $reply->Tag($tag);
}
break;
if ( ! $this->PrincipalProperty( $tag, $prop, $reply, $denied ) && ! $request->ServerProperty( $tag, $prop, $reply ) ) {
dbg_error_log( 'principal', 'Request for unsupported property "%s" of principal "%s".', $tag, $this->username );
$not_found[] = $reply->Tag($tag);
}
}

View File

@ -940,7 +940,7 @@ EOSQL;
/**
* Return general server-related properties for this URL
*/
function ServerProperty( $tag, $prop, $reply = null ) {
function ServerProperty( $tag, $prop, &$reply = null ) {
global $c, $session;
if ( $reply === null ) $reply = $GLOBALS['reply'];

View File

@ -63,37 +63,43 @@ function privilege_to_bits( $raw_privs ) {
function bits_to_privilege( $raw_bits ) {
$out_priv = array();
if ( is_string($raw_bits) ) {
$raw_bits = bindec($raw_bits);
}
if ( ($raw_bits & 16777215) == 16777215 ) $out_priv[] = 'all';
if ( (in_bits & 1) != 0 ) $out_priv[] = 'DAV::read';
if ( (in_bits & 8) != 0 ) $out_priv[] = 'DAV::unlock';
if ( (in_bits & 16) != 0 ) $out_priv[] = 'DAV::read-acl';
if ( (in_bits & 32) != 0 ) $out_priv[] = 'DAV::read-current-user-privilege-set';
if ( (in_bits & 256) != 0 ) $out_priv[] = 'DAV::write-acl';
if ( (in_bits & 512) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:read-free-busy';
if ( ($raw_bits & 1) != 0 ) $out_priv[] = 'DAV::read';
if ( ($raw_bits & 8) != 0 ) $out_priv[] = 'DAV::unlock';
if ( ($raw_bits & 16) != 0 ) $out_priv[] = 'DAV::read-acl';
if ( ($raw_bits & 32) != 0 ) $out_priv[] = 'DAV::read-current-user-privilege-set';
if ( ($raw_bits & 256) != 0 ) $out_priv[] = 'DAV::write-acl';
if ( ($raw_bits & 512) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:read-free-busy';
if ( (in_bits & 198) != 0 ) {
if ( (in_bits & 198) == 198 ) $out_priv[] = 'DAV::write';
if ( (in_bits & 2) != 0 ) $out_priv[] = 'DAV::write-properties';
if ( (in_bits & 4) != 0 ) $out_priv[] = 'DAV::write-content';
if ( (in_bits & 64) != 0 ) $out_priv[] = 'DAV::bind';
if ( (in_bits & 128) != 0 ) $out_priv[] = 'DAV::unbind';
if ( ($raw_bits & 198) != 0 ) {
if ( ($raw_bits & 198) == 198 ) $out_priv[] = 'DAV::write';
if ( ($raw_bits & 2) != 0 ) $out_priv[] = 'DAV::write-properties';
if ( ($raw_bits & 4) != 0 ) $out_priv[] = 'DAV::write-content';
if ( ($raw_bits & 64) != 0 ) $out_priv[] = 'DAV::bind';
if ( ($raw_bits & 128) != 0 ) $out_priv[] = 'DAV::unbind';
}
if ( (in_bits & 7168) != 0 ) {
if ( (in_bits & 7168) == 7168 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver';
if ( (in_bits & 1024) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver-invite';
if ( (in_bits & 2048) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver-reply';
if ( (in_bits & 4096) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-query-freebusy';
if ( ($raw_bits & 7168) != 0 ) {
if ( ($raw_bits & 7168) == 7168 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver';
if ( ($raw_bits & 1024) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver-invite';
if ( ($raw_bits & 2048) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-deliver-reply';
if ( ($raw_bits & 4096) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-query-freebusy';
}
if ( (in_bits & 57344) != 0 ) {
if ( (in_bits & 57344) == 57344 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send';
if ( (in_bits & 8192) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-invite';
if ( (in_bits & 16384) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-reply';
if ( (in_bits & 32768) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-freebusy';
if ( ($raw_bits & 57344) != 0 ) {
if ( ($raw_bits & 57344) == 57344 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send';
if ( ($raw_bits & 8192) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-invite';
if ( ($raw_bits & 16384) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-reply';
if ( ($raw_bits & 32768) != 0 ) $out_priv[] = 'urn:ietf:params:xml:ns:caldav:schedule-send-freebusy';
}
// dbg_error_log( 'DAVResource', ' Privilege bit "%s" is "%s".', $raw_bits, implode(', ', $out_priv) );
return $out_priv;
}
@ -126,9 +132,9 @@ class DAVResource
protected $resource;
/**
* @var The type of the resource, possibly multiple
* @var The types of the resource, possibly multiple
*/
protected $resourcetype;
protected $resourcetypes;
/**
* @var The type of the content
@ -170,6 +176,11 @@ class DAVResource
*/
private $_is_addressbook;
/**
* @var True if this resource is, or is in, a proxy collection
*/
private $_is_proxy_request;
/**
* @var An array of the methods we support on this resource.
*/
@ -180,6 +191,11 @@ class DAVResource
*/
private $supported_reports;
/**
* @var An array of the dead properties held for this resource
*/
private $dead_properties;
/**
* @var An array of the component types we support on this resource.
*/
@ -198,14 +214,16 @@ class DAVResource
$this->resource = null;
$this->collection = null;
$this->principal = null;
$this->resourcetype = null;
$this->resourcetypes = null;
$this->contenttype = null;
$this->privileges = null;
$this->dead_properties = null;
$this->_is_collection = false;
$this->_is_principal = false;
$this->_is_calendar = false;
$this->_is_collection = false;
$this->_is_principal = false;
$this->_is_calendar = false;
$this->_is_addressbook = false;
$this->_is_proxy = false;
if ( isset($parameters) && is_object($parameters) ) {
$this->FromRow($parameters);
}
@ -230,19 +248,73 @@ class DAVResource
if ( $row == null ) return;
$this->exists = true;
$this->dav_name = $row->dav_name;
$this->_is_collection = preg_match( '{/$}', $row->dav_name );
if ( $this->_is_collection ) {
$this->contenttype = 'httpd/unix-directory';
$this->collection = (object) array();
$this->_is_principal = preg_match( '{^/[^/]+/$}', $row->dav_name );
if ( preg_match( '#^(/principals/[^/]+/[^/]+)/?$#', $row->dav_name, $matches) ) {
$this->collection->dav_name = $matches[1].'/';
$this->collection->type = 'principal_link';
$this->_is_principal = true;
}
}
else {
$this->resource = (object) array();
}
dbg_error_log( 'DAVResource', ':FromRow: Named "%s" is%s a collection.', $row->dav_name, ($this->_is_collection?'':' not') );
foreach( $row AS $k => $v ) {
dbg_error_log( 'DAVResource', 'Processing resource property "%s" has "%s".', $row->dav_name, $k );
$this->resource->{$k} = $v;
if ( $this->_is_collection )
$this->collection->{$k} = $v;
else
$this->resource->{$k} = $v;
switch ( $k ) {
case 'created':
case 'modified':
case 'resourcetypes':
$this->{$k} = $v;
break;
case 'dav_etag':
$this->unique_tag = '"'.$v.'"';
break;
case 'is_calendar': if ( $this->_is_collection) $this->_is_calendar = ($v == 't'); break;
case 'is_addressbook': if ( $this->_is_collection) $this->_is_addressbook = ($v == 't'); break;
case 'is_principal': if ( $this->_is_collection) $this->_is_principal = ($v == 't'); break;
}
}
if ( $this->_is_collection ) {
if ( !isset( $this->collection->type ) || $this->collection->type == 'collection' ) {
if ( $this->_is_principal )
$this->collection->type = 'principal';
else 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 if ( $this->dav_name == '/' )
$this->collection->type = 'root';
else
$this->collection->type = 'collection';
}
$this->_is_calendar = ($this->collection->is_calendar == 't');
$this->_is_addressbook = ($this->collection->is_addressbook == 't');
if ( $this->_is_principal && !isset($this->resourcetypes) ) {
$this->resourcetypes = '<DAV::collection/><DAV::principal/>';
}
if ( isset($this->collection->dav_displayname) ) $this->collection->displayname = $this->collection->dav_displayname;
}
else {
$this->resourcetypes = '';
if ( isset($this->resource->caldav_data) ) {
if ( substr($this->resource->caldav_data,0,15) == 'BEGIN:VCALENDAR' ) $this->contenttype = 'text/calendar';
$this->resource->displayname = $this->resource->summary;
}
}
}
@ -276,6 +348,16 @@ class DAVResource
if ( substr($ourpath,0,1) != '/' ) $ourpath = '/'.$ourpath;
$this->dav_name = $ourpath;
$this->FetchCollection();
if ( $this->_is_collection ) {
if ( $this->_is_principal ) $this->FetchPrincipal();
}
else {
$this->FetchResource();
}
dbg_error_log( 'DAVResource', ':FromPath: Path "%s" is%s a collection%s.',
$this->dav_name, ($this->_is_collection?' '.$this->resourcetypes:' not'), ($this->_is_principal?' and a principal':'') );
}
@ -295,10 +377,12 @@ 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->dav_name );
$this->collection = (object) array(
'collection_id' => -1,
'type' => 'nonexistent',
'is_calendar' => false, 'is_principal' => false, 'is_addressbook' => false, 'resourcetypes' => '<DAV::collection/>',
'is_calendar' => false, 'is_principal' => false, 'is_addressbook' => false
);
$base_sql = 'SELECT collection.*, path_privileges(:session_principal, collection.dav_name), ';
@ -316,6 +400,7 @@ class DAVResource
$qry = new AwlQuery( $sql, $params );
if ( $qry->Exec('DAVResource') && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
$this->collection = $row;
$this->collection->exists = true;
if ( $row->is_calendar == 't' )
$this->collection->type = 'calendar';
else if ( $row->is_addressbook == 't' )
@ -343,13 +428,16 @@ EOSQL;
$qry = new AwlQuery( $base_sql . ' dav_name = :raw_path', $params );
if ( $qry->Exec('DAVResource') && $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
$this->collection = $row;
$this->collection->exists = true;
}
}
else if ( preg_match( '#^((/[^/]+/)calendar-proxy-(read|write))/?[^/]*$#', $this->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];
$this->collection->dav_name = $matches[1].'/';
$this->collection->dav_displayname = sprintf( '%s proxy %s', $matches[2], $matches[3] );
$this->collection->exists = true;
}
else if ( preg_match( '#^(/[^/]+)/?$#', $this->dav_name, $matches) ) {
$this->collection->dav_name = $matches[1].'/';
@ -364,6 +452,8 @@ EOSQL;
else if ( $this->dav_name == '/' ) {
$this->collection->dav_name = '/';
$this->collection->type = 'root';
$this->collection->exists = true;
$this->collection->displayname = $c->system_name;
}
else {
dbg_error_log( 'DAVResource', 'No collection for path "%s".', $this->dav_name );
@ -371,24 +461,29 @@ EOSQL;
$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->dav_name || $this->collection->dav_name == $this->dav_name.'/' );
if ( $this->_is_collection ) {
$this->dav_name = $this->collection->dav_name;
$this->_is_calendar = ($this->collection->type == 'calendar');
$this->_is_addressbook = ($this->collection->type == 'addressbook');
$this->contenttype = 'httpd/unix-directory';
if ( isset($this->collection->dav_etag) ) $this->unique_tag = $this->collection->dav_etag;
if ( isset($this->collection->created) ) $this->created = $this->collection->created;
if ( isset($this->collection->modified) ) $this->modified = $this->collection->modified;
if ( isset($this->collection->resourcetype) )
$this->resourcetype = $this->collection->resourcetype;
if ( !isset($this->exists) && isset($this->collection->exists) ) {
// If this seems peculiar it's because we only set it to false above...
$this->exists = $this->collection->exists;
}
if ( $this->exists ) {
if ( isset($this->collection->dav_etag) ) $this->unique_tag = '"'.$this->collection->dav_etag.'"';
if ( isset($this->collection->created) ) $this->created = $this->collection->created;
if ( isset($this->collection->modified) ) $this->modified = $this->collection->modified;
if ( isset($this->collection->dav_displayname) ) $this->collection->displayname = $this->collection->dav_displayname;
}
if ( isset($this->collection->resourcetypes) )
$this->resourcetypes = $this->collection->resourcetypes;
else {
$this->resourcetype = '<DAV::collection/>';
if ( $this->_is_principal )
$this->resourcetype .= '<DAV::principal/>';
else {
$this->exists = (!isset($this->collection->exists) || $this->collection->exists);
}
$this->resourcetypes = '<DAV::collection/>';
if ( $this->_is_principal ) $this->resourcetypes .= '<DAV::principal/>';
}
}
}
@ -400,12 +495,13 @@ EOSQL;
function FetchPrincipal() {
global $c, $session;
$this->principal = new CalDAVPrincipal( array( "path" => $this->dav_name ) );
if ( $this->IsPrincipal() ) {
$this->contenttype = 'httpd/unix-directory';
if ( $this->_is_principal && $this->principal->Exists() ) {
// $this->contenttype = 'httpd/unix-directory';
$this->exists = true;
$this->unique_tag = $this->principal->dav_etag;
$this->created = $this->principal->created;
$this->modified = $this->principal->modified;
$this->resourcetype = '<DAV::principal/>';
$this->resourcetypes = '<DAV::collection/><DAV::principal/>';
}
}
@ -417,7 +513,6 @@ EOSQL;
global $c, $session;
if ( isset($this->exists) ) return; // True or false, we've got what we can already
if ( !isset($this->collection) ) $this->FetchCollection();
if ( $this->_is_collection ) return; // We have all we're going to read
$sql = <<<EOQRY
@ -433,8 +528,10 @@ EOQRY;
$this->unique_tag = $this->resource->dav_etag;
$this->created = $this->resource->created;
$this->modified = $this->resource->modified;
$this->contenttype = 'text/calendar';
$this->resourcetype = '';
if ( substr($this->resource->caldav_data,0,15) == 'BEGIN:VCALENDAR' ) {
$this->contenttype = 'text/calendar';
}
$this->resourcetypes = '';
}
else {
$this->exists = false;
@ -442,6 +539,22 @@ EOQRY;
}
/**
* Fetch any dead properties for this URL
*/
function FetchDeadProperties() {
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->dav_name) );
if ( $qry->Exec('DAVResource') ) {
while ( $property = $qry->Fetch() ) {
$this->dead_properties[$property->property_name] = $property->property_value;
}
}
}
/**
* Build permissions for this URL
*/
@ -450,32 +563,32 @@ EOQRY;
if ( $this->dav_name == '/' || $this->dav_name == '' ) {
$this->privileges = 1; // read
// dbg_error_log( 'DAVResource', 'Read permissions for user accessing /' );
dbg_error_log( 'DAVResource', 'Read permissions for user accessing /' );
return;
}
if ( $session->AllowedTo('Admin') ) {
$this->privileges = privilege_to_bits('all');
// dbg_error_log( 'DAVResource', 'Full permissions for an administrator.' );
dbg_error_log( 'DAVResource', 'Full permissions for an administrator.' );
return;
}
if ( $this->IsPrincipal() ) {
if ( !isset($this->principal) ) $this->FetchPrincipal();
$this->privileges = $this->principal->Privileges();
// dbg_error_log( 'DAVResource', 'Privileges of "%s" for user accessing principal "%s"', $this->privileges, $this->principal->username );
dbg_error_log( 'DAVResource', 'Privileges of "%s" for user accessing principal "%s"', $this->privileges, $this->principal->username );
return;
}
$this->privileges = 0;
if ( !isset($this->collection) ) $this->FetchCollection();
if ( !isset($this->collection->path_privileges) ) {
$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 );
dbg_error_log( 'DAVResource', 'Checking privileges of "%s" - parent of "%s"', $parent_path, $this->collection->dav_name );
$parent = new DAVResource( $parent_path );
$this->collection->path_privileges = $parent->Privileges();
$this->collection->user_no = $parent->GetProperty('user_no');
}
$this->privileges = $this->collection->path_privileges;
@ -505,7 +618,7 @@ EOQRY;
/**
* Returns the array of privilege names converted into XMLElements
*/
function BuildPrivileges( $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);
@ -530,7 +643,6 @@ EOQRY;
*/
function FetchSupportedMethods( ) {
if ( isset($this->supported_methods) ) return $this->supported_methods;
if ( !isset($this->collection) ) $this->FetchCollection();
$this->supported_methods = array(
'OPTIONS' => '',
@ -643,7 +755,7 @@ EOQRY;
* Checks whether this resource is a principal
*/
function IsPrincipal() {
return $this->_is_collection;
return $this->_is_collection && $this->_is_principal;
}
@ -651,7 +763,19 @@ EOQRY;
* Checks whether this resource is a calendar
*/
function IsCalendar() {
return $this->_is_calendar;
return $this->_is_collection && $this->_is_calendar;
}
/**
* Checks whether this resource is a calendar
* @param string $type The type of scheduling collection, 'read', 'write' or 'any'
*/
function IsSchedulingCollection( $type = 'any' ) {
if ( $this->_is_collection && preg_match( '{schedule-(inbox|outbox)}', $this->collection->type, $matches ) ) {
return ($type == 'any' || $type == $matches[1]);
}
return false;
}
@ -659,7 +783,7 @@ EOQRY;
* Checks whether this resource is an addressbook
*/
function IsAddressbook() {
return $this->_is_addressbook;
return $this->_is_collection && $this->_is_addressbook;
}
@ -672,10 +796,7 @@ EOQRY;
if ( !isset($this->principal) ) $this->FetchPrincipal();
$this->exists = $this->principal->Exists();
}
else if ( $this->IsCollection() ) {
if ( !isset($this->collection) ) $this->FetchCollection();
}
else {
else if ( ! $this->IsCollection() ) {
if ( !isset($this->resource) ) $this->FetchResource();
}
}
@ -710,11 +831,8 @@ EOQRY;
*/
function unique_tag() {
if ( isset($this->unique_tag) ) return $this->unique_tag;
if ( $this->IsCollection() && !isset($this->collection) ) {
$this->FetchCollection();
if ( $this->IsPrincipal() && !isset($this->principal) ) $this->FetchPrincipal();
}
else if ( !isset($this->resource) ) $this->FetchResource();
if ( $this->IsPrincipal() && !isset($this->principal) ) $this->FetchPrincipal();
else if ( !$this->_is_collection && !isset($this->resource) ) $this->FetchResource();
if ( $this->exists !== true || !isset($this->unique_tag) ) $this->unique_tag = '';
@ -726,7 +844,6 @@ EOQRY;
* Checks whether the target collection is publicly_readable
*/
function IsPublic() {
if ( !isset($this->collection) ) $this->FetchCollection();
return ( isset($this->collection->publicly_readable) && $this->collection->publicly_readable == 't' );
}
@ -735,7 +852,6 @@ EOQRY;
* 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;
@ -772,15 +888,21 @@ EOQRY;
function GetProperty( $name ) {
global $c, $session;
// dbg_error_log( 'DAVResource', 'Processing "%s".', $name );
// dbg_error_log( 'DAVResource', ':GetProperty: Fetching "%s".', $name );
$value = null;
switch( $name ) {
case 'collection_id':
if ( !isset($this->collection) ) $this->FetchCollection();
return $this->collection->collection_id;
break;
case 'resourcetype':
if ( isset($this->resourcetypes) ) {
$this->resourcetypes = preg_replace('{^<(.*)/>$}', '$1', $this->resourcetypes);
$type_list = explode('/><', $this->resourcetypes);
return $type_list;
}
default:
if ( $this->_is_principal ) {
if ( !isset($this->principal) ) $this->FetchPrincipal();
@ -788,7 +910,6 @@ EOQRY;
if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
}
else if ( $this->_is_collection ) {
if ( !isset($this->collection) ) $this->FetchCollection();
if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
if ( isset($this->principal->{$name}) ) return $this->principal->{$name};
}
@ -797,55 +918,89 @@ EOQRY;
if ( isset($this->resource->{$name}) ) return $this->resource->{$name};
if ( !isset($this->principal) ) $this->FetchPrincipal();
if ( isset($this->principal->{$name}) ) return $this->principal->{$name};
if ( !isset($this->collection) ) $this->FetchCollection();
if ( isset($this->collection->{$name}) ) return $this->collection->{$name};
}
dbg_error_log( 'ERROR', 'Request for property "%s" which is not understood.', $name );
dbg_error_log( 'DAVResource', ':GetProperty: Failed to find property "%s" on "%s".', $name, $this->dav_name );
}
return $value;
}
/**
* Return an array which is an expansion of the DAV::allprop
*/
function DAV_AllProperties() {
if ( isset($this->dead_properties) ) $this->FetchDeadProperties();
$allprop = array_merge( (isset($this->dead_properties)?$this->dead_properties:array()), array(
'DAV::getcontenttype', 'DAV::resourcetype', 'DAV::getcontentlength', 'DAV::displayname', 'DAV::getlastmodified',
'DAV::creationdate', 'DAV::getetag', 'DAV::getcontentlanguage', 'DAV::supportedlock', 'DAV::lockdiscovery',
'DAV::owner', 'DAV::principal-URL', 'DAV::current-user-principal'
) );
return $allprop;
}
/**
* Return general server-related properties for this URL
*/
function ResourceProperty( $tag, $prop, $reply = null, &$denied ) {
function ResourceProperty( $tag, $prop, &$reply, &$denied ) {
global $c, $session;
// dbg_error_log( 'DAVResource', 'Processing "%s" on "%s".', $tag, $this->dav_name );
if ( $reply === null ) $reply = $GLOBALS['reply'];
dbg_error_log( 'DAVResource', 'Processing "%s" on "%s".', $tag, $this->dav_name );
switch( $tag ) {
case 'DAV::allprop':
$property_list = $this->DAV_AllProperties();
$discarded = array();
foreach( $property_list AS $k => $v ) {
$this->ResourceProperty($v, $prop, $reply, $discarded);
}
break;
case 'DAV::href':
$prop->NewElement('href', ConstructURL($this->dav_name) );
break;
case 'DAV::getcontenttype':
if ( isset($this->contenttype) ) $prop->NewElement('getcontenttype', $this->contenttype );
if ( !isset($this->contenttype) && !$this->_is_collection && !isset($this->resource) ) $this->FetchResource();
$prop->NewElement('getcontenttype', $this->contenttype );
break;
case 'DAV::resourcetype':
$prop->NewElement('resourcetype', $this->resourcetype );
break;
case 'DAV::displayname':
if ( isset($this->displayname) ) $prop->NewElement('displayname', $this->displayname );
$resourcetypes = $prop->NewElement('resourcetype' );
$type_list = $this->GetProperty('resourcetype');
if ( !is_array($type_list) ) return true;
dbg_error_log( 'DAVResource', ':ResourceProperty: "%s" are "%s".', $tag, implode(', ',$type_list) );
foreach( $type_list AS $k => $v ) {
if ( $v == '' ) continue;
$reply->NSElement( $resourcetypes, $v );
}
break;
case 'DAV::getlastmodified':
$prop->NewElement('getlastmodified', $this->modified );
/** peculiarly, it seems that getlastmodified is HTTP Date format! */
$reply->NSElement($prop, $tag, ISODateToHTTPDate($this->GetProperty('modified')) );
break;
case 'DAV::creationdate':
$prop->NewElement('creationdate', $this->created );
/** bizarrely, it seems that creationdate is ISO8601 format */
$reply->NSElement($prop, $tag, DateToISODate($this->GetProperty('created')) );
break;
case 'DAV::getcontentlength':
if ( $this->_is_collection ) return false;
if ( !isset($this->resource) ) $this->FetchResource();
$reply->NSElement($prop, $tag, strlen($this->resource->caldav_data) );
break;
case 'DAV::getcontentlanguage':
$locale = (isset($c->current_locale) ? $c->current_locale : '');
if ( isset($this->locale) && $this->locale != '' ) $locale = $this->locale;
$prop->NewElement('getcontentlanguage', $locale );
$reply->NSElement($prop, $tag, $locale );
break;
case 'DAV::owner':
@ -855,46 +1010,77 @@ EOQRY;
// Empty tag responses.
case 'DAV::alternate-URI-set':
case 'DAV::getcontentlength':
$prop->NewElement( $reply->Tag($tag));
$reply->NSElement($prop, $tag );
break;
case 'DAV::getetag':
if ( $this->_is_collection ) {
return false;
}
else {
$prop->NewElement('getetag', $this->unique_tag );
if ( $this->_is_collection ) return false;
$reply->NSElement($prop, $tag, $this->unique_tag() );
break;
case 'http://calendarserver.org/ns/:getctag':
if ( ! $this->_is_collection ) return false;
$reply->NSElement($prop, $tag, $this->unique_tag() );
break;
case 'http://calendarserver.org/ns/:calendar-proxy-read-for':
$proxy_type = 'read';
case 'http://calendarserver.org/ns/:calendar-proxy-write-for':
if ( ! $this->_is_proxy_request ) return true;
if ( !isset($proxy_type) ) $proxy_type = 'write';
$reply->CalendarserverElement($prop, 'calendar-proxy-'.$proxy_type.'-for', $reply->href( $this->principal->ProxyFor($proxy_type) ) );
break;
case 'DAV::current-user-privilege-set':
$reply->NSElement($prop, $tag, $this->BuildPrivileges() );
break;
case 'urn:ietf:params:xml:ns:caldav:supported-calendar-data':
if ( ! $this->IsCalendar() && ! $this->IsSchedulingCollection() ) return false;
$reply->NSElement($prop, $tag, 'text/calendar' );
break;
case 'urn:ietf:params:xml:ns:caldav:supported-calendar-component-set':
if ( ! $this->_is_collection ) return false;
if ( $this->IsCalendar() )
$set_of_components = array( 'VEVENT', 'VTODO', 'VJOURNAL', 'VTIMEZONE', 'VFREEBUSY' );
else if ( $this->IsSchedulingCollection() )
$set_of_components = array( 'VEVENT', 'VTODO', 'VFREEBUSY' );
else return false;
$components = array();
foreach( $set_of_components AS $v ) {
$components[] = $reply->NewXMLElement( 'comp', '', array('name' => $v), 'urn:ietf:params:xml:ns:caldav');
}
$reply->CalDAVElement($prop, 'supported-calendar-component-set', $components );
break;
case 'SOME-DENIED-PROPERTY': /** @todo indicating the style for future expansion */
$denied[] = $reply->Tag($tag);
break;
case 'http://calendarserver.org/ns/:getctag':
if ( $this->_is_collection ) {
$prop->NewElement('http://calendarserver.org/ns/:getctag', $this->unique_tag );
}
else {
return false;
}
break;
case 'urn:ietf:params:xml:ns:caldav:calendar-data':
if ( $this->_is_collection ) {
if ( !isset($this->resource) ) $this->FetchResource();
$reply->CalDAVElement($prop, $k, $this->resource->caldav_data );
}
else {
return false;
}
if ( $this->_is_collection ) return false;
if ( !isset($this->resource) ) $this->FetchResource();
$reply->NSElement($prop, $tag, $this->resource->caldav_data );
break;
default:
dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of path "%s".', $tag, $this->dav_name );
return false;
$property_value = $this->GetProperty(preg_replace('{^.*:}', '', $tag));
if ( isset($property_value) ) {
$reply->NSElement($prop, $tag, $property_value );
}
else {
if ( !isset($this->dead_properties) ) $this->FetchDeadProperties();
if ( isset($this->dead_properties[$tag]) ) {
$reply->NSElement($prop, $tag, $this->dead_properties[$tag] );
}
else {
dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of path "%s".', $tag, $this->dav_name );
return false;
}
}
}
return true;
}
@ -902,25 +1088,35 @@ EOQRY;
/**
* Construct XML propstat fragment for this resource
*
* @param array $properties The requested properties for this resource
* @param array of string $properties The requested properties for this resource
*
* @return string An XML fragment with the requested properties for this resource
*/
function GetPropStat( $properties ) {
global $session, $c, $request, $reply;
function GetPropStat( $properties, &$reply, $props_only = false ) {
global $request;
dbg_error_log('DAVResource',': GetPropStat: href "%s"', $this->dav_name );
dbg_error_log('DAVResource',':GetPropStat: propstat for href "%s"', $this->dav_name );
$prop = new XMLElement('prop');
$denied = array();
$not_found = array();
foreach( $properties AS $k => $tag ) {
// dbg_error_log( 'DAVResource', 'Looking at resource "%s" for property [%s]"%s".', $this->dav_name, $k, $tag );
if ( ! $this->ResourceProperty($tag, $prop, $reply, $denied ) ) {
if ( is_object($tag) ) {
dbg_error_log( 'DAVResource', ':GetPropStat: "$properties" should be an array of text. Assuming this object is an XMLElement!.' );
$tag = $tag->GetTag();
}
$found = $this->ResourceProperty($tag, $prop, $reply, $denied );
if ( !$found ) {
if ( !isset($this->principal) ) $this->FetchPrincipal();
$found = $this->principal->PrincipalProperty( $tag, $prop, $reply, $denied );
}
if ( ! $found && ! $request->ServerProperty($tag, $prop, $reply) ) {
dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of resource "%s".', $tag, $this->dav_name );
$not_found[] = $reply->Tag($tag);
}
}
if ( $props_only ) return $prop;
$status = new XMLElement('status', 'HTTP/1.1 200 OK' );
$elements = array( new XMLElement( 'propstat', array($prop,$status) ) );
@ -958,44 +1154,17 @@ EOQRY;
function RenderAsXML( $properties, &$reply, $props_only = false ) {
global $session, $c, $request;
dbg_error_log('DAVResource',': RenderAsXML: Principal "%s"', $this->username );
dbg_error_log('DAVResource',':RenderAsXML: Resource "%s"', $this->dav_name );
$prop = new XMLElement('prop');
$denied = array();
$not_found = array();
foreach( $properties AS $k => $tag ) {
if ( ! $this->ResourceProperty($tag, $prop, $reply) ) {
dbg_error_log( 'DAVResource', 'Request for unsupported property "%s" of principal "%s".', $tag, $this->username );
$not_found[] = $reply->Tag($tag);
}
if ( !$this->Exists() ) return null;
if ( $props_only ) {
dbg_error_log('LOG WARNING','DAVResource::RenderAsXML Called misguidedly - should be to DAVResource::GetPropStat' );
return $this->GetPropStat( $properties, $reply, true );
}
if ( $props_only ) return $prop;
$status = new XMLElement('status', 'HTTP/1.1 200 OK' );
$propstat = new XMLElement( 'propstat', array( $prop, $status) );
$href = $reply->href( ConstructURL($this->dav_name) ); /** @TODO: make ::href() into an accessor */
$elements = array($href,$propstat);
if ( count($denied) > 0 ) {
$status = new XMLElement('status', 'HTTP/1.1 403 Forbidden' );
$noprop = new XMLElement('prop');
foreach( $denied AS $k => $v ) {
$noprop->NewElement( $v );
}
$elements[] = new XMLElement( 'propstat', array( $noprop, $status) );
}
if ( count($not_found) > 0 ) {
$status = new XMLElement('status', 'HTTP/1.1 404 Not Found' );
$noprop = new XMLElement('prop');
foreach( $not_found AS $k => $v ) {
$noprop->NewElement( $v );
}
$elements[] = new XMLElement( 'propstat', array( $noprop, $status) );
}
$elements = $this->GetPropStat( $properties, $reply );
array_unshift( $elements, $reply->href(ConstructURL($this->dav_name)));
$response = new XMLElement( 'response', $elements );

View File

@ -298,3 +298,12 @@ function ISODateToHTTPDate( $isodate ) {
// Use strtotime since strptime is not available on Windows platform.
return( gmstrftime('%a, %d %b %Y %T GMT', strtotime($isodate)) );
}
/**
* Convert a date into ISO format into the sparkly new ISO format.
* @param string $indate The date to convert
*/
function DateToISODate( $indate ) {
// Use strtotime since strptime is not available on Windows platform.
return( date('c', strtotime($indate)) );
}

View File

@ -298,3 +298,12 @@ function ISODateToHTTPDate( $isodate ) {
// Use strtotime since strptime is not available on Windows platform.
return( gmstrftime('%a, %d %b %Y %T GMT', strtotime($isodate)) );
}
/**
* Convert a date into ISO format into the sparkly new ISO format.
* @param string $indate The date to convert
*/
function DateToISODate( $indate ) {
// Use strtotime since strptime is not available on Windows platform.
return( date('c', strtotime($indate)) );
}

View File

@ -120,7 +120,7 @@ if ( $src->IsCollection() ) {
$sql = 'UPDATE collection SET dav_name = :dst_name ';
$params = array(':dst_name' => $dst_name);
if ( $src_user_no != $dst_user_no ) {
$sql .= ', user_no = :dst_user_no';
$sql .= ', user_no = :dst_user_no ';
$params[':dst_user_no'] = $dst_user_no;
}
$sql .= 'WHERE collection_id = :src_collection';

View File

@ -15,7 +15,7 @@
* return true if it's a whole calendar
*/
include_once('iCalendar.php');
require_once('iCalendar.php');
/**
* A regex which will match most reasonable timezones acceptable to PostgreSQL.

View File

@ -39,7 +39,7 @@ foreach( $searches AS $k => $search ) {
}
}
if ( $where != "" ) $where = "WHERE $where";
$sql = "SELECT * FROM usr $where";
$sql = "SELECT * FROM usr JOIN principal USING(user_no) $where";
$qry = new PgQuery($sql);

View File

@ -103,7 +103,7 @@ if ( $qry->Exec("REPORT",__LINE__,__FILE__) ) {
);
if ( $object->sync_status != 404 ) {
$dav_resource = new DAVResource($object);
$resultset = array_merge( $resultset, $dav_resource->GetPropStat($proplist) );
$resultset = array_merge( $resultset, $dav_resource->GetPropStat($proplist,$reply) );
}
$responses[] = new XMLElement( 'sync-response', $resultset );
$first_status = $object->sync_status;

View File

@ -60,13 +60,6 @@ switch( $xmltree->GetTag() ) {
exit; // Not that it should return anyway.
}
// Must have read privilege for all other reports
if ( ! ($request->AllowedTo('read') ) ) {
// If they got this far they *do* have freebusy access, so can know the
// calendar really exists. Informing them is therefore OK.
$request->DoResponse( 404, translate("You may not access that calendar") );
}
/**
* Return XML for a single calendar (or todo) entry from the DB
@ -88,7 +81,7 @@ function calendar_to_xml( $properties, $item ) {
if ( !$request->AllowedTo('all') && $session->user_no != $item->user_no ){
// the user is not admin / owner of this calendarlooking at his calendar and can not admin the other cal
/** @todo We should examine the ORGANIZER and ATTENDEE fields in the event. If this person is there then they should see this */
if ( $item->class == 'CONFIDENTIAL' ) {
if ( $item->class == 'CONFIDENTIAL' || !$request->AllowedTo('read') ) {
$ical = new iCalComponent( $caldav_data );
$resources = $ical->GetComponents('VTIMEZONE',false);
$first = $resources[0];
@ -106,6 +99,7 @@ function calendar_to_xml( $properties, $item ) {
$ical->SetComponents(array($confidential),$confidential->GetType());
$caldav_data = $ical->Render();
$displayname = translate('Busy');
}
}
}