props.c

来自「linux subdivision ying gai ke yi le ba」· C语言 代码 · 共 1,153 行 · 第 1/3 页

C
1,153
字号
/*
 * props.c :  routines for fetching DAV properties
 *
 * ====================================================================
 * 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>
#include <apr_tables.h>
#include <apr_strings.h>
#define APR_WANT_STRFUNC
#include <apr_want.h>

#include <ne_socket.h>
#include <ne_basic.h>
#include <ne_props.h>
#include <ne_xml.h>

#include "svn_error.h"
#include "svn_delta.h"
#include "svn_ra.h"
#include "svn_path.h"
#include "svn_dav.h"
#include "svn_base64.h"
#include "svn_xml.h"
#include "svn_pools.h"

#include "svn_private_config.h"

#include "ra_dav.h"


/* some definitions of various properties that may be fetched */
const ne_propname svn_ra_dav__vcc_prop = {
  "DAV:", "version-controlled-configuration"
};
const ne_propname svn_ra_dav__checked_in_prop = {
  "DAV:", "checked-in"
};

/* when we begin a checkout, we fetch these from the "public" resources to
   steer us towards a Baseline Collection. we fetch the resourcetype to
   verify that we're accessing a collection. */
static const ne_propname starting_props[] =
{
  { "DAV:", "version-controlled-configuration" },
  { "DAV:", "resourcetype" },
  { SVN_DAV_PROP_NS_DAV, "baseline-relative-path" },
  { SVN_DAV_PROP_NS_DAV, "repository-uuid"},
  { NULL }
};

/* when speaking to a Baseline to reach the Baseline Collection, fetch these
   properties. */
static const ne_propname baseline_props[] =
{
  { "DAV:", "baseline-collection" },
  { "DAV:", "version-name" },
  { NULL }
};



/*** Propfind Implementation ***/

typedef struct {
  svn_ra_dav__xml_elmid id;
  const char *name;
  int is_property;      /* is it a property, or part of some structure? */
} elem_defn;


static const elem_defn elem_definitions[] =
{
  /*** NOTE: Make sure that every item in here is also represented in
       propfind_elements[] ***/

  /* DAV elements */
  { ELEM_multistatus, "DAV:multistatus", 0 },
  { ELEM_response, "DAV:response", 0 },
  { ELEM_href, "DAV:href", SVN_RA_DAV__XML_CDATA },
  { ELEM_propstat, "DAV:propstat", 0 },
  { ELEM_prop, "DAV:prop", 0 },
  { ELEM_status, "DAV:status", SVN_RA_DAV__XML_CDATA },
  { ELEM_baseline, "DAV:baseline", SVN_RA_DAV__XML_CDATA },
  { ELEM_collection, "DAV:collection", SVN_RA_DAV__XML_CDATA },
  { ELEM_resourcetype, "DAV:resourcetype", 0 },
  { ELEM_baseline_coll, SVN_RA_DAV__PROP_BASELINE_COLLECTION, 0 },
  { ELEM_checked_in, SVN_RA_DAV__PROP_CHECKED_IN, 0 },
  { ELEM_vcc, SVN_RA_DAV__PROP_VCC, 0 },
  { ELEM_version_name, SVN_RA_DAV__PROP_VERSION_NAME, 1 },
  { ELEM_get_content_length, SVN_RA_DAV__PROP_GETCONTENTLENGTH, 1 },
  { ELEM_creationdate, SVN_RA_DAV__PROP_CREATIONDATE, 1 },
  { ELEM_creator_displayname, SVN_RA_DAV__PROP_CREATOR_DISPLAYNAME, 1 },

  /* SVN elements */
  { ELEM_baseline_relpath, SVN_RA_DAV__PROP_BASELINE_RELPATH, 1 },
  { ELEM_md5_checksum, SVN_RA_DAV__PROP_MD5_CHECKSUM, 1 },
  { ELEM_repository_uuid, SVN_RA_DAV__PROP_REPOSITORY_UUID, 1 },
  { 0 }
};


