diff --git a/inc/AwlQuery.php b/inc/AwlQuery.php
index 3bd1ef49..c1283253 100644
--- a/inc/AwlQuery.php
+++ b/inc/AwlQuery.php
@@ -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 ) {
diff --git a/inc/CalDAVPrincipal.php b/inc/CalDAVPrincipal.php
index 512a7fb4..b9eec62f 100644
--- a/inc/CalDAVPrincipal.php
+++ b/inc/CalDAVPrincipal.php
@@ -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);
}
}
diff --git a/inc/CalDAVRequest.php b/inc/CalDAVRequest.php
index ed48a4d7..6d9f6e95 100644
--- a/inc/CalDAVRequest.php
+++ b/inc/CalDAVRequest.php
@@ -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'];
diff --git a/inc/DAVResource.php b/inc/DAVResource.php
index 006342e5..9278bf57 100644
--- a/inc/DAVResource.php
+++ b/inc/DAVResource.php
@@ -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 = '';
+ }
+ 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' => '',
+ '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 = '';
- if ( $this->_is_principal )
- $this->resourcetype .= '';
- else {
- $this->exists = (!isset($this->collection->exists) || $this->collection->exists);
- }
+ $this->resourcetypes = '';
+ if ( $this->_is_principal ) $this->resourcetypes .= '';
}
}
}
@@ -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 = '';
+ $this->resourcetypes = '';
}
}
@@ -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 = <<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 );
diff --git a/inc/always.php b/inc/always.php
index 381cd171..d46f76e3 100644
--- a/inc/always.php
+++ b/inc/always.php
@@ -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)) );
+}
diff --git a/inc/always.php.in b/inc/always.php.in
index 21f6bcc2..4bc717e7 100644
--- a/inc/always.php.in
+++ b/inc/always.php.in
@@ -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)) );
+}
diff --git a/inc/caldav-MOVE.php b/inc/caldav-MOVE.php
index 5b3242c3..342a0277 100644
--- a/inc/caldav-MOVE.php
+++ b/inc/caldav-MOVE.php
@@ -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';
diff --git a/inc/caldav-PUT-functions.php b/inc/caldav-PUT-functions.php
index 56c0b199..7b2d4b3e 100644
--- a/inc/caldav-PUT-functions.php
+++ b/inc/caldav-PUT-functions.php
@@ -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.
diff --git a/inc/caldav-REPORT-principal.php b/inc/caldav-REPORT-principal.php
index e89d83f1..f5810d90 100644
--- a/inc/caldav-REPORT-principal.php
+++ b/inc/caldav-REPORT-principal.php
@@ -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);
diff --git a/inc/caldav-REPORT-sync-collection.php b/inc/caldav-REPORT-sync-collection.php
index d32126fa..739437fe 100644
--- a/inc/caldav-REPORT-sync-collection.php
+++ b/inc/caldav-REPORT-sync-collection.php
@@ -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;
diff --git a/inc/caldav-REPORT.php b/inc/caldav-REPORT.php
index 40f04851..00dc66b8 100644
--- a/inc/caldav-REPORT.php
+++ b/inc/caldav-REPORT.php
@@ -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');
}
}
}