Compare commits

...

7 Commits

Author SHA1 Message Date
Nick v H
35c8d78a15 Merge branch 'master' into 'master'
Enclose XML response to only contain the XML tags (trimming any possible...

See merge request davical-project/davical!82
2025-02-02 23:39:19 +00:00
Andrew Ruthven
8f38332fce Set DAVResource type correctly when restoring from memcache
Assuming we're always dealing with a Principal isn't correct.
2025-01-27 23:27:45 +13:00
Andrew Ruthven
1fec8fd111 Add debug logging for ACE changes 2025-01-27 23:27:45 +13:00
Andrew Ruthven
f6547bd376 Make some lines more readable 2025-01-27 23:27:45 +13:00
Andrew Ruthven
b4bcc6cc25 Fix DAV:current-user-principal for iPhone devices
iPhone devices incorrectly implement DAV:current-user-principal from
RFC 5397. They assume that current-user-principal is the href for the
resource being queried. The RFC says it should be the current resource.

See: https://gitlab.com/davical-project/davical/-/issues/335
2025-01-27 23:27:45 +13:00
Andrew Ruthven
c1cfd8eb0d Fix typo 2025-01-27 23:02:36 +13:00
Nick v H
5920fe1efe Enclose XML response to only contain the XML tags (trimming any possible trailing characters after the last XML tag) 2021-09-20 19:04:42 +00:00
11 changed files with 246 additions and 15 deletions

View File

@ -1,6 +1,9 @@
2025-01-22 Andrew Ruthven <andrew@etc.gen.nz>
* Using a Ticket ID requires public.php
2024-12-13 Andrew Ruthven <andrew@etc.gen.nz>
* Fix iPhone's accessing other principal's collections.
2024-04-15 Andrew Ruthven <andrew@etc.gen.nz>
* Add caching of user credential success/failure

View File

@ -363,13 +363,19 @@ class DAVResource
$this->FetchCollection();
if ( $this->_is_collection ) {
if ( $this->_is_principal || $this->collection->type == 'principal' ) $this->FetchPrincipal();
if ( $this->_is_principal || $this->collection->type == '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':'') );
$this->dav_name,
($this->_is_collection ? ' ' . $this->resourcetypes
: ' not'),
($this->_is_principal ? ' and a principal'
: '')
);
}
@ -569,29 +575,34 @@ EOSQL;
$cache_ns = 'collection-'.preg_replace( '{/[^/]*$}', '/', $this->dav_name);
$cache_key = 'dav_resource'.$session->user_no;
$this->collection = $cache->get( $cache_ns, $cache_key );
if ( $this->collection === false ) {
$this->ReadCollectionFromDatabase();
if ( $this->collection->type != 'principal' && $this->_collection_is_cacheable ) {
$cache_ns = 'collection-'.$this->collection->dav_name;
@dbg_error_log( 'Cache', ':FetchCollection: Setting cache ns "%s" key "%s". Type: %s', $cache_ns, $cache_key, $this->collection->type );
$cache->set( $cache_ns, $cache_key, $this->collection );
}
@dbg_error_log( 'DAVResource', ':FetchCollection: Found collection named "%s" of type "%s".', $this->collection->dav_name, $this->collection->type );
}
else {
@dbg_error_log( 'Cache', ':FetchCollection: Got cache ns "%s" key "%s". Type: %s', $cache_ns, $cache_key, $this->collection->type );
if ( preg_match( '#^(/[^/]+)/?$#', $this->dav_name, $matches)
|| preg_match( '#^((/principals/[^/]+/)[^/]+)/?$#', $this->dav_name, $matches) ) {
@dbg_error_log( 'Cache', ':FetchCollection: Got cache ns "%s" key "%s". dav_name: "%s", Type: %s', $cache_ns, $cache_key, $this->collection->dav_name, $this->collection->type );
if ($this->collection->type == 'principal') {
$this->_is_principal = true;
$this->FetchPrincipal();
$this->collection->is_principal = true;
$this->collection->type = 'principal';
} else {
if ($this->collection->type == 'proxy') {
$this->_is_proxy_resource = true;
$this->proxy_type = $this->collection->proxy_type;
}
} else if ($this->collection->type == 'calendar') {
$this->_is_calendar = true;
} else if ($this->collection->type == 'proxy') {
$this->_is_proxy_resource = true;
$this->proxy_type = $this->collection->proxy_type;
}
@dbg_error_log( 'DAVResource', ':FetchCollection: Read cached collection named "%s" of type "%s".', $this->collection->dav_name, $this->collection->type );
}
@ -1881,8 +1892,15 @@ EOQRY;
$prop->NewElement( 'principal-collection-set', $reply->href( ConstructURL('/') ) );
break;
# iPhone devices incorrectly implement DAV:current-user-principal from
# RFC 5397. They assume that current-user-principal is the href for the
# resource being queried. The RFC says it should be the current resource.
# See: https://gitlab.com/davical-project/davical/-/issues/335
case 'DAV::current-user-principal':
$prop->NewElement('current-user-principal', $reply->href( ConstructURL(DeconstructURL($session->principal->url())) ) );
if ( preg_match('/iPhone/', $_SERVER['HTTP_USER_AGENT']) )
$prop->NewElement('current-user-principal', $reply->href( ConstructURL(DeconstructURL($request->principal->url())) ) );
else
$prop->NewElement('current-user-principal', $reply->href( ConstructURL(DeconstructURL($session->principal->url())) ) );
break;
case 'SOME-DENIED-PROPERTY': /** indicating the style for future expansion */

View File

@ -125,6 +125,9 @@ function process_ace( $grantor, $by_principal, $by_collection, $ace ) {
$principal_content = $principal_node->GetContent();
if ( count($principal_content) != 1 ) $request->MalformedRequest('ACL request must contain exactly one principal per ACE');
$principal_content = $principal_content[0];
dbg_error_log( 'ACE', 'NSTag: "%s", by_collection: %s, by_principal: %s', $principal_content->GetNSTag(), $by_collection ?? 'Null', $by_principal ?? 'Null');
switch( $principal_content->GetNSTag() ) {
case 'DAV::property':
$principal_property = $principal_content->GetContent();
@ -142,8 +145,10 @@ function process_ace( $grantor, $by_principal, $by_collection, $ace ) {
$principal_type = 'href';
$grantee = new DAVResource( DeconstructURL($principal_content->GetContent()) );
$grantee_id = $grantee->getProperty('principal_id');
if ( !$grantee->Exists() || !$grantee->IsPrincipal() )
$request->PreconditionFailed(403,'recognized-principal', 'Principal "' . $principal_content->GetContent() . '" not found.');
$sqlparms = array( ':to_principal' => $grantee_id);
$where = 'WHERE to_principal=:to_principal AND ';
if ( isset($by_principal) ) {
@ -154,6 +159,7 @@ function process_ace( $grantor, $by_principal, $by_collection, $ace ) {
$sqlparms[':by_collection'] = $by_collection;
$where .= 'by_collection = :by_collection';
}
$qry = new AwlQuery('SELECT privileges FROM grants '.$where, $sqlparms);
if ( $qry->Exec('ACL',__LINE__,__FILE__) && $qry->rows() == 1 && $current = $qry->Fetch() ) {
$sql = 'UPDATE grants SET privileges=:privileges::INT::BIT(24) '.$where;
@ -169,6 +175,15 @@ function process_ace( $grantor, $by_principal, $by_collection, $ace ) {
Principal::cacheDelete('dav_name',$grantee->dav_name());
Principal::cacheFlush('principal_id IN (SELECT member_id FROM group_member WHERE group_id = ?)', array($grantee_id));
}
/**
* Basically this has changed everyone's permissions now, so...
*/
$cache = getCacheInstance();
$cache->flush();
#Principal::cacheFlush('TRUE');
break;
case 'DAV::authenticated':

View File

@ -70,7 +70,7 @@ class CalDAVClient {
protected $calendar_urls;
/**
* The useragent which is send to the caldav server
* The useragent which is sent to the caldav server
*
* @var string
*/

View File

@ -26,7 +26,7 @@ class CalDAVClient {
var $base_url, $user, $pass, $calendar, $entry, $protocol, $server, $port;
/**
* The useragent which is send to the caldav server
* The useragent which is sent to the caldav server
*
* @var string
*/
@ -119,12 +119,13 @@ class CalDAVClient {
*/
function ParseResponse( $response ) {
$pos = strpos($response, '<?xml');
$epos = strrpos($response, '>');
if ($pos === false) {
$this->httpResponse = trim($response);
}
else {
$this->httpResponse = trim(substr($response, 0, $pos));
$this->xmlResponse = trim(substr($response, $pos));
$this->xmlResponse = trim(substr($response, $pos, $epos-$pos+1));
}
}

View File

@ -0,0 +1,15 @@
HTTP/1.1 200 OK
Date: Dow, 01 Jan 2000 00:00:00 GMT
DAV: 1, 2, 3, access-control, calendar-access, calendar-schedule
DAV: extended-mkcol, bind, addressbook, calendar-auto-schedule, calendar-proxy
Content-Length: 0
Content-Type: text/plain; charset="utf-8"
SQL Query 1 Result:
by_collection: >1609<
by_principal: >NULL<
displayname: >User 4<
privileges: >000000000000000000100001<
to_principal: >1005<

View File

@ -0,0 +1,47 @@
# Test for iPhone devices which incorrectly implement
# DAV:current-user-principal from RFC 5397. They assume that
# current-user-principal is the href for the resource being queried. The
# RFC says it should be the current resource. #Sigh.
#
# See: https://gitlab.com/davical-project/davical/-/issues/335
#
# Ensure that user4 can access user1's address book.
TYPE=ACL
HEADER=User-Agent: RFC3744 Spec Tests
HEADER=Content-Type: text/xml; charset="UTF-8"
HEAD
BEGINDATA
<?xml version="1.0" encoding="utf-8" ?>
<acl xmlns="DAV:" xmlns:CalDAV="urn:ietf:params:xml:ns:caldav">
<ace>
<principal>
<href>/caldav.php/user4/</href>
</principal>
<grant>
<privilege><read/></privilege>
<privilege><read-current-user-privilege-set/></privilege>
</grant>
</ace>
<ace>
<principal><authenticated/></principal>
<grant>
<privilege/>
</grant>
</ace>
</acl>
ENDDATA
URL=http://regression.host/caldav.php/user1/
# This is by_collection, and by_principal isn't set, shouldn't it be set?
# WHERE p_by.dav_name = '/user1/'
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 by_collection = 1609
AND p_to.dav_name = '/user4/'
ORDER BY by_principal, to_principal
ENDQUERY

View File

@ -0,0 +1,50 @@
HTTP/1.1 207 Multi-Status
Date: Dow, 01 Jan 2000 00:00:00 GMT
Content-Location: /caldav.php/user1/addresses/
DAV: 1, 2, 3, access-control, calendar-access, calendar-schedule
DAV: extended-mkcol, bind, addressbook, calendar-auto-schedule, calendar-proxy
ETag: "43765875e20eef2d841725645b2f3c95"
Content-Length: 1129
Content-Type: text/xml; charset="utf-8"
<?xml version="1.0" encoding="utf-8" ?>
<multistatus xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:carddav">
<response>
<href>/caldav.php/user1/addresses/</href>
<propstat>
<prop>
<getcontenttype>httpd/unix-directory</getcontenttype>
<resourcetype>
<collection/>
<C:addressbook/>
</resourcetype>
<displayname>user1 addresses</displayname>
<getlastmodified>Sun, 15 Mar 1998 12:00:00 GMT</getlastmodified>
<creationdate>19570725T120000Z</creationdate>
<getcontentlanguage/>
<supportedlock>
<lockentry>
<lockscope>
<exclusive/>
</lockscope>
<locktype>
<write/>
</locktype>
</lockentry>
</supportedlock>
<owner>
<href>/caldav.php/user1/</href>
</owner>
<current-user-principal>
<href>/caldav.php/user1/</href>
</current-user-principal>
<C:max-resource-size>6550000</C:max-resource-size>
<C:supported-address-data>
<C:address-data content-type="text/vcard" version="3.0"/>
</C:supported-address-data>
</prop>
<status>HTTP/1.1 200 OK</status>
</propstat>
</response>
</multistatus>

View File

@ -0,0 +1,16 @@
# Test for iPhone devices which incorrectly implement
# DAV:current-user-principal from RFC 5397. They assume that
# current-user-principal is the href for the resource being queried. The
# RFC says it should be the current resource. #Sigh.
#
# See: https://gitlab.com/davical-project/davical/-/issues/335
#
# Ensure that user4 has user1 as the current-user-principal as we're an
# 'iPhone'.
TYPE=PROPFIND
AUTH=user4:user4
HEADER=Content-Type: text/xml; charset="UTF-8"
HEADER=User-Agent: DAVKit/4.0 (728.3); iCalendar/1 (34); iPhone/3.0
HEAD
URL=http://regression.host/caldav.php/user1/addresses

View File

@ -0,0 +1,50 @@
HTTP/1.1 207 Multi-Status
Date: Dow, 01 Jan 2000 00:00:00 GMT
Content-Location: /caldav.php/user1/addresses/
DAV: 1, 2, 3, access-control, calendar-access, calendar-schedule
DAV: extended-mkcol, bind, addressbook, calendar-auto-schedule, calendar-proxy
ETag: "3f9506c10fe5b434f966d4c82f026c40"
Content-Length: 1129
Content-Type: text/xml; charset="utf-8"
<?xml version="1.0" encoding="utf-8" ?>
<multistatus xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:carddav">
<response>
<href>/caldav.php/user1/addresses/</href>
<propstat>
<prop>
<getcontenttype>httpd/unix-directory</getcontenttype>
<resourcetype>
<collection/>
<C:addressbook/>
</resourcetype>
<displayname>user1 addresses</displayname>
<getlastmodified>Sun, 15 Mar 1998 12:00:00 GMT</getlastmodified>
<creationdate>19570725T120000Z</creationdate>
<getcontentlanguage/>
<supportedlock>
<lockentry>
<lockscope>
<exclusive/>
</lockscope>
<locktype>
<write/>
</locktype>
</lockentry>
</supportedlock>
<owner>
<href>/caldav.php/user1/</href>
</owner>
<current-user-principal>
<href>/caldav.php/user4/</href>
</current-user-principal>
<C:max-resource-size>6550000</C:max-resource-size>
<C:supported-address-data>
<C:address-data content-type="text/vcard" version="3.0"/>
</C:supported-address-data>
</prop>
<status>HTTP/1.1 200 OK</status>
</propstat>
</response>
</multistatus>

View File

@ -0,0 +1,16 @@
# Test for iPhone devices which incorrectly implement
# DAV:current-user-principal from RFC 5397. They assume that
# current-user-principal is the href for the resource being queried. The
# RFC says it should be the current resource. #Sigh.
#
# See: https://gitlab.com/davical-project/davical/-/issues/335
#
# Ensure that user4 has user4 as the current-user-principal as we're not an
# 'iPhone'.
TYPE=PROPFIND
AUTH=user4:user4
HEADER=Content-Type: text/xml; charset="UTF-8"
HEADER=User-Agent: Evolution/1.8.1
HEAD
URL=http://regression.host/caldav.php/user1/addresses