static const svn_ra_dav__xml_elm_t propfind_elements[] = 
{
  /*** NOTE: Make sure that every item in here is also represented in
       elem_definitions[] ***/

  /* DAV elements */
  { "DAV:", "multistatus", ELEM_multistatus, 0 },
  { "DAV:", "response", ELEM_response, 0 },
  { "DAV:", "href", ELEM_href, SVN_RA_DAV__XML_CDATA },
  { "DAV:", "propstat", ELEM_propstat, 0 },
  { "DAV:", "prop", ELEM_prop, 0 },
  { "DAV:", "status", ELEM_status, SVN_RA_DAV__XML_CDATA },
  { "DAV:", "baseline", ELEM_baseline, SVN_RA_DAV__XML_CDATA },
  { "DAV:", "baseline-collection", ELEM_baseline_coll, SVN_RA_DAV__XML_CDATA },
  { "DAV:", "checked-in", ELEM_checked_in, 0 },
  { "DAV:", "collection", ELEM_collection, SVN_RA_DAV__XML_CDATA },
  { "DAV:", "resourcetype", ELEM_resourcetype, 0 },
  { "DAV:", "version-controlled-configuration", ELEM_vcc, 0 },
  { "DAV:", "version-name", ELEM_version_name, SVN_RA_DAV__XML_CDATA },
  { "DAV:", "getcontentlength", ELEM_get_content_length,
    SVN_RA_DAV__XML_CDATA },
  { "DAV:", "creationdate", ELEM_creationdate, SVN_RA_DAV__XML_CDATA },
  { "DAV:", "creator-displayname", ELEM_creator_displayname,
    SVN_RA_DAV__XML_CDATA },

  /* SVN elements */
  { SVN_DAV_PROP_NS_DAV, "baseline-relative-path", ELEM_baseline_relpath,
    SVN_RA_DAV__XML_CDATA },
  { SVN_DAV_PROP_NS_DAV, "md5-checksum", ELEM_md5_checksum,
    SVN_RA_DAV__XML_CDATA },
  { SVN_DAV_PROP_NS_DAV, "repository-uuid", ELEM_repository_uuid,
    SVN_RA_DAV__XML_CDATA },

  /* Unknowns */
  { "", "", ELEM_unknown, SVN_RA_DAV__XML_COLLECT },

  { NULL } 
};


typedef struct propfind_ctx_t
{
  apr_hash_t *props; /* const char *URL-PATH -> svn_ra_dav_resource_t */

  svn_ra_dav_resource_t *rsrc; /* the current resource. */
  const char *encoding; /* property encoding (or NULL) */
  int status; /* status for the current <propstat> (or 0 if unknown). */
  apr_hash_t *propbuffer; /* holds properties until their status is known. */
  svn_ra_dav__xml_elmid last_open_id; /* the id of the last opened tag. */
  ne_xml_parser *parser; /* xml parser handling the PROPSET request. */

  apr_pool_t *pool;

} propfind_ctx_t;


/* Look up an element definition ID.  May return NULL if the elem is
   not recognized. */
static const elem_defn *defn_from_id(svn_ra_dav__xml_elmid id)
{
  const elem_defn *defn;

  for (defn = elem_definitions; defn->name != NULL; ++defn)
    {
      if (id == defn->id)
        return defn;
    }

  return NULL;
}


/* Assign URL to RSRC.  Use POOL for any allocations. */
static void assign_rsrc_url(svn_ra_dav_resource_t *rsrc, 
                            const char *url,
                            apr_pool_t *pool)
{
  char *url_path;
  apr_size_t len;
  ne_uri parsed_url;

  /* Parse the PATH element out of the URL.
     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(url, &parsed_url);
  url_path = apr_pstrdup(pool, parsed_url.path);
  ne_uri_free(&parsed_url);

  /* Clean up trailing slashes from the URL. */
  len = strlen(url_path);
  if (len > 1 && url_path[len - 1] == '/')
    url_path[len - 1] = '\0';
  rsrc->url = url_path;
}


static int validate_element(void *userdata, 
                            svn_ra_dav__xml_elmid parent, 
                            svn_ra_dav__xml_elmid child)
{
  switch (parent)
    {
    case ELEM_root:
      if (child == ELEM_multistatus)
        return SVN_RA_DAV__XML_VALID;
      else
        return SVN_RA_DAV__XML_INVALID;

    case ELEM_multistatus:
      if (child == ELEM_response)
        return SVN_RA_DAV__XML_VALID;
      else
        return SVN_RA_DAV__XML_DECLINE;

    case ELEM_response:
      if ((child == ELEM_href) || (child == ELEM_propstat))
        return SVN_RA_DAV__XML_VALID;
      else
        return SVN_RA_DAV__XML_DECLINE;

    case ELEM_propstat:
      if ((child == ELEM_prop) || (child == ELEM_status))
        return SVN_RA_DAV__XML_VALID;
      else
        return SVN_RA_DAV__XML_DECLINE;

    case ELEM_prop:
      return SVN_RA_DAV__XML_VALID; /* handle all children of <prop> */
        
    case ELEM_baseline_coll:
    case ELEM_checked_in:
    case ELEM_vcc:
      if (child == ELEM_href)
        return SVN_RA_DAV__XML_VALID;
      else
        return SVN_RA_DAV__XML_DECLINE; /* not concerned with other types */
      
    case ELEM_resourcetype:
      if ((child == ELEM_collection) || (child == ELEM_baseline))
        return SVN_RA_DAV__XML_VALID;
      else
        return SVN_RA_DAV__XML_DECLINE; /* not concerned with other types
                                           (### now) */

    default:
      return SVN_RA_DAV__XML_DECLINE;
    }

  /* NOTREACHED */
}


