📄 util.c
字号:
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
** DAV extension module for Apache 2.0.*
** - various utilities, repository-independent
*/
#include "apr_strings.h"
#include "apr_lib.h"
#define APR_WANT_STRFUNC
#include "apr_want.h"
#include "mod_dav.h"
#include "http_request.h"
#include "http_config.h"
#include "http_vhost.h"
#include "http_log.h"
#include "http_protocol.h"
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));
/* DBG3("dav_new_error: %d %d %s", status, error_id, desc ? desc : "(no desc)"); */
err->status = status;
err->error_id = error_id;
err->desc = desc;
err->save_errno = save_errno;
return err;
}
DAV_DECLARE(dav_error*) dav_new_error_tag(apr_pool_t *p, int status,
int error_id, const char *desc,
const char *namespace,
const char *tagname)
{
dav_error *err = dav_new_error(p, status, error_id, desc);
err->tagname = tagname;
err->namespace = namespace;
return err;
}
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));
err->status = status;
err->error_id = error_id;
err->desc = desc;
err->prev = prev;
return err;
}
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) {
char *newbuf;
pbuf->alloc_len += extra_needed + DAV_BUFFER_PAD;
newbuf = apr_palloc(p, pbuf->alloc_len);
memcpy(newbuf, pbuf->buf, pbuf->cur_len);
pbuf->buf = newbuf;
}
}
DAV_DECLARE(void) dav_set_bufsize(apr_pool_t * p, dav_buffer *pbuf,
apr_size_t size)
{
/* NOTE: this does not retain prior contents */
/* NOTE: this function is used to init the first pointer, too, since
the PAD will be larger than alloc_len (0) for zeroed structures */
/* grow if we don't have enough for the requested size plus padding */
if (size + DAV_BUFFER_PAD > pbuf->alloc_len) {
/* set the new length; min of MINSIZE */
pbuf->alloc_len = size + DAV_BUFFER_PAD;
if (pbuf->alloc_len < DAV_BUFFER_MINSIZE)
pbuf->alloc_len = DAV_BUFFER_MINSIZE;
pbuf->buf = apr_palloc(p, pbuf->alloc_len);
}
pbuf->cur_len = size;
}
/* initialize a buffer and copy the specified (null-term'd) string into it */
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 */
DAV_DECLARE(void) dav_buffer_append(apr_pool_t *p, dav_buffer *pbuf,
const char *str)
{
apr_size_t len = strlen(str);
dav_check_bufsize(p, pbuf, len + 1);
memcpy(pbuf->buf + pbuf->cur_len, str, len + 1);
pbuf->cur_len += len;
}
/* place a string on the end of the buffer, do NOT adjust length */
DAV_DECLARE(void) dav_buffer_place(apr_pool_t *p, dav_buffer *pbuf,
const char *str)
{
apr_size_t len = strlen(str);
dav_check_bufsize(p, pbuf, len + 1);
memcpy(pbuf->buf + pbuf->cur_len, str, len + 1);
}
/* place some memory on the end of a buffer; do NOT adjust length */
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);
}
/*
** dav_lookup_uri()
**
** Extension for ap_sub_req_lookup_uri() which can't handle absolute
** URIs properly.
**
** If NULL is returned, then an error occurred with parsing the URI or
** the URI does not match the current server.
*/
DAV_DECLARE(dav_lookup_result) dav_lookup_uri(const char *uri,
request_rec * r,
int must_be_absolute)
{
dav_lookup_result result = { 0 };
const char *scheme;
apr_port_t port;
apr_uri_t comp;
char *new_file;
const char *domain;
/* first thing to do is parse the URI into various components */
if (apr_uri_parse(r->pool, uri, &comp) != APR_SUCCESS) {
result.err.status = HTTP_BAD_REQUEST;
result.err.desc = "Invalid syntax in Destination URI.";
return result;
}
/* the URI must be an absoluteURI (WEBDAV S9.3) */
if (comp.scheme == NULL && must_be_absolute) {
result.err.status = HTTP_BAD_REQUEST;
result.err.desc = "Destination URI must be an absolute URI.";
return result;
}
/* the URI must not have a query (args) or a fragment */
if (comp.query != NULL || comp.fragment != NULL) {
result.err.status = HTTP_BAD_REQUEST;
result.err.desc =
"Destination URI contains invalid components "
"(a query or a fragment).";
return result;
}
/* If the scheme or port was provided, then make sure that it matches
the scheme/port of this request. If the request must be absolute,
then require the (explicit/implicit) scheme/port be matching.
### hmm. if a port wasn't provided (does the parse return port==0?),
### but we're on a non-standard port, then we won't detect that the
### URI's port implies the wrong one.
*/
if (comp.scheme != NULL || comp.port != 0 || must_be_absolute)
{
/* ### not sure this works if the current request came in via https: */
scheme = r->parsed_uri.scheme;
if (scheme == NULL)
scheme = ap_http_method(r);
/* insert a port if the URI did not contain one */
if (comp.port == 0)
comp.port = apr_uri_port_of_scheme(comp.scheme);
/* now, verify that the URI uses the same scheme as the current.
request. the port must match our port.
*/
apr_sockaddr_port_get(&port, r->connection->local_addr);
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,
"Destination URI refers to "
"different scheme or port "
"(%s://hostname:%d)" APR_EOL_STR
"(want: %s://hostname:%d)",
comp.scheme ? comp.scheme : scheme,
comp.port ? comp.port : port,
scheme, port);
return result;
}
}
/* we have verified the scheme, port, and general structure */
/*
** Hrm. IE5 will pass unqualified hostnames for both the
** Host: and Destination: headers. This breaks the
** http_vhost.c::matches_aliases function.
**
** For now, qualify unqualified comp.hostnames with
** r->server->server_hostname.
**
** ### this is a big hack. Apache should provide a better way.
** ### maybe the admin should list the unqualified hosts in a
** ### <ServerAlias> block?
*/
if (comp.hostname != NULL
&& strrchr(comp.hostname, '.') == NULL
&& (domain = strchr(r->server->server_hostname, '.')) != NULL) {
comp.hostname = apr_pstrcat(r->pool, comp.hostname, domain, NULL);
}
/* now, if a hostname was provided, then verify that it represents the
same server as the current connection. note that we just use our
port, since we've verified the URI matches ours */
#ifdef APACHE_PORT_HANDLING_IS_BUSTED
if (comp.hostname != NULL &&
!ap_matches_request_vhost(r, comp.hostname, port)) {
result.err.status = HTTP_BAD_GATEWAY;
result.err.desc = "Destination URI refers to a different server.";
return result;
}
#endif
/* we have verified that the requested URI denotes the same server as
the current request. Therefore, we can use ap_sub_req_lookup_uri() */
/* reconstruct a URI as just the path */
new_file = apr_uri_unparse(r->pool, &comp, APR_URI_UNP_OMITSITEPART);
/*
* Lookup the URI and return the sub-request. Note that we use the
* 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, NULL);
return result;
}
/* ---------------------------------------------------------------
**
** XML UTILITY FUNCTIONS
*/
/* validate that the root element uses a given DAV: tagname (TRUE==valid) */
DAV_DECLARE(int) dav_validate_root(const apr_xml_doc *doc,
const char *tagname)
{
return doc->root &&
doc->root->ns == APR_XML_NS_DAV_ID &&
strcmp(doc->root->name, tagname) == 0;
}
/* find and return the (unique) child with a given DAV: tagname */
DAV_DECLARE(apr_xml_elem *) dav_find_child(const apr_xml_elem *elem,
const char *tagname)
{
apr_xml_elem *child = elem->first_child;
for (; child; child = child->next)
if (child->ns == APR_XML_NS_DAV_ID && !strcmp(child->name, tagname))
return child;
return NULL;
}
/* gather up all the CDATA into a single string */
DAV_DECLARE(const char *) dav_xml_get_cdata(const apr_xml_elem *elem, apr_pool_t *pool,
int strip_white)
{
apr_size_t len = 0;
apr_text *scan;
const apr_xml_elem *child;
char *cdata;
char *s;
apr_size_t tlen;
const char *found_text = NULL; /* initialize to avoid gcc warning */
int found_count = 0;
for (scan = elem->first_cdata.first; scan != NULL; scan = scan->next) {
found_text = scan->text;
++found_count;
len += strlen(found_text);
}
for (child = elem->first_child; child != NULL; child = child->next) {
for (scan = child->following_cdata.first;
scan != NULL;
scan = scan->next) {
found_text = scan->text;
++found_count;
len += strlen(found_text);
}
}
/* some fast-path cases:
* 1) zero-length cdata
* 2) a single piece of cdata with no whitespace to strip
*/
if (len == 0)
return "";
if (found_count == 1) {
if (!strip_white
|| (!apr_isspace(*found_text)
&& !apr_isspace(found_text[len - 1])))
return found_text;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -