diff --git a/inc/caldav-ACL.php b/inc/caldav-ACL.php
index 2628e799..75855085 100644
--- a/inc/caldav-ACL.php
+++ b/inc/caldav-ACL.php
@@ -88,9 +88,132 @@ $resource = new DAVResource( $request->path );
would allow unauthenticated access to resources.
*/
-$ace = $xmltree->GetPath("/DAV::acl/DAV::ace/*");
+function precondition_failed($precondition, $explanation = '') {
+ global $request;
+ $xmldoc = sprintf('
+
+ <%s/>%s
+', $precondition, $explanation );
-foreach( $ace AS $k => $v ) {
+ $request->DoResponse( 403, $xmldoc, 'text/xml; charset="utf-8"' );
+ exit(0); // Unecessary, but might clarify things
}
+function malformed_request( $text = 'Bad request' ) {
+ global $request;
+ $request->DoResponse( 400, $text );
+ exit(0); // Unecessary, but might clarify things
+}
+
+
+$position = 0;
+$xmltree = BuildXMLTree( $request->xml_tags, $position);
+$aces = $xmltree->GetPath("/DAV::acl/*");
+
+$grantor = new DAVResource($request->path);
+if ( ! $grantor->Exists() ) $request->DoResponse( 404 );
+$by_principal = null;
+$by_collection = null;
+if ( $grantor->IsPrincipal() ) $by_principal = $grantor->GetProperty('principal_id');
+else if ( $grantor->IsCollection() ) $by_collection = $grantor->GetProperty('collection_id');
+else precondition_failed('not-supported-privilege','ACLs may only be applied to Principals or Collections');
+
+$qry = new AwlQuery('BEGIN');
+$qry->Exec('ACL',__FILE__,__LINE__);
+
+foreach( $aces AS $k => $ace ) {
+ $elements = $ace->GetContent();
+ $principal = $elements[0];
+ $grant = $elements[1];
+ if ( $principal->GetTag() != 'DAV::principal' ) malformed_request('ACL request must contain a principal, not '.$principal->GetTag());
+ $grant_tag = $grant->GetTag();
+ if ( $grant_tag == 'DAV::deny' ) precondition_failed('grant-only');
+ if ( $grant_tag == 'DAV::invert' ) precondition_failed('no-invert');
+ if ( $grant->GetTag() != 'DAV::grant' ) malformed_request('ACL request must contain a principal for each ACE');
+
+ $privilege_names = array();
+ $xml_privs = $grant->GetPath("/DAV::grant/DAV::privilege/*");
+ foreach( $xml_privs AS $k => $priv ) {
+ $privilege_names[] = $priv->GetTag();
+ }
+ $privileges = privilege_to_bits($privilege_names);
+
+ $principal_content = $principal->GetContent();
+ if ( count($principal_content) != 1 ) malformed_request('ACL request must contain exactly one principal per ACE');
+ $principal_content = $principal_content[0];
+ switch( $principal_content->GetTag() ) {
+ case 'DAV::property':
+ $principal_property = $principal_content->GetContent();
+ if ( $principal_property[0]->GetTag() != 'DAV::owner' ) precondition_failed( 'recognized-principal' );
+ if ( privilege_to_bits('all') != $privileges ) {
+ precondition_failed( 'no-protected-ace-conflict', 'Owner must always have all permissions' );
+ }
+ continue; // and then we ignore it, since it's protected
+ break;
+
+ case 'DAV::unauthenticated':
+ precondition_failed( 'allowed-principal', 'May not set privileges for unauthenticated users' );
+ break;
+
+ case 'DAV::href':
+ $principal_type = 'href';
+ $principal = new DAVResource( DeconstructURL($principal_content->GetContent()) );
+ if ( ! $principal->Exists() || !$principal->IsPrincipal() )
+ precondition_failed('recognized-principal', 'Principal "' + $principal_content->GetContent() + '" not found.');
+ $sqlparms = array( ':to_principal' => $principal->GetProperty('principal_id') );
+ $where = 'WHERE to_principal=:to_principal AND ';
+ if ( isset($by_principal) ) {
+ $sqlparms[':by_principal'] = $by_principal;
+ $where .= 'by_principal = :by_principal';
+ }
+ else {
+ $sqlparms[':by_collection'] = $by_collection;
+ $where .= 'by_collection = :by_collection';
+ }
+ $qry = new AwlQuery('SELECT privileges FROM grants '.$where, $sqlparms);
+ if ( $qry->Exec('ACL',__FILE__,__LINE__) && $qry->rows() == 1 && $current = $qry->Fetch() ) {
+ $sql = 'UPDATE grants SET privileges=:privileges::INT::BIT(24) '.$where;
+ }
+ else {
+ $sqlparms[':by_principal'] = $by_principal;
+ $sqlparms[':by_collection'] = $by_collection;
+ $sql = 'INSERT INTO grants (by_principal, by_collection, to_principal, privileges) VALUES(:by_principal, :by_collection, :to_principal, :privileges::INT::BIT(24))';
+ }
+ $sqlparms[':privileges'] = $privileges;
+ $qry = new AwlQuery($sql, $sqlparms);
+ $qry->Exec('ACL',__FILE__,__LINE__);
+ break;
+
+ case 'DAV::authenticated':
+ $principal_type = 'authenticated';
+ if ( bindec($grantor->GetProperty('default_privileges')) == $privileges ) continue; // There is no change, so skip it
+ $sqlparms = array( ':privileges' => $privileges );
+ if ( isset($by_collection) ) {
+ $sql = 'UPDATE collection SET default_privileges=:privileges::INT::BIT(24) WHERE collection_id=:by_collection';
+ $sqlparms[':by_collection'] = $by_collection;
+ }
+ else {
+ $sql = 'UPDATE principal SET default_privileges=:privileges::INT::BIT(24) WHERE principal_id=:by_principal';
+ $sqlparms[':by_principal'] = $by_principal;
+ }
+ $qry = new AwlQuery($sql, $sqlparms);
+ $qry->Exec('ACL',__FILE__,__LINE__);
+ break;
+
+ case 'DAV::all':
+// $principal_type = 'all';
+ precondition_failed( 'allowed-principal', 'May not set privileges for unauthenticated users' );
+ break;
+
+ default:
+ precondition_failed( 'recognized-principal' );
+ break;
+ }
+
+}
+
+$qry = new AwlQuery('COMMIT');
+$qry->Exec('ACL',__FILE__,__LINE__);
+
+
$request->DoResponse( 200 );
diff --git a/testing/tests/regression-suite/945-ACL.result b/testing/tests/regression-suite/945-ACL.result
new file mode 100644
index 00000000..fe451115
--- /dev/null
+++ b/testing/tests/regression-suite/945-ACL.result
@@ -0,0 +1,25 @@
+HTTP/1.1 200 OK
+Date: Dow, 01 Jan 2000 00:00:00 GMT
+DAV: 1, 2, access-control, calendar-access, calendar-schedule, extended-mkcol, calendar-proxy
+Content-Length: 0
+Content-Type: text/plain; charset="utf-8"
+
+
+ by_collection: >NULL<
+ by_principal: >3<
+ displayname: >User 4<
+ privileges: >000000001111111011111111<
+ to_principal: >6<
+
+ by_collection: >NULL<
+ by_principal: >3<
+ displayname: >Assistant 1<
+ privileges: >000000000001001011000111<
+ to_principal: >10<
+
+ by_collection: >NULL<
+ by_principal: >3<
+ displayname: >Team for Client1<
+ privileges: >000000000001001000000001<
+ to_principal: >14<
+
diff --git a/testing/tests/regression-suite/945-ACL.test b/testing/tests/regression-suite/945-ACL.test
new file mode 100644
index 00000000..9315d59a
--- /dev/null
+++ b/testing/tests/regression-suite/945-ACL.test
@@ -0,0 +1,56 @@
+#
+# ACL setting default privileges on a collection to nothing, and
+# specific privileges to include read-acl.
+#
+TYPE=ACL
+URL=http://regression.host/caldav.php/user1/
+HEADER=User-Agent: RFC3744 Spec Tests
+HEADER=Content-Type: text/xml; charset="UTF-8"
+HEAD
+
+
+BEGINDATA
+
+
+
+
+
+
+
+
+
+
+
+
+ /caldav.php/user4/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ENDDATA
+
+QUERY
+SELECT by_principal, by_collection, privileges, p_to.displayname, to_principal
+ FROM grants JOIN dav_principal p_to ON (to_principal=principal_id)
+ LEFT JOIN collection ON (by_collection=collection.collection_id)
+ LEFT JOIN dav_principal p_by ON (by_principal=p_by.principal_id)
+ WHERE p_by.username = 'user1'
+ENDQUERY
+
diff --git a/testing/tests/regression-suite/946-ACL.result b/testing/tests/regression-suite/946-ACL.result
new file mode 100644
index 00000000..682bcc7a
--- /dev/null
+++ b/testing/tests/regression-suite/946-ACL.result
@@ -0,0 +1,13 @@
+HTTP/1.1 200 OK
+Date: Dow, 01 Jan 2000 00:00:00 GMT
+DAV: 1, 2, access-control, calendar-access, calendar-schedule, extended-mkcol, calendar-proxy
+Content-Length: 0
+Content-Type: text/plain; charset="utf-8"
+
+
+ by_collection: >10<
+ by_principal: >NULL<
+ displayname: >User 4<
+ privileges: >000000000000001000110001<
+ to_principal: >6<
+
diff --git a/testing/tests/regression-suite/946-ACL.test b/testing/tests/regression-suite/946-ACL.test
new file mode 100644
index 00000000..147dc9e4
--- /dev/null
+++ b/testing/tests/regression-suite/946-ACL.test
@@ -0,0 +1,41 @@
+#
+# ACL setting default privileges on a collection to nothing, and
+# specific privileges to include read-acl.
+#
+TYPE=ACL
+URL=http://regression.host/caldav.php/user1/home/
+HEADER=User-Agent: RFC3744 Spec Tests
+HEADER=Content-Type: text/xml; charset="UTF-8"
+HEAD
+
+
+BEGINDATA
+
+
+
+
+ /caldav.php/user4/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ENDDATA
+
+QUERY
+SELECT by_principal, by_collection, privileges, p_to.displayname, to_principal
+ FROM grants JOIN dav_principal p_to ON (to_principal=principal_id)
+ LEFT JOIN collection ON (by_collection=collection.collection_id)
+ WHERE collection.dav_name = '/user1/home/'
+ENDQUERY
+
diff --git a/testing/tests/regression-suite/947-ACL.result b/testing/tests/regression-suite/947-ACL.result
new file mode 100644
index 00000000..a4e9be3c
--- /dev/null
+++ b/testing/tests/regression-suite/947-ACL.result
@@ -0,0 +1,25 @@
+HTTP/1.1 200 OK
+Date: Dow, 01 Jan 2000 00:00:00 GMT
+DAV: 1, 2, access-control, calendar-access, calendar-schedule, extended-mkcol, calendar-proxy
+Content-Length: 0
+Content-Type: text/plain; charset="utf-8"
+
+
+ by_collection: >NULL<
+ by_principal: >3<
+ displayname: >User 4<
+ privileges: >000000000001001000100001<
+ to_principal: >6<
+
+ by_collection: >NULL<
+ by_principal: >3<
+ displayname: >Assistant 1<
+ privileges: >000000000001001011000111<
+ to_principal: >10<
+
+ by_collection: >NULL<
+ by_principal: >3<
+ displayname: >Team for Client1<
+ privileges: >000000000001001000000001<
+ to_principal: >14<
+
diff --git a/testing/tests/regression-suite/947-ACL.test b/testing/tests/regression-suite/947-ACL.test
new file mode 100644
index 00000000..b37933d0
--- /dev/null
+++ b/testing/tests/regression-suite/947-ACL.test
@@ -0,0 +1,42 @@
+#
+# ACL setting default privileges on a collection to nothing, and
+# specific privileges to include read-acl.
+#
+TYPE=ACL
+URL=http://regression.host/caldav.php/user1/
+HEADER=User-Agent: RFC3744 Spec Tests
+HEADER=Content-Type: text/xml; charset="UTF-8"
+HEAD
+
+
+BEGINDATA
+
+
+
+
+ /caldav.php/user4/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ENDDATA
+
+QUERY
+SELECT by_principal, by_collection, privileges, p_to.displayname, to_principal
+ FROM grants JOIN dav_principal p_to ON (to_principal=principal_id)
+ LEFT JOIN collection ON (by_collection=collection.collection_id)
+ LEFT JOIN dav_principal p_by ON (by_principal=p_by.principal_id)
+ WHERE p_by.dav_name = '/user1/'
+ENDQUERY
+