Refactor checking of If-*-Match headers into a single place.

This also exposes and fixes a bug in PUT vcard where If-None-Match: "*"
was not being correctly processed.
This commit is contained in:
Andrew McMillan 2012-04-04 16:20:41 +12:00
parent ea874375b6
commit 200b3a08dd
5 changed files with 58 additions and 49 deletions

View File

@ -197,7 +197,7 @@ class CalDAVRequest
if ( $this->method == 'PROPFIND' || $this->method == 'REPORT' || $this->method == 'PROPPATCH' || $this->method == 'BIND' || $this->method == 'MKTICKET' || $this->method == 'ACL' ) {
if ( !preg_match( '{^(text|application)/xml$}', $this->content_type ) ) {
@dbg_error_log( "LOG request", 'Request is "%s" but client set content-type to "%s". Assuming they meant XML!',
$request->method, $this->content_type );
$this->method, $this->content_type );
$this->content_type = 'text/xml';
}
}
@ -1076,6 +1076,59 @@ EOSQL;
}
/**
* Check that the incoming Etag matches the one for the existing (or non-existing) resource.
*
* @param boolean $exists Whether the destination exists
* @param string $dest_etag The etag for the destination.
*/
function CheckEtagMatch( $exists, $dest_etag ) {
global $c;
if ( ! $exists ) {
if ( (isset($this->etag_if_match) && $this->etag_if_match != '') ) {
/**
* RFC2068, 14.25:
* If none of the entity tags match, or if "*" is given and no current
* entity exists, the server MUST NOT perform the requested method, and
* MUST return a 412 (Precondition Failed) response.
*/
$this->PreconditionFailed(412, 'if-match', translate('No resource exists at the destination.'));
}
}
else {
if ( isset($c->strict_etag_checking) && $c->strict_etag_checking )
$trim_chars = '\'\\" ';
else
$trim_chars = ' ';
if ( isset($this->etag_if_match) && $this->etag_if_match != '' && trim( $this->etag_if_match, $trim_chars) != trim( $dest_etag, $trim_chars ) ) {
/**
* RFC2068, 14.25:
* If none of the entity tags match, or if "*" is given and no current
* entity exists, the server MUST NOT perform the requested method, and
* MUST return a 412 (Precondition Failed) response.
*/
$this->PreconditionFailed(412,'if-match',sprintf('Existing resource ETag of <<%s>> does not match <<%s>>', $dest_etag, $this->etag_if_match) );
}
else if ( isset($this->etag_none_match) && $this->etag_none_match != ''
&& ($this->etag_none_match == $dest_etag || $this->etag_none_match == '*') ) {
/**
* RFC2068, 14.26:
* If any of the entity tags match the entity tag of the entity that
* would have been returned in the response to a similar GET request
* (without the If-None-Match header) on that resource, or if "*" is
* given and any current entity exists for that resource, then the
* server MUST NOT perform the requested method.
*/
$this->PreconditionFailed(412,'if-none-match', translate( 'Existing resource matches "If-None-Match" header - not accepted.'));
}
}
}
/**
* Is the user has the privileges to do what is requested.
*/

View File

@ -63,13 +63,7 @@ else {
$dest->NeedPrivilege('DAV::write-content');
}
if ( isset($request->etag_none_match) && $request->etag_none_match != '*' && $dest->Exists() ) {
$request->DoResponse(412);
}
if ( isset($request->etag_if_match) && $request->etag_if_match != $dest->unique_tag() ) {
$request->DoResponse(412);
}
$request->CheckEtagMatch( $dest->Exists(), $dest->unique_tag() );
$collection_id = $container->GetProperty('collection_id');

View File

@ -76,39 +76,7 @@ if ( $dav_resource->IsCollection() ) {
$etag = md5($request->raw_post);
if ( ! $dav_resource->Exists() && (isset($request->etag_if_match) && $request->etag_if_match != '') ) {
/**
* RFC2068, 14.25:
* If none of the entity tags match, or if "*" is given and no current
* entity exists, the server MUST NOT perform the requested method, and
* MUST return a 412 (Precondition Failed) response.
*/
$request->PreconditionFailed(412,'if-match');
}
if ( $dav_resource->Exists() ) {
if ( isset($request->etag_if_match) && $request->etag_if_match != '' && $request->etag_if_match != $dav_resource->unique_tag() ) {
/**
* RFC2068, 14.25:
* If none of the entity tags match, or if "*" is given and no current
* entity exists, the server MUST NOT perform the requested method, and
* MUST return a 412 (Precondition Failed) response.
*/
$request->PreconditionFailed(412,'if-match',sprintf('Existing resource ETag of "%s" does not match "%s"', $dav_resource->unique_tag(), $request->etag_if_match) );
}
else if ( isset($request->etag_none_match) && $request->etag_none_match != ''
&& ($request->etag_none_match == $dav_resource->unique_tag() || $request->etag_none_match == '*') ) {
/**
* RFC2068, 14.26:
* If any of the entity tags match the entity tag of the entity that
* would have been returned in the response to a similar GET request
* (without the If-None-Match header) on that resource, or if "*" is
* given and any current entity exists for that resource, then the
* server MUST NOT perform the requested method.
*/
$request->PreconditionFailed(412,'if-none-match', translate( 'Existing resource matches "If-None-Match" header - not accepted.'));
}
}
$request->CheckEtagMatch( $dav_resource->Exists(), $dav_resource->unique_tag() );
$put_action_type = ($dav_resource->Exists() ? 'UPDATE' : 'INSERT');
$collection = $dav_resource->GetParentContainer();

View File

@ -65,13 +65,7 @@ else {
$dest->NeedPrivilege('DAV::write-content');
}
if ( isset($request->etag_none_match) && $request->etag_none_match != '*' && $dest->Exists() ) {
$request->PreconditionFailed(412,'if-none-match', translate('A resource already exists at the destination.'));
}
if ( isset($request->etag_if_match) && $request->etag_if_match != $dest->unique_tag() ) {
$request->PreconditionFailed(412,'if-match',sprintf('Existing resource ETag of "%s" does not match "%s"', $dest->unique_tag(), $request->etag_if_match) );
}
$request->CheckEtagMatch( $dest->Exists(), $dest->unique_tag() );
$collection_id = $container->GetProperty('collection_id');

View File

@ -6,7 +6,7 @@ URL=http://regression.host/caldav.php/user1/addressbook/F06EC844-EACD-4ADF-8823-
HEADER=User-Agent: DAViCalTester/public
HEADER=Content-Type: text/vcard; charset=utf-8
HEADER=If-None-Match: *
HEADER=If-Match: "3419498c6e5eae71dc1704f1787faf1c"
HEAD
BEGINDATA