static int start_element(void *userdata, 
                         const svn_ra_dav__xml_elm_t *elm, 
                         const char **atts)
{
  propfind_ctx_t *pc = userdata;

  switch (elm->id)
    {
    case ELEM_response:
      if (pc->rsrc)
        return SVN_RA_DAV__XML_INVALID;
      /* Create a new resource. */
      pc->rsrc = apr_pcalloc(pc->pool, sizeof(*(pc->rsrc)));
      pc->rsrc->pool = pc->pool;
      pc->rsrc->propset = apr_hash_make(pc->pool);
      pc->status = 0;
      break;

    case ELEM_propstat:
      pc->status = 0;
      break;

    case ELEM_href:
      /* Remember this <href>'s parent so that when we close this tag,
         we know to whom the URL assignment belongs.  Could be the
         resource itself, or one of the properties:
         ELEM_baseline_coll, ELEM_checked_in, ELEM_vcc: */
      pc->rsrc->href_parent = pc->last_open_id;
      break;

    case ELEM_collection:
      pc->rsrc->is_collection = 1;
      break;

    case ELEM_unknown:
      /* these are our user-visible properties, presumably. */
      pc->encoding = ne_xml_get_attr(pc->parser, atts, SVN_DAV_PROP_NS_DAV,
                                     "encoding");
      if (pc->encoding)
        pc->encoding = apr_pstrdup(pc->pool, pc->encoding);
      break;

    default:
      /* nothing to do for these */
      break;
    }

  /* Remember the last tag we opened. */
  pc->last_open_id = elm->id;
  return SVN_RA_DAV__XML_VALID;
}


static int end_element(void *userdata, 
                       const svn_ra_dav__xml_elm_t *elm,
                       const char *cdata)
{
  propfind_ctx_t *pc = userdata;
  svn_ra_dav_resource_t *rsrc = pc->rsrc;
  const char *name;
  const svn_string_t *value = NULL;
  const elem_defn *parent_defn;
  const elem_defn *defn;
  ne_status status;

  switch (elm->id)
    {
    case ELEM_response:
      /* Verify that we've received a URL for this resource. */
      if (!pc->rsrc->url)
        return SVN_RA_DAV__XML_INVALID;

      /* Store the resource in the top-level hash table. */
      apr_hash_set(pc->props, pc->rsrc->url, APR_HASH_KEY_STRING, pc->rsrc);
      pc->rsrc = NULL;
      return SVN_RA_DAV__XML_VALID;

    case ELEM_propstat:
      /* We're at the end of a set of properties.  Do the right thing
         status-wise. */
      if (pc->status)
        {
          /* We have a status.  Loop over the buffered properties, and
             if the status is a good one (200), copy them into the
             resources's property hash.  Regardless of the status,
             we'll be removing these from the temporary buffer as we
             go along. */
          apr_hash_index_t *hi = apr_hash_first(pc->pool, pc->propbuffer);
          for (; hi; hi = apr_hash_next(hi))
            {
              const void *key;
              apr_ssize_t klen;
              void *val;
              apr_hash_this(hi, &key, &klen, &val);
              if (pc->status == 200)
                apr_hash_set(rsrc->propset, key, klen, val);
              apr_hash_set(pc->propbuffer, key, klen, NULL);
            }
        }
      else if (! pc->status)
        {
          /* No status at all?  Bogosity. */
          return SVN_RA_DAV__XML_INVALID;
        }
      return SVN_RA_DAV__XML_VALID;

    case ELEM_status:
      /* Parse the <status> tag's CDATA for a status code. */
      if (ne_parse_statusline(cdata, &status))
        return SVN_RA_DAV__XML_INVALID;
      free(status.reason_phrase);
      pc->status = status.code;
      return SVN_RA_DAV__XML_VALID;

    case ELEM_href:
      /* Special handling for <href> that belongs to the <response> tag. */

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?