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 + -
显示快捷键?