From 200b3a08dd6a18fe6950348d88894211ce966343 Mon Sep 17 00:00:00 2001 From: Andrew McMillan Date: Wed, 4 Apr 2012 16:20:41 +1200 Subject: [PATCH] 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. --- inc/CalDAVRequest.php | 55 ++++++++++++++++++++++- inc/caldav-PUT-default.php | 8 +--- inc/caldav-PUT-vcalendar.php | 34 +------------- inc/caldav-PUT-vcard.php | 8 +--- testing/tests/carddav/2014-PUT-vcard.test | 2 +- 5 files changed, 58 insertions(+), 49 deletions(-) diff --git a/inc/CalDAVRequest.php b/inc/CalDAVRequest.php index 8daeca43..b95ee182 100644 --- a/inc/CalDAVRequest.php +++ b/inc/CalDAVRequest.php @@ -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. */ diff --git a/inc/caldav-PUT-default.php b/inc/caldav-PUT-default.php index 1b670359..64b1a752 100644 --- a/inc/caldav-PUT-default.php +++ b/inc/caldav-PUT-default.php @@ -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'); diff --git a/inc/caldav-PUT-vcalendar.php b/inc/caldav-PUT-vcalendar.php index a9f5cd8d..b683d929 100644 --- a/inc/caldav-PUT-vcalendar.php +++ b/inc/caldav-PUT-vcalendar.php @@ -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(); diff --git a/inc/caldav-PUT-vcard.php b/inc/caldav-PUT-vcard.php index cc83194d..62ea25c5 100644 --- a/inc/caldav-PUT-vcard.php +++ b/inc/caldav-PUT-vcard.php @@ -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'); diff --git a/testing/tests/carddav/2014-PUT-vcard.test b/testing/tests/carddav/2014-PUT-vcard.test index 99eb53c0..e796e61c 100644 --- a/testing/tests/carddav/2014-PUT-vcard.test +++ b/testing/tests/carddav/2014-PUT-vcard.test @@ -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