]> granicus.if.org Git - apache/blobdiff - modules/dav/main/util.c
Clean up some of the includes:
[apache] / modules / dav / main / util.c
index 4ca3b92a0066b09deedfc728949dd62301bf6294..22b1e2282e5b5c356c9187af6c2f7aebdb6d5695 100644 (file)
 **  - various utilities, repository-independent
 */
 
+#include "apr_strings.h"
+
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
+
 #include "mod_dav.h"
 
 #include "http_request.h"
@@ -65,9 +70,8 @@
 #include "http_log.h"
 #include "http_protocol.h"
 
-#include "apr_strings.h"
-
-dav_error *dav_new_error(apr_pool_t *p, int status, int error_id, const char *desc)
+DAV_DECLARE(dav_error*) dav_new_error(apr_pool_t *p, int status, 
+                                      int error_id, const char *desc)
 {
     int save_errno = errno;
     dav_error *err = apr_pcalloc(p, sizeof(*err));
@@ -82,8 +86,9 @@ dav_error *dav_new_error(apr_pool_t *p, int status, int error_id, const char *de
     return err;
 }
 
-dav_error *dav_push_error(apr_pool_t *p, int status, int error_id, const char *desc,
-                         dav_error *prev)
+DAV_DECLARE(dav_error*) dav_push_error(apr_pool_t *p, int status, 
+                                       int error_id, const char *desc, 
+                                       dav_error *prev)
 {
     dav_error *err = apr_pcalloc(p, sizeof(*err));
 
@@ -95,7 +100,8 @@ dav_error *dav_push_error(apr_pool_t *p, int status, int error_id, const char *d
     return err;
 }
 
-void dav_check_bufsize(apr_pool_t * p, dav_buffer *pbuf, size_t extra_needed)
+DAV_DECLARE(void) dav_check_bufsize(apr_pool_t * p, dav_buffer *pbuf, 
+                                    apr_size_t extra_needed)
 {
     /* grow the buffer if necessary */
     if (pbuf->cur_len + extra_needed > pbuf->alloc_len) {
@@ -108,7 +114,8 @@ void dav_check_bufsize(apr_pool_t * p, dav_buffer *pbuf, size_t extra_needed)
     }
 }
 
-void dav_set_bufsize(apr_pool_t * p, dav_buffer *pbuf, size_t size)
+DAV_DECLARE(void) dav_set_bufsize(apr_pool_t * p, dav_buffer *pbuf, 
+                                  apr_size_t size)
 {
     /* NOTE: this does not retain prior contents */
 
@@ -129,14 +136,16 @@ void dav_set_bufsize(apr_pool_t * p, dav_buffer *pbuf, size_t size)
 
 
 /* initialize a buffer and copy the specified (null-term'd) string into it */
-void dav_buffer_init(apr_pool_t *p, dav_buffer *pbuf, const char *str)
+DAV_DECLARE(void) dav_buffer_init(apr_pool_t *p, dav_buffer *pbuf, 
+                                  const char *str)
 {
     dav_set_bufsize(p, pbuf, strlen(str));
     memcpy(pbuf->buf, str, pbuf->cur_len + 1);
 }
 
 /* append a string to the end of the buffer, adjust length */
-void dav_buffer_append(apr_pool_t *p, dav_buffer *pbuf, const char *str)
+DAV_DECLARE(void) dav_buffer_append(apr_pool_t *p, dav_buffer *pbuf, 
+                                    const char *str)
 {
     size_t len = strlen(str);
 
@@ -146,7 +155,8 @@ void dav_buffer_append(apr_pool_t *p, dav_buffer *pbuf, const char *str)
 }
 
 /* place a string on the end of the buffer, do NOT adjust length */
-void dav_buffer_place(apr_pool_t *p, dav_buffer *pbuf, const char *str)
+DAV_DECLARE(void) dav_buffer_place(apr_pool_t *p, dav_buffer *pbuf, 
+                                   const char *str)
 {
     size_t len = strlen(str);
 
@@ -155,8 +165,9 @@ void dav_buffer_place(apr_pool_t *p, dav_buffer *pbuf, const char *str)
 }
 
 /* place some memory on the end of a buffer; do NOT adjust length */
-void dav_buffer_place_mem(apr_pool_t *p, dav_buffer *pbuf, const void *mem,
-                          size_t amt, size_t pad)
+DAV_DECLARE(void) dav_buffer_place_mem(apr_pool_t *p, dav_buffer *pbuf, 
+                                       const void *mem, apr_size_t amt, 
+                                       apr_size_t pad)
 {
     dav_check_bufsize(p, pbuf, amt + pad);
     memcpy(pbuf->buf + pbuf->cur_len, mem, amt);
@@ -175,7 +186,7 @@ dav_lookup_result dav_lookup_uri(const char *uri, request_rec * r)
 {
     dav_lookup_result result = { 0 };
     const char *scheme;
-    unsigned short port = ntohs(r->connection->local_addr.sin_port);
+    apr_port_t port;
     uri_components comp;
     char *new_file;
     const char *domain;
@@ -207,6 +218,7 @@ dav_lookup_result dav_lookup_uri(const char *uri, request_rec * r)
        the port, must match our port.
        the URI must not have a query (args) or a fragment
      */
+    apr_sockaddr_port_get(&port, r->connection->local_addr);
     if (strcasecmp(comp.scheme, scheme) != 0 ||
        comp.port != port) {
        result.err.status = HTTP_BAD_GATEWAY;
@@ -267,7 +279,7 @@ dav_lookup_result dav_lookup_uri(const char *uri, request_rec * r)
      * same HTTP method on the destination. This allows the destination
      * to apply appropriate restrictions (e.g. readonly).
      */
-    result.rnew = ap_sub_req_method_uri(r->method, new_file, r);
+    result.rnew = ap_sub_req_method_uri(r->method, new_file, r, NULL);
 
     return result;
 }
@@ -296,6 +308,61 @@ ap_xml_elem *dav_find_child(const ap_xml_elem *elem, const char *tagname)
     return NULL;
 }
 
+/* gather up all the CDATA into a single string */
+const char *dav_xml_get_cdata(const ap_xml_elem *elem, apr_pool_t *pool,
+                              int strip_white)
+{
+    apr_size_t len = 0;
+    ap_text *scan;
+    const ap_xml_elem *child;
+    char *cdata;
+    char *s;
+    apr_size_t tlen;
+
+    for (scan = elem->first_cdata.first; scan != NULL; scan = scan->next)
+        len += strlen(scan->text);
+
+    for (child = elem->first_child; child != NULL; child = child->next) {
+        for (scan = child->following_cdata.first;
+             scan != NULL;
+             scan = scan->next)
+            len += strlen(scan->text);
+    }
+
+    cdata = s = apr_palloc(pool, len + 1);
+
+    for (scan = elem->first_cdata.first; scan != NULL; scan = scan->next) {
+        tlen = strlen(scan->text);
+        memcpy(s, scan->text, tlen);
+        s += tlen;
+    }
+
+    for (child = elem->first_child; child != NULL; child = child->next) {
+        for (scan = child->following_cdata.first;
+             scan != NULL;
+             scan = scan->next) {
+            tlen = strlen(scan->text);
+            memcpy(s, scan->text, tlen);
+            s += tlen;
+        }
+    }
+
+    *s = '\0';
+
+    if (strip_white && len > 0) {
+        /* trim leading whitespace */
+        while (apr_isspace(*cdata))     /* assume: return false for '\0' */
+            ++cdata;
+
+        /* trim trailing whitespace */
+        while (len-- > 0 && apr_isspace(cdata[len]))
+            continue;
+        cdata[len + 1] = '\0';
+    }
+
+    return cdata;
+}
+
 /* ---------------------------------------------------------------
 **
 ** Timeout header processing
@@ -654,7 +721,7 @@ static dav_error * dav_validate_resource_state(apr_pool_t *p,
     int num_matched;
     int num_that_apply;
     int seen_locktoken;
-    size_t uri_len;
+    apr_size_t uri_len;
     const char *reason = NULL;
 
     /* DBG1("validate: <%s>", resource->uri); */
@@ -1176,12 +1243,13 @@ static dav_error * dav_validate_resource_state(apr_pool_t *p,
 }
 
 /* dav_validate_walker:  Walker callback function to validate resource state */
-static dav_error * dav_validate_walker(dav_walker_ctx *ctx, int calltype)
+static dav_error * dav_validate_walker(dav_walk_resource *wres, int calltype)
 {
+    dav_walker_ctx *ctx = wres->walk_ctx;
     dav_error *err;
 
-    if ((err = dav_validate_resource_state(ctx->pool, ctx->resource,
-                                          ctx->lockdb,
+    if ((err = dav_validate_resource_state(ctx->w.pool, wres->resource,
+                                          ctx->w.lockdb,
                                           ctx->if_header, ctx->flags,
                                           &ctx->work_buf, ctx->r)) == NULL) {
        /* There was no error, so just bug out. */
@@ -1193,14 +1261,14 @@ static dav_error * dav_validate_walker(dav_walker_ctx *ctx, int calltype)
     ** then just return error (not a multistatus).
     */
     if (ap_is_HTTP_SERVER_ERROR(err->status)
-        || (*ctx->resource->hooks->is_same_resource)(ctx->resource,
-                                                     ctx->root)) {
+        || (*wres->resource->hooks->is_same_resource)(wres->resource,
+                                                      ctx->w.root)) {
        /* ### maybe push a higher-level description? */
        return err;
     }
 
     /* associate the error with the current URI */
-    dav_add_response(ctx, ctx->uri.buf, err->status, NULL);
+    dav_add_response(wres, err->status, NULL);
 
     return NULL;
 }
@@ -1304,27 +1372,27 @@ dav_error * dav_validate_request(request_rec *r, dav_resource *resource,
 
     /* (1) Validate the specified resource, at the specified depth */
     if (resource->exists && depth > 0) {
-       dav_walker_ctx ctx = { 0 };
+        dav_walker_ctx ctx = { { 0 } };
+        dav_response *multi_status;
+
+       ctx.w.walk_type = DAV_WALKTYPE_NORMAL;
+       ctx.w.func = dav_validate_walker;
+        ctx.w.walk_ctx = &ctx;
+       ctx.w.pool = r->pool;
+        ctx.w.root = resource;
 
-       ctx.walk_type = DAV_WALKTYPE_ALL;
-       ctx.postfix = 0;
-       ctx.func = dav_validate_walker;
-       ctx.pool = r->pool;
        ctx.if_header = if_header;
        ctx.r = r;
         ctx.flags = flags;
-        ctx.resource = resource;
 
        if (lockdb != NULL) {
-           ctx.lockdb = lockdb;
-           ctx.walk_type |= DAV_WALKTYPE_LOCKNULL;
+           ctx.w.lockdb = lockdb;
+           ctx.w.walk_type |= DAV_WALKTYPE_LOCKNULL;
        }
 
-       dav_buffer_init(r->pool, &ctx.uri, resource->uri);
-
-       err = (*repos_hooks->walk)(&ctx, DAV_INFINITY);
+       err = (*repos_hooks->walk)(&ctx.w, DAV_INFINITY, &multi_status);
        if (err == NULL) {
-            *response = ctx.response;
+            *response = multi_status;;
        }
         /* else: implies a 5xx status code occurred. */
     }
@@ -1335,13 +1403,15 @@ dav_error * dav_validate_request(request_rec *r, dav_resource *resource,
 
     /* (2) Validate the parent resource if requested */
     if (err == NULL && (flags & DAV_VALIDATE_PARENT)) {
-        dav_resource *parent_resource = (*repos_hooks->get_parent_resource)(resource);
+        dav_resource *parent_resource;
+
+        err = (*repos_hooks->get_parent_resource)(resource, &parent_resource);
 
-       if (parent_resource == NULL) {
+       if (err == NULL && parent_resource == NULL) {
            err = dav_new_error(r->pool, HTTP_FORBIDDEN, 0,
                                "Cannot access parent of repository root.");
        }
-       else {
+       else if (err == NULL) {
            err = dav_validate_resource_state(r->pool, parent_resource, lockdb,
                                              if_header,
                                               flags | DAV_VALIDATE_IS_PARENT,
@@ -1480,6 +1550,8 @@ dav_error * dav_get_locktoken_list(request_rec *r, dav_locktoken_list **ltl)
     return NULL;
 }
 
+#if 0 /* not needed right now... */
+
 static const char *strip_white(const char *s, apr_pool_t *pool)
 {
     apr_size_t idx;
@@ -1501,79 +1573,76 @@ static const char *strip_white(const char *s, apr_pool_t *pool)
 
     return s;
 }
+#endif
 
-/* see mod_dav.h for docco */
-const char *dav_get_target_selector(request_rec *r, const ap_xml_elem *version)
+#define DAV_LABEL_HDR "Label"
+
+/* dav_add_vary_header
+ *
+ * If there were any headers in the request which require a Vary header
+ * in the response, add it.
+ */
+void dav_add_vary_header(request_rec *in_req,
+                        request_rec *out_req,
+                        const dav_resource *resource)
 {
-    if (version != NULL) {
-        /* DAV:version contains a DAV:href element. find it. */
-        if ((version = dav_find_child(version, "href")) == NULL) {
-            /* ### this should generate an error... fallthru for now */
-        }
-        else {
-            /* return the contents of the DAV:href element */
-            /* ### this presumes no child elements */
-            return strip_white(version->first_cdata.first->text, r->pool);
-        }
+    const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(in_req);
+
+    /* ### this is probably all wrong... I think there is a function in
+       ### the Apache API to add things to the Vary header. need to check */
+
+    /* Only versioning headers require a Vary response header,
+     * so only do this check if there is a versioning provider */
+    if (vsn_hooks != NULL) {
+       const char *target = apr_table_get(in_req->headers_in, DAV_LABEL_HDR);
+       const char *vary = apr_table_get(out_req->headers_out, "Vary");
+
+        /* If Target-Selector specified, add it to the Vary header */
+       if (target != NULL) {
+           if (vary == NULL)
+               vary = DAV_LABEL_HDR;
+           else
+               vary = apr_pstrcat(out_req->pool, vary, "," DAV_LABEL_HDR,
+                                   NULL);
+
+           apr_table_setn(out_req->headers_out, "Vary", vary);
+       }
     }
-
-    /* no element. see if a Target-Selector header was provided. */
-    return apr_table_get(r->headers_in, "Target-Selector");
 }
 
 /* see mod_dav.h for docco */
 dav_error *dav_ensure_resource_writable(request_rec *r,
-                                         dav_resource *resource,
-                                          int parent_only,
-                                         dav_resource **parent_resource,
-                                         int *resource_existed,
-                                         int *resource_was_writable,
-                                         int *parent_was_writable)
+                                       dav_resource *resource,
+                                        int parent_only,
+                                        dav_auto_version_info *av_info)
 {
     const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
-    dav_resource *parent = NULL;
     const char *body;
-    int auto_version;
     dav_error *err;
-    const char *location;
-
-    if (parent_resource != NULL)
-        *parent_resource = NULL;
-
-    if (!parent_only) {
-        *resource_existed = resource->exists;
-        *resource_was_writable = 0;
-    }
-
-    if (parent_was_writable != NULL)
-        *parent_was_writable = 0;
 
-    /* if a Target-Selector header is present, then the client knows about
-     * versioning, so it should not be relying on implicit versioning
-     */
-    auto_version = (dav_get_target_selector(r, NULL) == NULL);
+    /* Initialize results */
+    memset(av_info, 0, sizeof(*av_info));
 
     /* check parent resource if requested or if resource must be created */
     if (!resource->exists || parent_only) {
-       parent = (*resource->hooks->get_parent_resource)(resource);
+       dav_resource *parent;
+
+        if ((err = (*resource->hooks->get_parent_resource)(resource,
+                                                           &parent)) != NULL)
+            return err;
+
         if (parent == NULL || !parent->exists) {
            body = apr_psprintf(r->pool,
-                              "Missing one or more intermediate collections. "
-                              "Cannot create resource %s.",
-                              ap_escape_html(r->pool, resource->uri));
+                                "Missing one or more intermediate collections. "
+                                "Cannot create resource %s.",
+                                ap_escape_html(r->pool, resource->uri));
            return dav_new_error(r->pool, HTTP_CONFLICT, 0, body);
         }
 
-        if (parent_resource != NULL)
-           *parent_resource = parent;
+        av_info->parent_resource = parent;
 
        /* if parent not versioned, assume child can be created */
        if (!parent->versioned) {
-            if (!parent_only)
-               *resource_was_writable = 1;
-
-            if (parent_was_writable != NULL)
-               *parent_was_writable = 1;
            return NULL;
        }
 
@@ -1585,118 +1654,134 @@ dav_error *dav_ensure_resource_writable(request_rec *r,
                                 "provider?");
        }
 
-        /* remember whether parent was already writable */
-        if (parent_was_writable != NULL)
-           *parent_was_writable = parent->working;
-
        /* parent must be checked out */
        if (!parent->working) {
-           if ((err = (*vsn_hooks->checkout)(parent, &location)) != NULL) {
+            /* if parent cannot be automatically checked out, fail */
+            if (!(*vsn_hooks->auto_version_enabled)(parent)) {
+               body = apr_psprintf(r->pool,
+                                    "Parent collection must be checked out. "
+                                    "Cannot create resource %s.",
+                                    ap_escape_html(r->pool, resource->uri));
+               return dav_new_error(r->pool, HTTP_CONFLICT, 0, body);
+            }
+
+            /* Try to checkout the parent collection.
+             * Note that auto-versioning can only be applied to a version selector,
+             * so no separate working resource will be created.
+             */
+           if ((err = (*vsn_hooks->checkout)(parent, 0, 0, 0, NULL, NULL))
+                != NULL)
+            {
                body = apr_psprintf(r->pool,
-                                  "Unable to checkout parent collection. "
-                                  "Cannot create resource %s.",
-                                  ap_escape_html(r->pool, resource->uri));
+                                    "Unable to checkout parent collection. "
+                                    "Cannot create resource %s.",
+                                    ap_escape_html(r->pool, resource->uri));
                return dav_push_error(r->pool, HTTP_CONFLICT, 0, body, err);
            }
 
-            /* ### what to do with the location? */
+            /* remember that parent was checked out */
+            av_info->parent_checkedout = 1;
        }
 
        /* if not just checking parent, create new child resource */
         if (!parent_only) {
-           if ((err = (*vsn_hooks->mkresource)(resource)) != NULL) {
+           if ((err = (*vsn_hooks->vsn_control)(resource, NULL)) != NULL) {
                body = apr_psprintf(r->pool,
-                                  "Unable to create versioned resource %s.",
-                                  ap_escape_html(r->pool, resource->uri));
+                                    "Unable to create versioned resource %s.",
+                                    ap_escape_html(r->pool, resource->uri));
                return dav_push_error(r->pool, HTTP_CONFLICT, 0, body, err);
            }
+
+            /* remember that resource was created */
+            av_info->resource_created = 1;
         }
     }
-    else {
-       /* resource exists: if not versioned, then assume it is writable */
-       if (!resource->versioned) {
-           *resource_was_writable = 1;
-           return NULL;
-       }
-
-       *resource_was_writable = resource->working;
+    else if (!resource->versioned) {
+       /* resource exists and is not versioned; assume it is writable */
+       return NULL;
     }
 
     /* if not just checking parent, make sure child resource is checked out */
     if (!parent_only && !resource->working) {
-       if ((err = (*vsn_hooks->checkout)(resource, &location)) != NULL) {
+        /* Auto-versioning can only be applied to version selectors, so
+         * no separate working resource will be created. */
+       if ((err = (*vsn_hooks->checkout)(resource, 0, 0, 0, NULL, NULL))
+            != NULL)
+        {
            body = apr_psprintf(r->pool,
-                              "Unable to checkout resource %s.",
-                              ap_escape_html(r->pool, resource->uri));
+                                "Unable to checkout resource %s.",
+                                ap_escape_html(r->pool, resource->uri));
            return dav_push_error(r->pool, HTTP_CONFLICT, 0, body, err);
        }
 
-        /* ### what to do with the location? */
+        /* remember that resource was checked out */
+        av_info->resource_checkedout = 1;
     }
 
     return NULL;
 }
 
 /* see mod_dav.h for docco */
-dav_error *dav_revert_resource_writability(request_rec *r,
-                                          dav_resource *resource,
-                                          dav_resource *parent_resource,
-                                          int undo,
-                                          int resource_existed,
-                                          int resource_was_writable,
-                                          int parent_was_writable)
+dav_error *dav_revert_resource_writability(
+    request_rec *r,
+    dav_resource *resource,
+    int undo,
+    const dav_auto_version_info *av_info)
 {
     const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
     const char *body;
     dav_error *err;
 
+    /* If a resource was provided, restore its writable state.
+     * Otherwise, only the parent must have been modified */
     if (resource != NULL) {
-        if (!resource_was_writable
-           && resource->versioned && resource->working) {
+        if (av_info->resource_checkedout) {
 
             if (undo)
                 err = (*vsn_hooks->uncheckout)(resource);
             else
-                err = (*vsn_hooks->checkin)(resource);
+                err = (*vsn_hooks->checkin)(resource, NULL);
 
             if (err != NULL) {
                body = apr_psprintf(r->pool,
-                                  "Unable to %s resource %s.",
-                                   undo ? "uncheckout" : "checkin",
-                                  ap_escape_html(r->pool, resource->uri));
+                                    "Unable to %s resource %s.",
+                                    undo ? "uncheckout" : "checkin",
+                                    ap_escape_html(r->pool, resource->uri));
                 return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
                                      body, err);
             }
         }
 
-        if (undo && !resource_existed && resource->exists) {
+        /* If undoing because of an error, and the resource was created,
+         * then remove it */
+        if (undo && av_info->resource_created) {
            dav_response *response;
 
            /* ### should we do anything with the response? */
             if ((err = (*resource->hooks->remove_resource)(resource,
                                                           &response)) != NULL) {
                body = apr_psprintf(r->pool,
-                                  "Unable to undo creation of resource %s.",
-                                  ap_escape_html(r->pool, resource->uri));
+                                    "Unable to undo creation of resource %s.",
+                                    ap_escape_html(r->pool, resource->uri));
                 return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
                                      body, err);
             }
         }
     }
 
-    if (parent_resource != NULL && !parent_was_writable
-       && parent_resource->versioned && parent_resource->working) {
+    /* If parent resource was made writable, restore its state */
+    if (av_info->parent_resource != NULL && av_info->parent_checkedout) {
 
        if (undo)
-           err = (*vsn_hooks->uncheckout)(parent_resource);
+           err = (*vsn_hooks->uncheckout)(av_info->parent_resource);
        else
-           err = (*vsn_hooks->checkin)(parent_resource);
+           err = (*vsn_hooks->checkin)(av_info->parent_resource, NULL);
 
        if (err != NULL) {
            body = apr_psprintf(r->pool,
-                              "Unable to %s parent collection of %s.",
-                              undo ? "uncheckout" : "checkin",
-                              ap_escape_html(r->pool, resource->uri));
+                                "Unable to %s parent collection %s.",
+                                undo ? "uncheckout" : "checkin",
+                                ap_escape_html(r->pool, av_info->parent_resource->uri));
            return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
                                  body, err);
        }