Test of MOVE predicated by ETag

Timothy Wood tjw at omnigroup.com
Wed Sep 19 01:14:13 EDT 2012


I'm running into what I believe is a bug in the https shipped with Mac OS X 10.8

	Server version: Apache/2.2.22 (Unix)
	Server built:   Jun 20 2012 13:57:09

The scenario is:

	* make a collection
	* collect its ETag with PROPFIND
	* add a file to the collection
	* do a MOVE with an If header requiring the source to have the old ETag

This should fail with a 412 precondition failure.

But, if the collection name has percent-encoded characters in it, it spuriously works -- the MOVE is allowed, even though the If condition is not met.

I've added a test to litmus-0.13 to check this, patch below. If there is a better way to submit this, I'd be happy to reformat it.

Any confirmation on whether this is in fact a bug or whether I'm mistaken would also be very welcome.

Thanks!

-tim



Index: configure
===================================================================
--- configure	(revision 172911)
+++ configure	(revision 172912)
@@ -3426,7 +3426,7 @@
 test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
 
 
-TESTS="basic copymove props locks http"
+TESTS="basic copymove props locks http if_etag"
 
 
 
Index: Makefile.in
===================================================================
--- Makefile.in	(revision 172911)
+++ Makefile.in	(revision 172912)
@@ -90,6 +90,9 @@
 http: src/http.o $(ODEPS)
 	$(CC) $(LDFLAGS) -o $@ src/http.o $(ALL_LIBS)
 
+if_etag: src/if_etag.o $(ODEPS)
+	$(CC) $(LDFLAGS) -o $@ src/if_etag.o $(ALL_LIBS)
+
 largefile: src/largefile.o $(ODEPS)
 	$(CC) $(LDFLAGS) -o $@ src/largefile.o $(ALL_LIBS)
 
@@ -120,4 +123,5 @@
 src/locks.o: src/locks.c $(HDRS)
 src/props.o: src/props.c $(HDRS)
 src/http.o: src/http.c $(HDRS)
+src/if_etag.o: src/if_etag.c $(HDRS)
 src/largefile.o: src/largefile.c $(HDRS)
Index: src/if_etag.c
===================================================================
--- src/if_etag.c	(revision 0)
+++ src/if_etag.c	(revision 172912)
@@ -0,0 +1,156 @@
+/* 
+   litmus: WebDAV server test suite
+   Copyright (C) 2001-2002, 2007, Joe Orton <joe at manyfish.co.uk>
+                                                                     
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+  
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+  
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "config.h"
+
+#include <sys/types.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <fcntl.h>
+
+#include <ne_request.h>
+#include <ne_string.h>
+#include <ne_props.h>
+
+#include "common.h"
+
+static char *mroot;
+
+static int if_etag_init(void)
+{
+    mroot = ne_concat(i_path, "mroot/", NULL);
+    
+    ONMREQ("MKCOL", mroot, ne_mkcol(i_session, mroot));
+
+    return OK;
+}
+
+static void prop_results(void *userdata, const ne_uri *uri, const ne_prop_result_set *results)
+{
+    const ne_propname ETagName = {"DAV:", "getetag"};
+    const char **etagp = userdata;
+    const char *ETag = ne_propset_value(results, &ETagName);
+    if (ETag)
+        *etagp = strdup(ETag);
+}
+
+// get_etag() not working for collections?
+static char *propfind_etag(const char *url)
+{
+    ne_propfind_handler *handler;
+    const ne_propname names[] = {
+        {"DAV:", "getetag"},
+        {NULL, NULL}
+    };
+
+    char *etag = NULL;
+    
+    handler = ne_propfind_create(i_session, url, NE_DEPTH_ZERO);
+    int rc = ne_propfind_named(handler, names, prop_results, &etag);
+    ne_propfind_destroy(handler);
+    return etag;
+}
+
+// upload_foo() prepends i_path, which we've already done
+static int put_foo(const char *uri)
+{
+    int ret;
+    /* i_foo_fd is rewound automagically by ne_request.c */
+    ret = ne_put(i_session, uri, i_foo_fd);
+    if (ret)
+	t_context("PUT of `%s': %s", uri, ne_get_error(i_session));
+    return ret;
+}
+
+/* Perform a conditional MOVE request with given If: header value,
+ * placing response status-code in *code and class in *klass.  Fails
+ * if requests cannot be dispatched. */
+static int conditional_move(const char *src, const char *dst, const char *ifhdr, int *klass, int *code)
+{
+    ne_request *req;
+    
+    req = ne_request_create(i_session, "MOVE", src);
+
+    ne_print_request_header(req, "Destination", "%s://%s%s", 
+                            ne_get_scheme(i_session), 
+                            ne_get_server_hostport(i_session), dst);
+    ne_print_request_header(req, "If", "%s", ifhdr);
+    
+    ONMREQ("MOVE", src, ne_request_dispatch(req));
+
+    if (code) *code = ne_get_status(req)->code;
+    if (klass) *klass = ne_get_status(req)->klass;
+        
+    ne_request_destroy(req);
+    return OK;
+}
+
+static int move_coll_if_etag(const char *src, const char *dst)
+{
+    char *msrc = ne_concat(mroot, src, NULL);
+    char *mfile = ne_concat(msrc, "foo", NULL);
+    char *mdst = ne_concat(mroot, dst, NULL);
+
+    /* make a collection and get its etag */
+    ONMREQ("MKCOL", msrc, ne_mkcol(i_session, msrc));
+    char *etag = propfind_etag(msrc);
+
+    // put a file in the collection (changing its etag)
+    CALL(put_foo(mfile));
+
+    // move the collection, with the old etag as a requirement
+    char ifhdr[200];
+    ne_snprintf(ifhdr, sizeof ifhdr, "<%s> ([%s])", msrc, etag);
+    
+    int klass, code;
+    CALL(conditional_move(msrc, mdst, ifhdr, &klass, &code));
+    ONV(code != 412, ("Conditional MOVE of modified collection should have failed with 412, but got %d", code));
+    
+    return OK;
+}
+
+static int move_coll_if_etag_basic(void)
+{
+    return move_coll_if_etag("src/", "dst/");
+}
+
+static int move_coll_if_etag_spaces(void)
+{
+    return move_coll_if_etag("s%20r%20c/", "d%20s%20t/");
+}
+
+static int if_etag_cleanup(void)
+{
+    ne_delete(i_session, mroot);
+    return OK;
+}
+
+ne_test tests[] = {
+   INIT_TESTS,
+ 
+   T(if_etag_init),
+   T(move_coll_if_etag_basic),
+   T(move_coll_if_etag_spaces),
+   T(if_etag_cleanup),
+
+   FINISH_TESTS
+};

Property changes on: src/if_etag.c
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+header
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: configure.ac
===================================================================
--- configure.ac	(revision 172911)
+++ configure.ac	(revision 172912)
@@ -18,7 +18,7 @@
 AC_PROG_INSTALL
 
 dnl List of tests
-AC_SUBST([TESTS], ["basic copymove props locks http"])
+AC_SUBST([TESTS], ["basic copymove props locks http if_etag"])
 
 NE_REQUIRE_VERSIONS([0], [25 26 27 28 29])
 NEON_WITHOUT_ZLIB





More information about the litmus mailing list