util.c revision f4c310fd2555c6faca1f980f00b161eadb089023
0237f43ab925775250e266e479d0a337ff374a4btakashi** Copyright (C) 1998-2000 Greg Stein. All Rights Reserved.
fd9abdda70912b99b24e3bf1a38f26fde908a74cnd** By using this file, you agree to the terms and conditions set forth in
fd9abdda70912b99b24e3bf1a38f26fde908a74cnd** the LICENSE.html file which can be found at the top level of the mod_dav
0237f43ab925775250e266e479d0a337ff374a4btakashi** distribution or at http://www.webdav.org/mod_dav/license-1.html.
0237f43ab925775250e266e479d0a337ff374a4btakashi** Contact information:
0237f43ab925775250e266e479d0a337ff374a4btakashi** Greg Stein, PO Box 760, Palo Alto, CA, 94302
96ad5d81ee4a2cc66a4ae19893efc8aa6d06fae7jailletc** gstein@lyra.org, http://www.webdav.org/mod_dav/
0237f43ab925775250e266e479d0a337ff374a4btakashi** DAV extension module for Apache 1.3.*
0237f43ab925775250e266e479d0a337ff374a4btakashi** - various utilities, repository-independent
0237f43ab925775250e266e479d0a337ff374a4btakashi** Written by Greg Stein, gstein@lyra.org, http://www.lyra.org/
0237f43ab925775250e266e479d0a337ff374a4btakashidav_error *dav_new_error(pool *p, int status, int error_id, const char *desc)
0237f43ab925775250e266e479d0a337ff374a4btakashi /* DBG3("dav_new_error: %d %d %s", status, error_id, desc ? desc : "(no desc)"); */
0237f43ab925775250e266e479d0a337ff374a4btakashidav_error *dav_push_error(pool *p, int status, int error_id, const char *desc,
e0872ce93f9e1cfe48431781fc43cde555e985c3trawickvoid dav_text_append(pool * p, dav_text_header *hdr, const char *text)
0237f43ab925775250e266e479d0a337ff374a4btakashi /* no text elements yet */
0237f43ab925775250e266e479d0a337ff374a4btakashi /* append to the last text element */
de5cbc413070dea83e2d897c4a00dc6a80291227trawickvoid dav_check_bufsize(pool * p, dav_buffer *pbuf, size_t extra_needed)
0237f43ab925775250e266e479d0a337ff374a4btakashi /* grow the buffer if necessary */
0237f43ab925775250e266e479d0a337ff374a4btakashi if (pbuf->cur_len + extra_needed > pbuf->alloc_len) {
e13b2a9a4ed05ff8a3532508693c5b44a381ffabtrawickvoid dav_set_bufsize(pool * p, dav_buffer *pbuf, size_t size)
de5cbc413070dea83e2d897c4a00dc6a80291227trawick /* NOTE: this does not retain prior contents */
de5cbc413070dea83e2d897c4a00dc6a80291227trawick /* NOTE: this function is used to init the first pointer, too, since
de5cbc413070dea83e2d897c4a00dc6a80291227trawick the PAD will be larger than alloc_len (0) for zeroed structures */
de5cbc413070dea83e2d897c4a00dc6a80291227trawick /* grow if we don't have enough for the requested size plus padding */
0237f43ab925775250e266e479d0a337ff374a4btakashi /* set the new length; min of MINSIZE */
0237f43ab925775250e266e479d0a337ff374a4btakashi/* initialize a buffer and copy the specified (null-term'd) string into it */
0237f43ab925775250e266e479d0a337ff374a4btakashivoid dav_buffer_init(pool *p, dav_buffer *pbuf, const char *str)
0237f43ab925775250e266e479d0a337ff374a4btakashi/* append a string to the end of the buffer, adjust length */
0237f43ab925775250e266e479d0a337ff374a4btakashivoid dav_buffer_append(pool *p, dav_buffer *pbuf, const char *str)
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar/* place a string on the end of the buffer, do NOT adjust length */
1f1b6bf13313fdd14a45e52e553d3ff28689b717coarvoid dav_buffer_place(pool *p, dav_buffer *pbuf, const char *str)
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar/* place some memory on the end of a buffer; do NOT adjust length */
1f1b6bf13313fdd14a45e52e553d3ff28689b717coarvoid dav_buffer_place_mem(pool *p, dav_buffer *pbuf, const void *mem,
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar/* ### this code can be used for 1.3.4 installations. use this function
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar * ### instead of ap_sub_req_method_uri()
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar * ### don't look at this code.
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar * ### it is a Crime Against All That is Right and Good
1f1b6bf13313fdd14a45e52e553d3ff28689b717coarstatic request_rec *gross_hack(const char *new_file, const request_rec * r)
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar request_rec *rnew = ap_sub_req_lookup_uri(new_file, r);
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar /* ### these aren't exported properly from the headers */
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar extern int ap_check_access(request_rec *); /* check access on non-auth basis */
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar extern int ap_check_user_id(request_rec *); /* obtain valid username from client auth */
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar extern int ap_check_auth(request_rec *); /* check (validated) user is authorized here */
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar /* re-run portions with a modified method */
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar#endif /* APACHE_RELEASE == 10304100 */
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar** dav_lookup_uri()
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar** Extension for ap_sub_req_lookup_uri() which can't handle absolute
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar** URIs properly.
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar** If NULL is returned, then an error occurred with parsing the URI or
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar** the URI does not match the current server.
1f1b6bf13313fdd14a45e52e553d3ff28689b717coardav_lookup_result dav_lookup_uri(const char *uri, request_rec * r)
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar const char *scheme;
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar unsigned short port = ntohs(r->connection->local_addr.sin_port);
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar const char *domain;
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar /* first thing to do is parse the URI into various components */
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar if (ap_parse_uri_components(r->pool, uri, &comp) != HTTP_OK) {
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar result.err.desc = "Invalid syntax in Destination URI.";
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar /* the URI must be an absoluteURI (WEBDAV S9.3) */
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar result.err.desc = "Destination URI must be an absolute URI.";
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar /* ### not sure this works if the current request came in via https: */
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar /* insert a port if the URI did not contain one */
a99c5d4cc3cab6a62b04d52000dbc22ce1fa2d94coar /* now, verify that the URI uses the same scheme as the current request.
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar the port, must match our port.
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar the URI must not have a query (args) or a fragment
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar "Destination URI refers to different "
a99c5d4cc3cab6a62b04d52000dbc22ce1fa2d94coar "scheme or port (%s://hostname:%d)\n"
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar "(want: %s://hostname:%d)",
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar "Destination URI contains invalid components "
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar "(a query or a fragment).";
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar /* we have verified the scheme, port, and general structure */
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar ** Hrm. IE5 will pass unqualified hostnames for both the
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar ** Host: and Destination: headers. This breaks the
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar ** http_vhost.c::matches_aliases function.
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar ** For now, qualify unqualified comp.hostnames with
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar ** r->server->server_hostname.
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar ** ### this is a big hack. Apache should provide a better way.
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar ** ### maybe the admin should list the unqualified hosts in a
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar ** ### <ServerAlias> block?
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar (domain = strchr(r->server->server_hostname, '.')) != NULL) {
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar comp.hostname = ap_pstrcat(r->pool, comp.hostname, domain, NULL);
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar /* now, if a hostname was provided, then verify that it represents the
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar same server as the current connection. note that we just use our
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar port, since we've verified the URI matches ours */
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar result.err.desc = "Destination URI refers to a different server.";
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar /* we have verified that the requested URI denotes the same server as
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar the current request. Therefore, we can use ap_sub_req_lookup_uri() */
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar /* reconstruct a URI as just the path */
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar new_file = ap_unparse_uri_components(r->pool, &comp, UNP_OMITSITEPART);
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar * Lookup the URI and return the sub-request. Note that we use the
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar * same HTTP method on the destination. This allows the destination
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar * to apply appropriate restrictions (e.g. readonly).
1f1b6bf13313fdd14a45e52e553d3ff28689b717coar result.rnew = ap_sub_req_method_uri(r->method, new_file, r);
0237f43ab925775250e266e479d0a337ff374a4btakashi/* ---------------------------------------------------------------
0237f43ab925775250e266e479d0a337ff374a4btakashi** XML UTILITY FUNCTIONS
0237f43ab925775250e266e479d0a337ff374a4btakashi/* validate that the root element uses a given DAV: tagname (TRUE==valid) */
0237f43ab925775250e266e479d0a337ff374a4btakashiint dav_validate_root(const dav_xml_doc *doc, const char *tagname)
0237f43ab925775250e266e479d0a337ff374a4btakashi/* find and return the (unique) child with a given DAV: tagname */
0237f43ab925775250e266e479d0a337ff374a4btakashidav_xml_elem *dav_find_child(const dav_xml_elem *elem, const char *tagname)
0237f43ab925775250e266e479d0a337ff374a4btakashi if (child->ns == DAV_NS_DAV_ID && !strcmp(child->name, tagname))
0237f43ab925775250e266e479d0a337ff374a4btakashi/* how many characters for the given integer? */
0237f43ab925775250e266e479d0a337ff374a4btakashi#define DAV_NS_LEN(ns) ((ns) < 10 ? 1 : (ns) < 100 ? 2 : (ns) < 1000 ? 3 : \
e13b2a9a4ed05ff8a3532508693c5b44a381ffabtrawick for (; t; t = t->next)
0237f43ab925775250e266e479d0a337ff374a4btakashistatic size_t dav_elem_size(const dav_xml_elem *elem, int style,
e13b2a9a4ed05ff8a3532508693c5b44a381ffabtrawick if (style == DAV_X2T_FULL || style == DAV_X2T_FULL_NS_LANG) {
0237f43ab925775250e266e479d0a337ff374a4btakashi ** The outer element will contain xmlns:ns%d="%s" attributes
0237f43ab925775250e266e479d0a337ff374a4btakashi ** and an xml:lang attribute, if applicable.
0237f43ab925775250e266e479d0a337ff374a4btakashi /* compute size of: ' xmlns:ns%d="%s"' */
0237f43ab925775250e266e479d0a337ff374a4btakashi /* compute size of: ' xml:lang="%s"' */
0237f43ab925775250e266e479d0a337ff374a4btakashi /* compute size of: <%s> */
0237f43ab925775250e266e479d0a337ff374a4btakashi /* compute size of: <ns%d:%s> */
0237f43ab925775250e266e479d0a337ff374a4btakashi size += 3 + DAV_NS_LEN(ns) + 1 + strlen(elem->name) + 1;
0237f43ab925775250e266e479d0a337ff374a4btakashi /* insert a closing "/" */
0237f43ab925775250e266e479d0a337ff374a4btakashi * two of above plus "/":
0237f43ab925775250e266e479d0a337ff374a4btakashi * <ns%d:%s> ... </ns%d:%s>
0237f43ab925775250e266e479d0a337ff374a4btakashi * OR <%s> ... </%s>
0237f43ab925775250e266e479d0a337ff374a4btakashi /* compute size of: ' %s="%s"' */
0237f43ab925775250e266e479d0a337ff374a4btakashi size += 1 + strlen(attr->name) + 2 + strlen(attr->value) + 1;
0237f43ab925775250e266e479d0a337ff374a4btakashi /* compute size of: ' ns%d:%s="%s"' */
0237f43ab925775250e266e479d0a337ff374a4btakashi size += 3 + DAV_NS_LEN(attr->ns) + 1 + strlen(attr->name) + 2 + strlen(attr->value) + 1;
0237f43ab925775250e266e479d0a337ff374a4btakashi ** If the element has an xml:lang value that is *different* from
de5cbc413070dea83e2d897c4a00dc6a80291227trawick ** its parent, then add the thing in: ' xml:lang="%s"'.
0237f43ab925775250e266e479d0a337ff374a4btakashi ** NOTE: we take advantage of the pointer equality established by
0237f43ab925775250e266e479d0a337ff374a4btakashi ** the parsing for "inheriting" the xml:lang values from parents.
0237f43ab925775250e266e479d0a337ff374a4btakashi (elem->parent == NULL || elem->lang != elem->parent->lang)) {
0237f43ab925775250e266e479d0a337ff374a4btakashi * This style prepends the xml:lang value plus a null terminator.
0237f43ab925775250e266e479d0a337ff374a4btakashi * If a lang value is not present, then we insert a null term.
0237f43ab925775250e266e479d0a337ff374a4btakashi for (elem = elem->first_child; elem; elem = elem->next) {
0237f43ab925775250e266e479d0a337ff374a4btakashi /* the size of the child element plus the CDATA that follows it */
0237f43ab925775250e266e479d0a337ff374a4btakashi size += (dav_elem_size(elem, DAV_X2T_FULL, NULL, ns_map) +
0237f43ab925775250e266e479d0a337ff374a4btakashistatic char *dav_write_text(char *s, const dav_text *t)
0237f43ab925775250e266e479d0a337ff374a4btakashi for (; t; t = t->next) {
0237f43ab925775250e266e479d0a337ff374a4btakashistatic char *dav_write_elem(char *s, const dav_xml_elem *elem, int style,
0237f43ab925775250e266e479d0a337ff374a4btakashi if (style == DAV_X2T_FULL || style == DAV_X2T_FULL_NS_LANG) {
0237f43ab925775250e266e479d0a337ff374a4btakashi len = sprintf(s, " %s=\"%s\"", attr->name, attr->value);
0237f43ab925775250e266e479d0a337ff374a4btakashi len = sprintf(s, " ns%d:%s=\"%s\"", attr->ns, attr->name, attr->value);
0237f43ab925775250e266e479d0a337ff374a4btakashi /* add the xml:lang value if necessary */
f7b27c374cf3e650d6abe8363f0d4ac49d96b6a8trawick /* add namespace definitions, if required */
0237f43ab925775250e266e479d0a337ff374a4btakashi /* no more to do. close it up and go. */
0237f43ab925775250e266e479d0a337ff374a4btakashi *s++ = '/';
0237f43ab925775250e266e479d0a337ff374a4btakashi *s++ = '>';
da90a03ebe5290cb064b088a0508ea8e53e46464trawick /* just close it */
da90a03ebe5290cb064b088a0508ea8e53e46464trawick *s++ = '>';
f7b27c374cf3e650d6abe8363f0d4ac49d96b6a8trawick /* prepend the xml:lang value */
a99c5d4cc3cab6a62b04d52000dbc22ce1fa2d94coar *s++ = '\0';
0237f43ab925775250e266e479d0a337ff374a4btakashi for (child = elem->first_child; child; child = child->next) {
0237f43ab925775250e266e479d0a337ff374a4btakashi s = dav_write_elem(s, child, DAV_X2T_FULL, NULL, ns_map);
0237f43ab925775250e266e479d0a337ff374a4btakashi s = dav_write_text(s, child->following_cdata.first);
0237f43ab925775250e266e479d0a337ff374a4btakashi if (style == DAV_X2T_FULL || style == DAV_X2T_FULL_NS_LANG) {
0237f43ab925775250e266e479d0a337ff374a4btakashi/* convert an element to a text string */
de5cbc413070dea83e2d897c4a00dc6a80291227trawick const char **pbuf,
0237f43ab925775250e266e479d0a337ff374a4btakashi /* get the exact size, plus a null terminator */
0237f43ab925775250e266e479d0a337ff374a4btakashi size_t size = dav_elem_size(elem, style, namespaces, ns_map) + 1;
0237f43ab925775250e266e479d0a337ff374a4btakashi (void) dav_write_elem(s, elem, style, namespaces, ns_map);
de5cbc413070dea83e2d897c4a00dc6a80291227trawickconst char *dav_empty_elem(pool * p, const dav_xml_elem *elem)
0237f43ab925775250e266e479d0a337ff374a4btakashi * The prefix (xml...) is already within the prop name, or
0237f43ab925775250e266e479d0a337ff374a4btakashi * the element simply has no prefix.
0237f43ab925775250e266e479d0a337ff374a4btakashi return ap_psprintf(p, "<%s/>" DEBUG_CR, elem->name);
0237f43ab925775250e266e479d0a337ff374a4btakashi return ap_psprintf(p, "<ns%d:%s/>" DEBUG_CR, elem->ns, elem->name);
0237f43ab925775250e266e479d0a337ff374a4btakashi** dav_quote_string: quote an XML string
0237f43ab925775250e266e479d0a337ff374a4btakashi** Replace '<', '>', and '&' with '<', '>', and '&'.
0237f43ab925775250e266e479d0a337ff374a4btakashi** If quotes is true, then replace '"' with '"'.
0237f43ab925775250e266e479d0a337ff374a4btakashi** quotes is typically set to true for XML strings that will occur within
0237f43ab925775250e266e479d0a337ff374a4btakashi** double quotes -- attribute values.
0237f43ab925775250e266e479d0a337ff374a4btakashiconst char * dav_quote_string(pool *p, const char *s, int quotes)
0237f43ab925775250e266e479d0a337ff374a4btakashi const char *scan;
0237f43ab925775250e266e479d0a337ff374a4btakashi for (scan = s; (c = *scan) != '\0'; ++scan, ++len) {
if (extra == 0)
*qscan++ = c;
return qstr;
return DAV_TIMEOUT_INFINITE;
return DAV_TIMEOUT_INFINITE;
return DAV_TIMEOUT_INFINITE;
return NULL;
return ih;
const char *state_token,
if (t == dav_if_opaquelock) {
return err;
return NULL;
char *sp;
char *token;
token++;
return NULL;
return token;
char *str;
char *list;
const char *state_token;
int condition;
return NULL;
while (*str) {
switch(*str) {
while (*list) {
switch (*list) {
return err;
return err;
list++;
str++;
return NULL;
int flags,
request_rec *r)
const char *uri;
const char *etag;
int num_matched;
int num_that_apply;
int seen_locktoken;
return dav_push_error(p,
err);
if (seen_locktoken)
return NULL;
return NULL;
num_that_apply = 0;
case dav_if_etag:
reason =
goto state_list_failed;
&& !mismatch) {
reason =
goto state_list_failed;
case dav_if_opaquelock:
reason =
goto state_list_failed;
num_matched = 0;
reason =
goto state_list_failed;
const char *errmsg;
if (num_matched == 0
reason =
goto state_list_failed;
if (seen_locktoken) {
return NULL;
if (num_that_apply == 0) {
if (seen_locktoken)
return NULL;
locks_hooks)) {
return NULL;
ap_psprintf(p,
return NULL;
return NULL;
return err;
return NULL;
int result;
int lock_db_opened_locally = 0;
#if DAV_DEBUG
return err;
return err;
ctx.r = r;
&work_buf, r);
return err;
return err;
return NULL;
int parent_only,
int *resource_existed,
int *resource_was_writable,
int *parent_was_writable)
const char *body;
int auto_version;
if (!parent_only) {
*resource_was_writable = 0;
*parent_was_writable = 0;
if (!parent_only)
return NULL;
if (!parent_only) {
return NULL;
return NULL;
int undo,
int resource_existed,
int parent_was_writable)
const char *body;
if (undo)
if (undo)
return NULL;
const char **pelt;