util.c
来自「linux subdivision ying gai ke yi le ba」· C语言 代码 · 共 792 行 · 第 1/2 页
C
792 行
/*
* util.c : utility functions for the RA/DAV library
*
* ====================================================================
* Copyright (c) 2000-2004 CollabNet. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://subversion.tigris.org/license-1.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
*
* This software consists of voluntary contributions made by many
* individuals. For exact contribution history, see the revision
* history and logs, available at http://subversion.tigris.org/.
* ====================================================================
*/
#include <apr_pools.h>
#define APR_WANT_STRFUNC
#include <apr_want.h>
#include <ne_socket.h>
#include <ne_uri.h>
#include <ne_compress.h>
#include "svn_string.h"
#include "svn_utf.h"
#include "svn_xml.h"
#include "svn_path.h"
#include "svn_config.h"
#include "svn_private_config.h"
#include "ra_dav.h"
typedef struct {
apr_pool_t *pool; /* pool on which this is alloc-d */
void *original_userdata; /* userdata for callbacks */
const svn_ra_dav__xml_elm_t *elements; /* old-style elements table */
svn_ra_dav__xml_validate_cb *validate_cb; /* old-style validate callback */
svn_ra_dav__xml_startelm_cb *startelm_cb; /* old-style startelm callback */
svn_ra_dav__xml_endelm_cb *endelm_cb; /* old-style endelm callback */
svn_stringbuf_t *cdata_accum; /* stringbuffer for CDATA */
} neon_shim_baton_t;
const svn_ra_dav__xml_elm_t *
svn_ra_dav__lookup_xml_elem(const svn_ra_dav__xml_elm_t *table,
const char *nspace,
const char *name)
{
/* placeholder for `unknown' element if it's present */
const svn_ra_dav__xml_elm_t *elem_unknown = NULL;
const svn_ra_dav__xml_elm_t *elem;
for(elem = table; elem->nspace; ++elem)
{
if (strcmp(elem->nspace, nspace) == 0
&& strcmp(elem->name, name) == 0)
return elem;
/* Use a single loop to save CPU cycles.
*
* Maybe this element is defined as `unknown'? */
if (elem->id == ELEM_unknown)
elem_unknown = elem;
}
/* ELEM_unknown position in the table or NULL */
return elem_unknown;
}
/** Fill in temporary structure for ELEM_unknown element.
*
* Call only for element ELEM_unknown! For Neon 0.23 API
* compatibility, we need to fill the XML element structure with real
* namespace and element name, as "old-style" handler used to get that
* from Neon parser. This is a hack, so don't expect it to be elegant.
* The @a elem_pointer is a reference to element pointer which is
* returned by svn_ra_dav__lookup_xml_elem, and supposedly points at
* en entry in the XML elements table supplied by an "old-style"
* handler. @a elem_unknown_temporary is a reference to XML element
* structure allocated on the stack. There's no reason to allocate it
* anywhere else because it's going to use @a nspace and @a name which
* are passed into the "new-style" handler by the Neon parser, so the
* structure pointed at by @a elem_unknown_temporary must die when the
* calling function completes. This function is designed to be called
* from "new-style" startelm and endelm callbacks. */
static void
handle_unknown(const svn_ra_dav__xml_elm_t **elem_pointer,
svn_ra_dav__xml_elm_t *elem_unknown_temporary,
const char *nspace, const char *name)
{
elem_unknown_temporary->nspace = nspace;
elem_unknown_temporary->name = name;
elem_unknown_temporary->id = (*elem_pointer)->id;
elem_unknown_temporary->flags = (*elem_pointer)->flags;
/* The pointer will use temporary record instead of a table record */
*elem_pointer = elem_unknown_temporary;
}
/** (Neon 0.24) Start element parsing.
*
* Calls "old-style" API callbacks validate_cb and startelm_cb to emulate
* Neon 0.23 parser. @a userdata is a @c neon_shim_baton_t instance.
* ---- ne_xml.h ----
* The startelm callback may return:
* <0 => abort the parse (NE_XML_ABORT)
* 0 => decline this element (NE_XML_DECLINE)
* >0 => accept this element; value is state for this element.
* The 'parent' integer is the state returned by the handler of the
* parent element. */
static int
shim_startelm(void *userdata, int parent_state, const char *nspace,
const char *name, const char **attrs)
{
neon_shim_baton_t *baton = userdata;
svn_ra_dav__xml_elm_t elem_unknown_temporary;
const svn_ra_dav__xml_elm_t *elem =
svn_ra_dav__lookup_xml_elem(baton->elements, nspace, name);
int rc;
if (!elem)
return NE_XML_DECLINE; /* Let Neon handle this */
/* TODO: explore an option of keeping element pointer in the baton
* to cut one loop in endelm */
/* 'parent' here actually means a parent element's id as opposed
* to 'parent' parameter passed to the startelm() function */
rc = baton->validate_cb(baton->original_userdata, parent_state, elem->id);
if (rc != SVN_RA_DAV__XML_VALID) {
return (rc == SVN_RA_DAV__XML_DECLINE) ? NE_XML_DECLINE : NE_XML_ABORT;
}
if (elem->id == ELEM_unknown)
handle_unknown(&elem, &elem_unknown_temporary, nspace, name);
rc = baton->startelm_cb(baton->original_userdata, elem, attrs);
if (rc != SVN_RA_DAV__XML_VALID) {
return (rc == SVN_RA_DAV__XML_DECLINE) ? NE_XML_DECLINE : NE_XML_ABORT;
}
if (baton->cdata_accum != NULL)
svn_stringbuf_setempty(baton->cdata_accum);
else
baton->cdata_accum = svn_stringbuf_create("", baton->pool);
/* @a parent in the pre-Neon 0.24 interface was a parent's element
* id but now it's the status returned by parent's startelm(), so we need to
* bridge this by returning this element's id as a status.
* We also need to ensure that element ids start with 1, because
* zero is `decline'. See ra_dav.h definition of ELEM_* values.
*/
return elem->id;
}
/** (Neon 0.24) Collect element's contents.
*
* Collects element's contents into @a userdata string buffer. @a userdata is a
* @c neon_shim_baton_t instance.
* May return non-zero to abort the parse. */
static int shim_cdata(void *userdata, int state, const char *cdata, size_t len)
{
const neon_shim_baton_t *baton = userdata;
svn_stringbuf_appendbytes(baton->cdata_accum, cdata, len);
return 0; /* no error */
}
/** (Neon 0.24) Finish parsing element.
*
* Calls "old-style" endelm_cb callback. @a userdata is a @c neon_shim_baton_t
* instance.
* May return non-zero to abort the parse. */
static int shim_endelm(void *userdata, int state, const char *nspace,
const char *name)
{
const neon_shim_baton_t *baton = userdata;
svn_ra_dav__xml_elm_t elem_unknown_temporary;
const svn_ra_dav__xml_elm_t *elem =
svn_ra_dav__lookup_xml_elem(baton->elements, nspace, name);
int rc;
if (!elem)
return -1; /* shouldn't be here if startelm didn't abort the parse */
if (elem->id == ELEM_unknown)
handle_unknown(&elem, &elem_unknown_temporary, nspace, name);
rc = baton->endelm_cb(baton->original_userdata,
elem,
baton->cdata_accum->data);
if (rc != SVN_RA_DAV__XML_VALID)
return -1; /* abort the parse */
return 0; /* no error */
}
/** Push an XML handler onto Neon's handler stack.
*
* Parser @a p uses a stack of handlers to process XML. The handler is
* composed of validation callback @a validate_cb, start-element
* callback @a startelm_cb, and end-element callback @a endelm_cb, which
* collectively handle elements supplied in an array @a elements. Parser
* passes given user baton @a userdata to all callbacks.
* This is a new function on top of ne_xml_push_handler, adds memory pool
* @a pool as the last parameter. This parameter is not used with Neon
* 0.23.9, but will be with Neon 0.24. When Neon 0.24 is used, ra_dav
* receives calls from the new interface and performs functions described
* above by itself, using @a elements and calling callbacks according to
* 0.23 interface.
*/
static void shim_xml_push_handler(ne_xml_parser *p,
const svn_ra_dav__xml_elm_t *elements,
svn_ra_dav__xml_validate_cb validate_cb,
svn_ra_dav__xml_startelm_cb startelm_cb,
svn_ra_dav__xml_endelm_cb endelm_cb,
void *userdata,
apr_pool_t *pool)
{
neon_shim_baton_t *baton = apr_pcalloc(pool, sizeof(neon_shim_baton_t));
baton->pool = pool;
baton->original_userdata = userdata;
baton->elements = elements;
baton->validate_cb = validate_cb;
baton->startelm_cb = startelm_cb;
baton->endelm_cb = endelm_cb;
baton->cdata_accum = NULL; /* don't create until startelm is called */
ne_xml_push_handler(p, shim_startelm, shim_cdata, shim_endelm, baton);
}
void svn_ra_dav__copy_href(svn_stringbuf_t *dst, const char *src)
{
ne_uri parsed_url;
/* parse the PATH element out of the URL and store it.
### do we want to verify the rest matches the current session?
Note: mod_dav does not (currently) use an absolute URL, but simply a
server-relative path (i.e. this uri_parse is effectively a no-op).
*/
(void) ne_uri_parse(src, &parsed_url);
svn_stringbuf_set(dst, parsed_url.path);
ne_uri_free(&parsed_url);
}
svn_error_t *svn_ra_dav__convert_error(ne_session *sess,
const char *context,
int retcode,
apr_pool_t *pool)
{
int errcode = SVN_ERR_RA_DAV_REQUEST_FAILED;
const char *msg;
const char *hostport;
/* Convert the return codes. */
switch (retcode)
{
case NE_AUTH:
errcode = SVN_ERR_RA_NOT_AUTHORIZED;
msg = _("authorization failed");
break;
case NE_CONNECT:
msg = _("could not connect to server");
break;
case NE_TIMEOUT:
msg = _("timed out waiting for server");
break;
default:
/* Get the error string from neon and convert to UTF-8. */
SVN_ERR (svn_utf_cstring_to_utf8 (&msg, ne_get_error (sess), pool));
break;
}
/* The hostname may contain non-ASCII characters, so convert it to UTF-8. */
SVN_ERR (svn_utf_cstring_to_utf8 (&hostport, ne_get_server_hostport(sess),
pool));
return svn_error_createf (errcode, NULL, "%s: %s (%s://%s)",
context, msg, ne_get_scheme(sess),
hostport);
}
/** Error parsing **/
/* Custom function of type ne_accept_response. */
static int ra_dav_error_accepter(void *userdata,
ne_request *req,
const ne_status *st)
{
/* Only accept the body-response if the HTTP status code is *not* 2XX. */
return (st->klass != 2);
}
static const svn_ra_dav__xml_elm_t error_elements[] =
{
{ "DAV:", "error", ELEM_error, 0 },
{ "svn:", "error", ELEM_svn_error, 0 },
{ "http://apache.org/dav/xmlns", "human-readable",
ELEM_human_readable, SVN_RA_DAV__XML_CDATA },
/* ### our validator doesn't yet recognize the rich, specific
<D:some-condition-failed/> objects as defined by DeltaV.*/
{ NULL }
};
static int validate_error_elements(void *userdata,
svn_ra_dav__xml_elmid parent,
svn_ra_dav__xml_elmid child)
{
switch (parent)
{
case ELEM_root:
if (child == ELEM_error)
return SVN_RA_DAV__XML_VALID;
else
return SVN_RA_DAV__XML_INVALID;
case ELEM_error:
if (child == ELEM_svn_error
|| child == ELEM_human_readable)
return SVN_RA_DAV__XML_VALID;
else
return SVN_RA_DAV__XML_DECLINE; /* ignore if something else
was in there */
default:
return SVN_RA_DAV__XML_DECLINE;
}
/* NOTREACHED */
}
static int start_err_element(void *userdata, const svn_ra_dav__xml_elm_t *elm,
const char **atts)
{
svn_error_t **err = userdata;
switch (elm->id)
{
case ELEM_svn_error:
{
/* allocate the svn_error_t. Hopefully the value will be
overwritten by the <human-readable> tag, or even someday by
a <D:failed-precondition/> tag. */
*err = svn_error_create(APR_EGENERAL, NULL,
"General svn error from server");
break;
}
case ELEM_human_readable:
{
/* get the errorcode attribute if present */
const char *errcode_str =
svn_xml_get_attr_value("errcode", /* ### make constant in
some mod_dav header? */
atts);
if (errcode_str && *err)
(*err)->apr_err = atoi(errcode_str);
break;
}
default:
break;
}
return SVN_RA_DAV__XML_VALID;
}
static int end_err_element(void *userdata, const svn_ra_dav__xml_elm_t *elm,
const char *cdata)
{
svn_error_t **err = userdata;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?