diff -rubB httpd-2.2.9-orig/modules/dav/fs/repos.c httpd-2.2.9/modules/dav/fs/repos.c
--- httpd-2.2.9-orig/modules/dav/fs/repos.c 2008-01-02 10:41:46.000000000 +0100
+++ httpd-2.2.9/modules/dav/fs/repos.c 2008-07-22 05:24:07.000000000 +0200
@@ -37,7 +37,7 @@
/* to assist in debugging mod_dav's GET handling */
-#define DEBUG_GET_HANDLER 0
+#define DEBUG_GET_HANDLER 1
#define DAV_FS_COPY_BLOCKSIZE 16384 /* copy 16k at a time */
diff -rubB httpd-2.2.9-orig/modules/dav/main/mod_dav.c httpd-2.2.9/modules/dav/main/mod_dav.c
--- httpd-2.2.9-orig/modules/dav/main/mod_dav.c 2008-05-27 17:57:23.000000000 +0200
+++ httpd-2.2.9/modules/dav/main/mod_dav.c 2008-07-26 23:01:20.000000000 +0200
@@ -55,6 +55,7 @@
#include "http_main.h"
#include "http_protocol.h"
#include "http_request.h"
+#include "util_ebcdic.h"
#include "util_script.h"
#include "mod_dav.h"
@@ -103,6 +104,11 @@
};
static int dav_methods[DAV_M_LAST];
+typedef struct
+{
+ char *token;
+ char *path;
+} dav_remote_lock_token;
static int dav_init_handler(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
server_rec *s)
@@ -2550,6 +2556,844 @@
return dav_created(r, NULL, "Collection", 0);
}
+// copied from mod_proxy - maybe add to core or lib?
+DAV_DECLARE(dav_error *) dav_remote_copymove_create_socket(apr_pool_t *p, apr_sockaddr_t *addr, apr_socket_t **newsock)
+{
+ apr_status_t rv;
+
+ while (addr) {
+ if ((rv = apr_socket_create(newsock, addr->family,
+ SOCK_STREAM, 0, p)) != APR_SUCCESS) {
+ /*
+ * this could be an IPv6 address from the DNS but the
+ * local machine won't give us an IPv6 socket; hopefully the
+ * DNS returned an additional address to try
+ */
+ addr = addr->next;
+ continue;
+ }
+
+ rv = apr_socket_connect(*newsock, addr);
+
+ /* if an error occurred, loop round and try again */
+ if (rv != APR_SUCCESS) {
+ apr_socket_close(*newsock);
+ addr = addr->next;
+ continue;
+ }
+
+ return 0;
+ }
+
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, "Socket creating failed");
+}
+
+DAV_DECLARE(dav_error *) dav_remote_create_connection(apr_pool_t *p, apr_uri_t *uri, server_rec *s, conn_rec **conn)
+{
+ apr_status_t rv;
+ apr_sockaddr_t *src_addr;
+ apr_socket_t *socket;
+ dav_error *err;
+
+ rv = apr_sockaddr_info_get(&src_addr, uri->hostname, APR_UNSPEC, uri->port, 0, p);
+ if (APR_SUCCESS != rv) {
+ return dav_new_error(p, HTTP_BAD_REQUEST, 0, apr_pstrcat(p, "DNS lookup failure for: ", uri->hostname, NULL));
+ }
+
+ if (NULL != (err = dav_remote_copymove_create_socket(p, src_addr, &socket))) {
+ return err;
+ }
+
+ *conn = (conn_rec *)ap_run_create_connection(p, s, socket, 0, NULL, apr_bucket_alloc_create(p));
+
+ if (!*conn) {
+ // will it have memory to create error message? i guess not, let the caller check!
+ return 0;
+ }
+
+ /* set up the connection filters */
+ rv = ap_run_pre_connection(*conn, socket);
+ if (rv != OK && rv != DONE) {
+ (*conn)->aborted = 1;
+ return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0, "pre_connection setup failed");
+ }
+
+ return 0;
+}
+
+DAV_DECLARE(dav_error *) dav_remote_close_connection(request_rec *r)
+{
+ // TODO: find a better way to do it
+ r->connection = 0;
+}
+
+DAV_DECLARE(request_rec *) dav_remote_make_fake_request(request_rec *r)
+{
+ request_rec *rp = apr_pcalloc(r->pool, sizeof(*r));
+
+ rp->pool = r->pool;
+ rp->status = HTTP_OK;
+
+ rp->headers_in = apr_table_make(r->pool, 12);
+ rp->subprocess_env = apr_table_make(r->pool, 12);
+ rp->headers_out = apr_table_make(r->pool, 12);
+ rp->err_headers_out = apr_table_make(r->pool, 5);
+ rp->notes = apr_table_make(r->pool, 5);
+
+ rp->server = r->server;
+ rp->proxyreq = r->proxyreq;
+ rp->request_time = r->request_time;
+
+ rp->connection = 0;
+
+ // TODO: is it ok to do that?
+ rp->request_config = r->request_config;
+ rp->per_dir_config = r->per_dir_config;
+}
+
+DAV_DECLARE(dav_error *) dav_remote_set_connection(request_rec *r, apr_uri_t *resource_uri)
+{
+ dav_error *err;
+ conn_rec *conn;
+
+ if (r->connection && !r->connection->aborted && (r->connection->keepalive == AP_CONN_KEEPALIVE)) {
+ return 0;
+ }
+
+ if (NULL != (err = dav_remote_create_connection(r->pool, resource_uri, r->server, &conn))) {
+ return err;
+ }
+
+ conn->keepalive = AP_CONN_KEEPALIVE;
+
+ r->connection = conn;
+ r->output_filters = conn->output_filters;
+ r->input_filters = conn->input_filters;
+ r->proto_output_filters = conn->output_filters;
+ r->proto_input_filters = conn->input_filters;
+
+ return 0;
+}
+
+DAV_DECLARE(dav_error *) dav_remote_send_resource_content(request_rec *r, const dav_resource *resource)
+{
+ dav_error *err;
+ apr_bucket_brigade *request_brigade;
+ apr_bucket *e;
+ apr_status_t rv;
+
+ /* okay... time to deliver the content */
+ if ((err = (*resource->hooks->deliver)(resource,
+ r->output_filters)) != NULL) {
+ return dav_push_error(r->pool, err->status, 0,
+ "Unable to deliver content.",
+ err);
+ }
+
+ request_brigade = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+
+ e = apr_bucket_flush_create(r->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(request_brigade, e);
+
+ if (APR_SUCCESS != (rv = ap_pass_brigade(r->output_filters, request_brigade)))
+ return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, "Sending content failed");
+ else
+ return NULL;
+}
+
+DAV_DECLARE(dav_error *) dav_remote_send_string(request_rec *r, const char *string)
+{
+ dav_error *err;
+ apr_bucket_brigade *request_brigade;
+ apr_bucket *e;
+ apr_status_t rv;
+
+ request_brigade = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+
+ e = apr_bucket_pool_create(string, strlen(string), r->pool, r->connection->bucket_alloc);
+ ap_xlate_proto_to_ascii(string, strlen(string));
+ APR_BRIGADE_INSERT_TAIL(request_brigade, e);
+
+ e = apr_bucket_flush_create(r->connection->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(request_brigade, e);
+
+ if (APR_SUCCESS != (rv = ap_pass_brigade(r->output_filters, request_brigade)))
+ return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, "Sending content failed");
+ else
+ return NULL;
+}
+
+struct dav_remote_send_headers_wrapper {
+ apr_bucket_alloc_t *bucket_alloc;
+ apr_bucket_brigade *request_brigade;
+ apr_pool_t *p;
+};
+
+int dav_remote_send_header(void *rec, const char *key, const char *value)
+{
+ struct dav_remote_send_headers_wrapper *wrapper = (struct dav_remote_send_headers_wrapper *)rec;
+
+ apr_bucket_brigade *request_brigade = wrapper->request_brigade;
+ apr_bucket *e;
+ char *buf;
+
+ buf = apr_pstrcat(wrapper->p, key, ": ", value, CRLF, NULL);
+ e = apr_bucket_pool_create(buf, strlen(buf), wrapper->p, wrapper->bucket_alloc);
+ ap_xlate_proto_to_ascii(buf, strlen(buf));
+ APR_BRIGADE_INSERT_TAIL(request_brigade, e);
+}
+
+DAV_DECLARE(dav_error *) dav_remote_read_status_line(request_rec *r)
+{
+ apr_bucket_brigade *response_brigade;
+
+ char buff[HUGE_STRING_LEN];
+ char *buff_p = buff;
+ char keepchar;
+ apr_size_t len;
+
+ response_brigade = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+ ap_rgetline(&buff_p, sizeof(buff), &len, r, 0, response_brigade);
+
+ if (!apr_date_checkmask(buff, "HTTP/#.# ###*")) {
+ return dav_new_error(r->pool, HTTP_BAD_GATEWAY, 0, "Invalid response status line");
+ }
+
+ int major, minor;
+
+ if (2 != sscanf(buff, "HTTP/%u.%u", &major, &minor)) {
+ major = 1;
+ minor = 1;
+ }
+ else if ((buff[5] != '1') || (len >= sizeof(buff) - 1)) {
+ return dav_new_error(r->pool, HTTP_BAD_GATEWAY, 0, "Invalid response status line");
+ }
+
+ keepchar = buff[12];
+ buff[12] = '\0';
+ r->status = atoi(&buff[9]);
+
+ if (keepchar != '\0') {
+ buff[12] = keepchar;
+ } else {
+ /* 2616 requires the space in Status-Line; the origin
+ * server may have sent one but ap_rgetline_core will
+ * have stripped it. */
+ buff[12] = ' ';
+ buff[13] = '\0';
+ }
+ r->status_line = apr_pstrdup(r->pool, &buff[9]);
+
+ return 0;
+}
+
+DAV_DECLARE(dav_error *) dav_remote_read_response_headers(request_rec *r)
+{
+ dav_error *err;
+ apr_bucket_brigade *response_brigade;
+ apr_size_t len;
+ apr_status_t rv;
+ char buff[HUGE_STRING_LEN];
+ char *buff_p = buff;
+ char *value, *end;
+
+ apr_table_clear(r->headers_in);
+
+ response_brigade = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+ if (APR_SUCCESS != (rv = ap_rgetline(&buff_p, sizeof(buff), &len, r, 0, response_brigade))) {
+ return dav_new_error(r->pool, HTTP_BAD_GATEWAY, 0, "Error reading response header");
+ }
+
+ while (0 < len) {
+ if (!(value = strchr(buff, ':'))) {
+ if (APR_SUCCESS != (rv = ap_rgetline(&buff_p, sizeof(buff), &len, r, 0, response_brigade))) {
+ return dav_new_error(r->pool, HTTP_BAD_GATEWAY, 0, "Error reading response header");
+ }
+ }
+
+ *value = '\0';
+ ++value;
+ /* XXX: RFC2068 defines only SP and HT as whitespace, this test is
+ * wrong... and so are many others probably.
+ */
+ while (apr_isspace(*value))
+ ++value; /* Skip to start of value */
+
+ /* should strip trailing whitespace as well */
+ for (end = &value[strlen(value)-1]; end > value && apr_isspace(*end); --end)
+ *end = '\0';
+
+ apr_table_add(r->headers_in, buff, value);
+
+ if (APR_SUCCESS != (rv = ap_rgetline(&buff_p, sizeof(buff), &len, r, 0, response_brigade))) {
+ return dav_new_error(r->pool, HTTP_BAD_GATEWAY, 0, "Error reading response header");
+ }
+ }
+
+ return 0;
+}
+
+typedef DAV_DECLARE(dav_error *) (* write_request_content_callback) (request_rec *, void *data);
+typedef DAV_DECLARE(dav_error *) (* read_reponse_content_callback) (request_rec *, void *data);
+
+DAV_DECLARE(dav_error *) dav_remote_skip_response_content(request_rec *r, void *unused)
+{
+ if (APR_SUCCESS != ap_discard_request_body(r))
+ return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, "Error during discarding response body");
+
+ return NULL;
+}
+
+DAV_DECLARE(dav_error *) dav_remote_method_request(request_rec *r, const char *method, apr_uri_t *resource_uri,
+ const char *resource_path, const dav_resource *resource,
+ write_request_content_callback write_request_content, void *write_request_content_data,
+ read_reponse_content_callback read_response_content, void *read_response_content_data)
+{
+ apr_status_t rv;
+ dav_error *err;
+ apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc;
+ apr_bucket_brigade *request_brigade;
+ apr_bucket *e;
+ ap_filter_t *http_in_filter;
+ char *buf;
+
+ // as I see nobody in httpd check for NULL results of malloc/apr_palloc, so why I should?
+ request_brigade = apr_brigade_create(r->pool, bucket_alloc);
+
+ // request line
+ buf = apr_pstrcat(r->pool, method, " ", resource_uri->path, " HTTP/1.1", CRLF, NULL);
+ e = apr_bucket_pool_create(buf, strlen(buf), r->pool, bucket_alloc);
+ ap_xlate_proto_to_ascii(buf, strlen(buf));
+ APR_BRIGADE_INSERT_TAIL(request_brigade, e);
+
+ // headers
+ struct dav_remote_send_headers_wrapper wrapper;
+ wrapper.bucket_alloc = bucket_alloc;
+ wrapper.request_brigade = request_brigade;
+ wrapper.p = r->pool;
+
+ apr_table_do(dav_remote_send_header, &wrapper, r->headers_out, 0);
+
+ // empty line after headers
+ buf = apr_pstrcat(r->pool, CRLF, NULL);
+ e = apr_bucket_pool_create(buf, strlen(buf), r->pool, bucket_alloc);
+ ap_xlate_proto_to_ascii(buf, strlen(buf));
+ APR_BRIGADE_INSERT_TAIL(request_brigade, e);
+
+ e = apr_bucket_flush_create(bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(request_brigade, e);
+
+ // send it now
+ rv = ap_pass_brigade(r->output_filters, request_brigade);
+
+ if (rv != APR_SUCCESS) {
+ return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, "Sending request failed");
+ }
+
+ // for PUT requests
+ if (NULL != write_request_content) {
+ if (NULL != (err = write_request_content(r, write_request_content_data))) {
+ return dav_push_error(r->pool, err->status, 0,
+ apr_pstrcat(r->pool, "Could not write request for ", method, " for ", resource_path, NULL), err);
+ }
+ }
+
+ if (NULL != (err = dav_remote_read_status_line(r))) {
+ return dav_push_error(r->pool, err->status, 0,
+ apr_pstrcat(r->pool, "Could not read status for ", method, " for ", resource_path, NULL), err);
+ }
+
+ if (NULL != (err = dav_remote_read_response_headers(r))) {
+ return dav_push_error(r->pool, err->status, 0,
+ apr_pstrcat(r->pool, "Could not read response headers for ", method, " for ", resource_path, NULL), err);
+ }
+
+ http_in_filter = ap_add_input_filter("HTTP_IN", NULL, r, r->connection);
+
+ if (NULL == read_response_content) {
+ read_response_content = dav_remote_skip_response_content;
+ }
+
+ if (NULL != (err = read_response_content(r, read_response_content_data))) {
+ return dav_push_error(r->pool, err->status, 0,
+ apr_pstrcat(r->pool, "Could not read response content for ", method, " for ", resource_path, NULL), err);
+ }
+
+ ap_remove_input_filter(http_in_filter);
+
+ // MOVED PERMANENTLY - check with '/' at the end
+ if ((r->status == HTTP_MOVED_PERMANENTLY) && (resource_uri->path[strlen(resource_uri->path) - 1] != '/')) {
+ apr_uri_t *resource_with_slash_uri = apr_palloc(r->pool, sizeof(apr_uri_t));
+
+ resource_path = apr_pstrcat(r->pool, resource_path, "/", NULL);
+ // TODO: do something with it
+ if (strcmp(resource_path, apr_table_get(r->headers_in, "Location")) != 0) {
+ return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, "No idea what to do...");
+ }
+
+ *resource_with_slash_uri = *resource_uri;
+ resource_with_slash_uri->path = apr_pstrcat(r->pool, resource_uri->path, "/", NULL);
+
+ return dav_remote_method_request(r, method, resource_with_slash_uri, resource_path, resource, NULL, NULL, NULL, NULL);
+ }
+
+ return NULL;
+}
+
+DAV_DECLARE(dav_error *) dav_remote_resource_exists(request_rec *r, apr_uri_t *resource_uri,
+ const char *resource_path, const dav_resource *resource, int *exists)
+{
+ dav_error *err;
+
+ *exists = 0;
+
+ if (NULL != (err = dav_remote_set_connection(r, resource_uri))) {
+ return dav_push_error(r->pool, err->status, 0, "Unable to create connection in dav_remote_resource_exists.", err);
+ }
+
+ apr_table_clear(r->headers_out);
+ apr_table_add(r->headers_out, "Host", resource_uri->hostname);
+ apr_table_add(r->headers_out, "Content-Length", "0");
+ apr_table_add(r->headers_out, "Depth", "0");
+ apr_table_add(r->headers_out, "Connection", "Keep-Alive");
+
+ if (NULL != (err = dav_remote_method_request(r, "HEAD", resource_uri, resource_path,
+ resource,
+ NULL, NULL,
+ NULL, NULL))) {
+ return dav_push_error(r->pool, err->status, 0, "HEAD in dav_remote_resource_exists failed.", err);
+ }
+
+ *exists = r->status != HTTP_NOT_FOUND;
+ return NULL;
+}
+
+DAV_DECLARE(dav_error *) dav_remote_resource_delete(request_rec *r, apr_uri_t *resource_uri,
+ const char *resource_path, const dav_resource *resource)
+{
+ dav_error *err;
+
+ if (NULL != (err = dav_remote_set_connection(r, resource_uri))) {
+ return dav_push_error(r->pool, err->status, 0, "Unable to create connection in dav_remote_resource_delete.", err);
+ }
+
+ apr_table_clear(r->headers_out);
+ apr_table_add(r->headers_out, "Host", resource_uri->hostname);
+ apr_table_add(r->headers_out, "Content-Length", "0");
+ apr_table_add(r->headers_out, "Connection", "Keep-Alive");
+
+ if (NULL != (err = dav_remote_method_request(r, "DELETE", resource_uri, resource_path, resource, NULL, NULL, NULL, NULL))) {
+ return dav_push_error(r->pool, err->status, 0, "DELETE in dav_remote_resource_delete failed.", err);
+ }
+
+ if (r->status == HTTP_OK || r->status == HTTP_NO_CONTENT || r->status == HTTP_NOT_FOUND)
+ return NULL;
+ else
+ return dav_new_error(r->pool, r->status, 0, "DELETE refused by remote server");
+}
+
+DAV_DECLARE(dav_error *) dav_remote_resource_mkcol(request_rec *r, apr_uri_t *resource_uri,
+ const char *resource_path, const dav_resource *resource, int overwrite, dav_remote_lock_token token)
+{
+ dav_error *err;
+
+ if (NULL != (err = dav_remote_set_connection(r, resource_uri))) {
+ return dav_push_error(r->pool, err->status, 0, "Unable to create connection in dav_remote_resource_delete.", err);
+ }
+
+ apr_table_clear(r->headers_out);
+ apr_table_add(r->headers_out, "Host", resource_uri->hostname);
+ apr_table_add(r->headers_out, "Content-Length", "0");
+ apr_table_add(r->headers_out, "Connection", "Keep-Alive");
+
+ if (!overwrite) {
+ apr_table_add(r->headers_out, "If-None-Match", "*");
+ }
+
+ if (token.token && token.path) {
+ apr_table_add(r->headers_out, "If", apr_pstrcat(r->pool, "<", token.path, "> (", token.token, ")", NULL));
+ }
+
+ if (NULL != (err = dav_remote_method_request(r, "MKCOL", resource_uri, resource_path, resource, NULL, NULL, NULL, NULL))) {
+ return dav_push_error(r->pool, err->status, 0, "MKCOL failed.", err);
+ }
+
+ if (r->status != HTTP_CREATED)
+ return dav_new_error(r->pool, r->status, 0, "Remote server refused to make collection");
+ else
+ return NULL;
+}
+
+DAV_DECLARE(dav_error *) dav_remote_resource_put(request_rec *r, apr_uri_t *resource_uri,
+ const char *resource_path, const dav_resource *resource, int overwrite, dav_remote_lock_token token)
+{
+ dav_error *err;
+
+ if (NULL != (err = dav_remote_set_connection(r, resource_uri))) {
+ return dav_push_error(r->pool, err->status, 0, "Unable to create connection in dav_remote_resource_delete.", err);
+ }
+
+ apr_table_clear(r->headers_out);
+ apr_table_add(r->headers_out, "Host", resource_uri->hostname);
+ apr_table_add(r->headers_out, "Connection", "Keep-Alive");
+
+ if (!overwrite) {
+ apr_table_add(r->headers_out, "If-None-Match", "*");
+ }
+
+ if (token.token && token.path) {
+ apr_table_add(r->headers_out, "If", apr_pstrcat(r->pool, "<", token.path, "> (", token.token, ")", NULL));
+ }
+
+ if ((err = (*resource->hooks->set_headers)(r, resource)) != NULL) {
+ return dav_push_error(r->pool, err->status, 0,
+ "Unable to set up HTTP headers.",
+ err);
+ }
+
+ apr_table_unset(r->headers_out, "Accept-Ranges");
+ apr_table_unset(r->headers_out, "ETag");
+
+ if (NULL != (err = dav_remote_method_request(r, "PUT", resource_uri, resource_path, resource,
+ dav_remote_send_resource_content, (void *)resource,
+ NULL, NULL))) {
+ return dav_push_error(r->pool, err->status, 0, "PUT failed.", err);
+ }
+
+ if (r->status != HTTP_CREATED && r->status != HTTP_NO_CONTENT)
+ return dav_new_error(r->pool, r->status, 0, "Remote server does not accept resource");
+ else
+ return NULL;
+}
+
+DAV_DECLARE(dav_error *) dav_remote_resource_lock(request_rec *r, apr_uri_t *resource_uri,
+ const char *resource_path, const dav_resource *resource, const char *owner, dav_remote_lock_token *token)
+{
+ dav_error *err;
+ char *content;
+ apr_xml_doc *xmlDoc;
+
+ token->token = NULL;
+ token->path = NULL;
+
+ if (NULL != (err = dav_remote_set_connection(r, resource_uri))) {
+ return dav_push_error(r->pool, err->status, 0, "Unable to create connection in dav_remote_resource_delete.", err);
+ }
+
+ // create LOCK request
+ content = apr_pstrcat(r->pool,
+ "\n"
+ "\n"
+ " \n"
+ " \n"
+ " \n"
+ " ", owner, "\n"
+ " \n"
+ "",
+ NULL
+ );
+
+ apr_table_clear(r->headers_out);
+ apr_table_add(r->headers_out, "Host", resource_uri->hostname);
+ apr_table_add(r->headers_out, "Content-Type", "text/xml; charset=\"utf-8\"");
+ apr_table_add(r->headers_out, "Content-Length", apr_itoa(r->pool, strlen(content)));
+ apr_table_add(r->headers_out, "Connection", "Keep-Alive");
+ apr_table_add(r->headers_out, "Depth", "Infinity");
+ // only LOCK null resources
+ apr_table_add(r->headers_out, "If-None-Match", "*");
+
+ if (NULL != (err = dav_remote_method_request(r, "LOCK", resource_uri, resource_path, resource,
+ dav_remote_send_string, content,
+ NULL, NULL))) {
+ return dav_push_error(r->pool, err->status, 0, "LOCK failed.", err);
+ }
+
+ if (r->status == HTTP_OK) {
+ token->token = apr_table_get(r->headers_in, "Lock-Token");
+ token->path = apr_pstrdup(r->pool, resource_uri->path);
+ return NULL;
+ }
+
+ if (r->status == HTTP_PRECONDITION_FAILED) {
+ return dav_new_error(r->pool, r->status, 0, "Resource already locked");
+ }
+
+ return NULL;
+}
+
+DAV_DECLARE(dav_error *) dav_remote_resource_unlock(request_rec *r, apr_uri_t *resource_uri,
+ const char *resource_path, const dav_resource *resource, dav_remote_lock_token *token)
+{
+ dav_error *err;
+ apr_xml_doc *xmlDoc;
+ char *content;
+
+ if (NULL == token->token || NULL == token->path)
+ return 0; // no need
+
+ if (NULL != (err = dav_remote_set_connection(r, resource_uri))) {
+ return dav_push_error(r->pool, err->status, 0, "Unable to create connection in dav_remote_resource_delete.", err);
+ }
+
+ apr_table_clear(r->headers_out);
+ apr_table_add(r->headers_out, "Host", resource_uri->hostname);
+ apr_table_add(r->headers_out, "Connection", "Keep-Alive");
+ apr_table_add(r->headers_out, "Depth", "Infinity");
+ apr_table_add(r->headers_out, "Lock-Token", token->token);
+
+ if (NULL != (err = dav_remote_method_request(r, "UNLOCK", resource_uri, resource_path, resource,
+ NULL, NULL,
+ NULL, NULL))) {
+ return dav_push_error(r->pool, err->status, 0, "UNLOCK failed.", err);
+ }
+
+ if (r->status != HTTP_NO_CONTENT) {
+ return dav_new_error(r->pool, r->status, 0, "Unable to unlock resource");
+ }
+
+ return NULL;
+}
+
+DAV_DECLARE(dav_error *) dav_remote_resource_proppatch(request_rec *source_r, request_rec *r, apr_uri_t *resource_uri,
+ const char *resource_path, const dav_resource *resource, dav_remote_lock_token token, dav_lockdb *lockdb)
+{
+ dav_error *err;
+ const dav_hooks_db *prop_db_hooks;
+ const dav_db *prop_db;
+ dav_prop_name name;
+ char *content;
+ apr_text_header hdr = { 0 };
+ apr_text_header hdr_ns = { 0 };
+
+ prop_db_hooks = DAV_GET_HOOKS_PROPDB(r);
+ if (NULL == prop_db_hooks) {
+ return NULL; // nothing to do
+ }
+
+ if (NULL != (err = prop_db_hooks->open(r->pool, resource, 1, &prop_db))) {
+ return err;
+ }
+
+ if (NULL == prop_db)
+ return NULL; // nothing to do
+
+ if (NULL != (err = prop_db_hooks->first_name(prop_db, &name))) {
+ prop_db_hooks->close(prop_db);
+ return err;
+ }
+
+ dav_xmlns_info *xi = dav_xmlns_create(r->pool);
+
+ if (NULL != (err = (*prop_db_hooks->define_namespaces)(prop_db, xi))) {
+ prop_db_hooks->close(prop_db);
+ return err;
+ }
+
+ while (NULL != name.ns) {
+ int found;
+
+ if (NULL != (err = (*prop_db_hooks->output_value)(prop_db, &name,
+ xi, &hdr, &found))) {
+ prop_db_hooks->close(prop_db);
+ return err;
+ }
+
+ if (NULL != (err = prop_db_hooks->next_name(prop_db, &name))) {
+ prop_db_hooks->close(prop_db);
+ return err;
+ }
+ }
+
+ dav_xmlns_generate(xi, &hdr_ns);
+
+
+ // create LOCK request
+ content = apr_pstrcat(r->pool,
+ "\n"
+ "pool, content, text->text, NULL);
+ text = text->next;
+ }
+
+ content = apr_pstrcat(r->pool, content, ">\n"
+ " \n"
+ " \n",
+ NULL
+ );
+
+ text = hdr.first;
+ while (text) {
+ content = apr_pstrcat(r->pool, content, " ", text->text, NULL);
+ text = text->next;
+ }
+
+ content = apr_pstrcat(r->pool, content,
+ " \n"
+ " \n"
+ "\n",
+ NULL
+ );
+
+ apr_table_clear(r->headers_out);
+ apr_table_add(r->headers_out, "Host", resource_uri->hostname);
+ apr_table_add(r->headers_out, "Connection", "Keep-Alive");
+ apr_table_add(r->headers_out, "Content-Length", apr_itoa(r->pool, strlen(content)));
+ apr_table_add(r->headers_out, "Content-Type", "text/xml; charset=\"utf-8\"");
+
+ if (token.token && token.path) {
+ apr_table_add(r->headers_out, "If", apr_pstrcat(r->pool, "<", token.path, "> (", token.token, ")", NULL));
+ }
+
+ if (NULL != (err = dav_remote_method_request(r, "PROPPATCH", resource_uri, resource_path, resource,
+ dav_remote_send_string, content,
+ NULL, NULL))) {
+ return dav_push_error(r->pool, err->status, 0, "PROPATCH failed.", err);
+ }
+
+ if (r->status != HTTP_NO_CONTENT && r->status != HTTP_MULTI_STATUS) {
+ return dav_new_error(r->pool, r->status, 0, "Unable to unlock resource");
+ }
+
+ return NULL;
+}
+
+struct dav_remote_copymove_copy_walk_ctx
+{
+ request_rec *source_r;
+ request_rec *r;
+ const char *base_uri;
+ apr_uri_t *dest_uri;
+ const char *dest;
+ dav_remote_lock_token token;
+ int overwrite;
+ dav_lockdb *lockdb;
+};
+
+DAV_DECLARE(dav_error *) dav_remote_copymove_walk(dav_walk_resource *wres, int calltype)
+{
+ struct dav_remote_copymove_copy_walk_ctx *ctx = (struct dav_remote_copymove_copy_walk_ctx *)wres->walk_ctx;
+ char *new_resource_path;
+ apr_uri_t new_resource_uri;
+ dav_error *err;
+ int remove_length;
+
+ if (strncmp(wres->resource->uri, ctx->base_uri, strlen(ctx->base_uri)) != 0) {
+ return dav_new_error(wres->pool, HTTP_INTERNAL_SERVER_ERROR, 0, "Invalid uri of subresource");
+ }
+
+ remove_length = strlen(ctx->base_uri);
+ if (wres->resource->collection) {
+ remove_length++;
+ }
+
+ new_resource_path = apr_pstrcat(wres->pool, ctx->dest, &(wres->resource->uri[remove_length]), NULL);
+ printf("from %s and %s on base %s done: %s\n", ctx->dest, wres->resource->uri, ctx->base_uri, new_resource_path);
+
+ // shallow copy, only cange the path
+ new_resource_uri = *ctx->dest_uri;
+ new_resource_uri.path = apr_pstrcat(wres->pool, ctx->dest_uri->path, &(wres->resource->uri[remove_length]), NULL);
+
+ if (wres->resource->collection) {
+ // for lock-token...
+ new_resource_path[strlen(new_resource_path) - 1] = '\0';
+ new_resource_uri.path[strlen(new_resource_uri.path) - 1] = '\0';
+
+ if (NULL != (err = dav_remote_resource_mkcol(ctx->r, &new_resource_uri, new_resource_path, wres->resource, ctx->overwrite, ctx->token)))
+ return err;
+ } else {
+ if (NULL != (err = dav_remote_resource_put(ctx->r, &new_resource_uri, new_resource_path, wres->resource, ctx->overwrite, ctx->token)))
+ return err;
+ }
+
+ if (NULL != (err = dav_remote_resource_proppatch(ctx->source_r, ctx->r, ctx->dest_uri,
+ ctx->dest, wres->resource, ctx->token, ctx->lockdb))) {
+ return err;
+ }
+
+ return NULL;
+}
+
+DAV_DECLARE(dav_error *) dav_remote_copymove(request_rec *r, dav_resource *resource,
+ const char *dest, int overwrite, int depth, dav_lockdb *lockdb)
+{
+ dav_error *err = NULL;
+ dav_error *lock_err = NULL;
+ apr_uri_t dest_uri;
+ conn_rec *conn;
+ request_rec *rp;
+ int exists;
+ int deleted;
+ dav_walk_params walk_params = { 0 };
+ dav_response *response;
+ const char *lock_owner;
+ dav_remote_lock_token token;
+
+ if (APR_SUCCESS != apr_uri_parse(r->pool, dest, &dest_uri)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Destination URI cannot be parsed.", NULL);
+ return dav_new_error(r->pool, HTTP_BAD_REQUEST, 0, "Destination URI cannot be parsed.");
+ }
+
+ if (!dest_uri.port) {
+ dest_uri.port = apr_uri_port_of_scheme(dest_uri.scheme);
+ }
+
+ // 1. create request
+ rp = dav_remote_make_fake_request(r);
+
+ if (!overwrite) {
+ // 2. check if exists
+ if (NULL != (err = dav_remote_resource_exists(rp, &dest_uri, dest, resource, &exists))) {
+ return err;
+ }
+
+ if (exists) {
+ return dav_new_error(r->pool, HTTP_PRECONDITION_FAILED, 0, "Destination resource already exists.");
+ }
+ }
+ else {
+ // 2. delete old resource
+ if (NULL != (err = dav_remote_resource_delete(rp, &dest_uri, dest, resource))) {
+ return err;
+ }
+ }
+
+ // 3. gain lock on NULL resource
+ lock_owner = r->unparsed_uri;
+ if (NULL != (err = dav_remote_resource_lock(rp, &dest_uri, dest, resource, lock_owner, &token))) {
+ return err;
+ }
+
+ struct dav_remote_copymove_copy_walk_ctx ctx;
+ ctx.source_r = r;
+ ctx.r = rp;
+ ctx.base_uri = resource->uri;
+ ctx.dest_uri = &dest_uri;
+ ctx.dest = dest;
+ ctx.overwrite = overwrite;
+ ctx.token = token;
+ ctx.lockdb = lockdb;
+
+ walk_params.walk_type = DAV_WALKTYPE_AUTH | DAV_WALKTYPE_NORMAL;
+ walk_params.func = dav_remote_copymove_walk;
+ walk_params.walk_ctx = &ctx;
+ walk_params.pool = r->pool;
+ walk_params.root = resource;
+
+ // 4. copy resources/collections
+ err = (*resource->hooks->walk)(&walk_params, depth, &response);
+
+ // 5. release lock
+ if (NULL != (err = dav_remote_resource_unlock(rp, &dest_uri, dest, resource, &token))) {
+ return err;
+ }
+
+ return NULL;
+}
+
/* handle the COPY and MOVE methods */
static int dav_method_copymove(request_rec *r, int is_move)
{
@@ -2559,9 +3403,9 @@
dav_auto_version_info dst_av_info = { 0 };
const char *body;
const char *dest;
- dav_error *err;
- dav_error *err2;
- dav_error *err3;
+ dav_error *err = NULL;
+ dav_error *err2 = NULL;
+ dav_error *err3 = NULL;
dav_response *multi_response;
dav_lookup_result lookup;
int is_dir;
@@ -2571,6 +3415,7 @@
dav_lockdb *lockdb;
int replace_dest;
int resnew_state;
+ int remote = 0;
/* Ask repository module to resolve the resource */
err = dav_get_resource(r, !is_move /* label_allowed */,
@@ -2609,6 +3454,13 @@
return HTTP_BAD_REQUEST;
}
+ /* get and parse the overwrite header value */
+ if ((overwrite = dav_get_overwrite(r)) < 0) {
+ /* dav_get_overwrite() supplies additional information for the
+ * default message. */
+ return HTTP_BAD_REQUEST;
+ }
+
lookup = dav_lookup_uri(dest, r, 1 /* must_be_absolute */);
if (lookup.rnew == NULL) {
if (lookup.err.status == HTTP_BAD_REQUEST) {
@@ -2618,11 +3470,21 @@
return HTTP_BAD_REQUEST;
}
+ if (lookup.err.status == HTTP_BAD_GATEWAY) {
+ err = dav_get_resource(r, 1 /* label_allowed */, 0 /* use_checked_in */,
+ &resource);
+ remote = 1;
+ }
+
+ if (!remote) {
/* ### this assumes that dav_lookup_uri() only generates a status
* ### that Apache can provide a status line for!! */
return dav_error_response(r, lookup.err.status, lookup.err.desc);
}
+ }
+
+ if (!remote) {
if (lookup.rnew->status != HTTP_OK) {
const char *auth = apr_table_get(lookup.rnew->err_headers_out,
"WWW-Authenticate");
@@ -2651,21 +3513,14 @@
/* are the two resources handled by the same repository? */
if (resource->hooks != resnew->hooks) {
- /* ### this message exposes some backend config, but screw it... */
- return dav_error_response(r, HTTP_BAD_GATEWAY,
- "Destination URI is handled by a "
- "different repository than the source URI. "
- "MOVE or COPY between repositories is "
- "not possible.");
+ err = dav_get_resource(r, 1 /* label_allowed */, 0 /* use_checked_in */,
+ &resource);
+ remote = 1;
}
-
- /* get and parse the overwrite header value */
- if ((overwrite = dav_get_overwrite(r)) < 0) {
- /* dav_get_overwrite() supplies additional information for the
- * default message. */
- return HTTP_BAD_REQUEST;
}
+ // recheck remote, maybe it changed
+ if (!remote) {
/* quick failure test: if dest exists and overwrite is false. */
if (resnew->exists && !overwrite) {
/* Supply some text for the error response body. */
@@ -2679,7 +3534,7 @@
/* Supply some text for the error response body. */
return dav_error_response(r, HTTP_FORBIDDEN,
"Source and Destination URIs are the same.");
-
+ }
}
is_dir = resource->collection;
@@ -2732,6 +3587,8 @@
return dav_handle_err(r, err, multi_response);
}
+ if (!remote)
+ {
/*
* Check If-Headers and existing locks for destination. Note that we
* use depth==infinity since the target (hierarchy) will be deleted
@@ -2778,6 +3635,7 @@
"Destination collection contains the Source "
"and Overwrite has been specified.");
}
+ }
/* ### for now, we don't need anything in the body */
if ((result = ap_discard_request_body(r)) != OK) {
@@ -2818,6 +3676,8 @@
}
}
+ if (!remote)
+ {
/*
* Remember the initial state of the destination, so the lock system
* can be notified as to how it changed.
@@ -2894,13 +3754,12 @@
/* perform any auto-versioning cleanup */
err2 = dav_auto_checkin(r, NULL, err != NULL /* undo if error */,
0 /*unlock*/, &dst_av_info);
+ }
if (is_move) {
err3 = dav_auto_checkin(r, NULL, err != NULL /* undo if error */,
0 /*unlock*/, &src_av_info);
}
- else
- err3 = NULL;
/* check for error from remove/copy/move operations */
if (err != NULL) {
@@ -2935,6 +3794,7 @@
dav_log_err(r, err, APLOG_WARNING);
}
+ if (!remote) {
/* propagate any indirect locks at the target */
if (lockdb != NULL) {
@@ -2953,10 +3813,31 @@
return dav_handle_err(r, err, NULL);
}
}
+ }
+
+ if (remote) {
+ if (NULL != (err = dav_remote_copymove(r, resource, dest, overwrite, depth, lockdb))) {
+ return dav_handle_err(r, err, NULL);
+ }
+
+ if (is_move) {
+ if (NULL != (err = (*resource->hooks->remove_resource)(resource, &multi_response))) {
+ return dav_handle_err(r, err, NULL);
+ }
+ }
+
+ if (lockdb != NULL) {
+ (*lockdb->hooks->close_lockdb)(lockdb);
+ }
/* return an appropriate response (HTTP_CREATED or HTTP_NO_CONTENT) */
+ return dav_created(r, dest, "Destination", 0);
+ }
+ if (!remote) {
+ /* return an appropriate response (HTTP_CREATED or HTTP_NO_CONTENT) */
return dav_created(r, lookup.rnew->uri, "Destination",
resnew_state == DAV_RESOURCE_EXISTS);
+ }
}
/* dav_method_lock: Handler to implement the DAV LOCK method
diff -rubB httpd-2.2.9-orig/modules/dav/main/util.c httpd-2.2.9/modules/dav/main/util.c
--- httpd-2.2.9-orig/modules/dav/main/util.c 2008-01-05 10:45:07.000000000 +0100
+++ httpd-2.2.9/modules/dav/main/util.c 2008-07-17 19:19:19.000000000 +0200
@@ -216,10 +216,9 @@
request. the port must match our port.
*/
port = r->connection->local_addr->port;
+
if (strcasecmp(comp.scheme, scheme) != 0
-#ifdef APACHE_PORT_HANDLING_IS_BUSTED
|| comp.port != port
-#endif
) {
result.err.status = HTTP_BAD_GATEWAY;
result.err.desc = apr_psprintf(r->pool,
diff -rubB httpd-2.2.9-orig/modules/http/http_filters.c httpd-2.2.9/modules/http/http_filters.c
--- httpd-2.2.9-orig/modules/http/http_filters.c 2008-05-28 14:58:29.000000000 +0200
+++ httpd-2.2.9/modules/http/http_filters.c 2008-07-22 22:06:42.000000000 +0200
@@ -1372,7 +1372,7 @@
}
/* These are metadata buckets. */
- if (bucket->length == 0) {
+ if (APR_BUCKET_IS_METADATA(bucket)) {
continue;
}