diff -urbB 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 -urbB 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-23 19:16:23.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" @@ -2550,6 +2551,652 @@ 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); + + rv = ap_pass_brigade(r->output_filters, request_brigade); + + 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) +{ + apr_size_t content_length; + apr_size_t to_read; + apr_bucket_brigade *response_brigade; + apr_bucket *e; + int seen_eos = 0; + const char *content_length_str; + apr_bucket_alloc_t *bucket_alloc = r->connection->bucket_alloc; + char buff[HUGE_STRING_LEN]; + char *buff_p = buff; + + if (NULL == apr_table_get(r->headers_in, "Keep-Alive")) { // we can close the connection now + dav_remote_close_connection(r); + return NULL; + } + + content_length_str = apr_table_get(r->headers_in, "Content-Length"); + if (NULL == content_length_str) { + return NULL; // no content + } + + content_length = atoi(content_length_str); + if (0 == content_length) { + return NULL; // no content + } + + if (content_length < 0) { + dav_remote_close_connection(r); + return dav_new_error(r->pool, HTTP_BAD_GATEWAY, 0, "Invalid Content-Length of response"); + } + + while (0 < content_length) { + response_brigade = apr_brigade_create(r->pool, bucket_alloc); + + do { + apr_status_t rc; + + rc = ap_get_brigade(r->input_filters, response_brigade, AP_MODE_READBYTES, + APR_BLOCK_READ, DAV_READ_BLOCKSIZE); + + if (rc != APR_SUCCESS) { + return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, + "Could not get next bucket brigade"); + } + + for (e = APR_BRIGADE_FIRST(response_brigade); + e != APR_BRIGADE_SENTINEL(response_brigade); + e = APR_BUCKET_NEXT(e)) + { + const char *data; + apr_size_t len; + + if (APR_BUCKET_IS_EOS(e)) { + seen_eos = 1; + break; + } + + if (APR_BUCKET_IS_METADATA(e)) { + continue; + } + + rc = apr_bucket_read(e, &data, &len, APR_BLOCK_READ); + content_length -= len; + + if (rc != APR_SUCCESS) { + printf("2\n"); + return dav_new_error(r->pool, HTTP_BAD_REQUEST, 0, + "An error occurred while reading " + "the request body."); + } + } + + apr_brigade_cleanup(response_brigade); + } while (!seen_eos && content_length > 0); + } + + return NULL; +} + +DAV_DECLARE(dav_error *) dav_remote_skip_head_response_content(request_rec *r, void *unused) +{ + // nothing to do + 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; + 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); + } + + 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); + } + + // MOVED PERMANENTLY - check with '/' at the end + if ((r->status == HTTP_MOVED_PERMANENTLY) && (resource_uri->path[strlen(resource_uri->path) - 1] != '/')) { + 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_uri->path = apr_pstrcat(r->pool, resource_uri->path, "/", NULL); + + return dav_remote_method_request(r, method, resource_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, dav_remote_skip_head_response_content, NULL))) { + return dav_push_error(r->pool, err->status, 0, "HEAD in dav_remote_resource_exists failed.", err); + } + + *exists = r->status != 404; + 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 == 200 || r->status == 202 || r->status == 204 || r->status == 404) + 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_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 (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 in dav_remote_resource_mkcol failed.", err); + } + + if (r->status != 201) + 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_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 ((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, "DELETE in dav_remote_resource_delete failed.", err); + } + + if (r->status != 201 && r->status != 204) + return dav_new_error(r->pool, r->status, 0, "Remote server does not accept resource"); + else + return NULL; +} + +struct dav_remote_copymove_copy_walk_ctx +{ + const char *base_uri; + request_rec *r; + apr_uri_t *dest_uri; + const char *dest; + int overwrite; +}; + +DAV_DECLARE(dav_error *) dav_remote_copymove_copy_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; + const char *new_resource_path; + apr_uri_t new_resource_uri; + dav_error *err; + + 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"); + } + + new_resource_path = apr_pstrcat(wres->pool, ctx->dest, &(wres->resource->uri[strlen(ctx->base_uri)]), NULL); + + // 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[strlen(ctx->base_uri)]), NULL); + + if (wres->resource->collection) { + if (NULL != (err = dav_remote_resource_mkcol(ctx->r, &new_resource_uri, new_resource_path, wres->resource, ctx->overwrite))) + return err; + } else { + if (NULL != (err = dav_remote_resource_put(ctx->r, &new_resource_uri, new_resource_path, wres->resource, ctx->overwrite))) + 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_error *err; + apr_uri_t dest_uri; + conn_rec *conn; + request_rec *rp; + int exists; + int deleted; + dav_walk_params walk_params = { 0 }; + dav_response *response; + + 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; + } + } + + struct dav_remote_copymove_copy_walk_ctx ctx; + ctx.base_uri = resource->uri; + ctx.r = rp; + ctx.dest_uri = &dest_uri; + ctx.dest = dest; + ctx.overwrite = overwrite; + + walk_params.walk_type = DAV_WALKTYPE_AUTH | DAV_WALKTYPE_NORMAL; + walk_params.func = dav_remote_copymove_copy_walk; + walk_params.walk_ctx = &ctx; + walk_params.pool = r->pool; + walk_params.root = resource; + + // 3. copy resources/collections + if (NULL != (err = (*resource->hooks->walk)(&walk_params, depth, &response))) { + return err; + } + + return NULL; +} + /* handle the COPY and MOVE methods */ static int dav_method_copymove(request_rec *r, int is_move) { @@ -2559,9 +3206,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 +3218,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 +3257,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 +3273,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 +3316,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 +3337,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 +3390,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 +3438,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 +3479,8 @@ } } + if (!remote) + { /* * Remember the initial state of the destination, so the lock system * can be notified as to how it changed. @@ -2894,13 +3557,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 +3597,7 @@ dav_log_err(r, err, APLOG_WARNING); } + if (!remote) { /* propagate any indirect locks at the target */ if (lockdb != NULL) { @@ -2953,10 +3616,27 @@ return dav_handle_err(r, err, NULL); } } + } + + if (remote) { + if (NULL != (err = dav_remote_copymove(r, resource, dest, overwrite, depth))) { + 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); + } + } /* return an appropriate response (HTTP_CREATED or HTTP_NO_CONTENT) */ + return dav_created(r, dest, "Destination", 0); + } + else { + /* 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 -urbB 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 -urbB 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; }