httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From gst...@apache.org
Subject cvs commit: httpd-2.0/modules/dav/main mod_dav.h props.c
Date Tue, 18 Sep 2001 04:09:20 GMT
gstein      01/09/17 21:09:20

  Modified:    modules/dav/fs dbm.c
               modules/dav/main mod_dav.h props.c
  Log:
  Revamp the API that mod_dav uses to talk to back end deadprop (propdb)
  providers. The old API was really based on how the FS stored properties, but
  sucked for other types of providers (SQL databases, Subversion, etc). This
  new code is overall much cleaner as it moves from a DBM style API to one
  tuned for actual mod_dav operation; it also more flexible/clearer for future
  improvements (e.g. dropping props directly to the wire rather than buffering
  in memory).
  
  This new API allows the provider to better define namespace handling during
  the output of the values, how values are serialized and stored, the mapping
  between elements' namespaces and the internalized namespace storage, a
  clearer mechanism for naming properties (dav_prop_name), and an explicit
  rollback mechanism to deal with PROPPATCH atomicity.
  
  Updated the FS provider (fs/dbm.c) to the new API, mostly by moving code
  from main/props.c. Of course, with the new semantics, some big changes in
  the namespace mapping were made.
  
  Revision  Changes    Path
  1.19      +537 -9    httpd-2.0/modules/dav/fs/dbm.c
  
  Index: dbm.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/dav/fs/dbm.c,v
  retrieving revision 1.18
  retrieving revision 1.19
  diff -u -r1.18 -r1.19
  --- dbm.c	2001/09/18 03:55:41	1.18
  +++ dbm.c	2001/09/18 04:09:19	1.19
  @@ -79,8 +79,28 @@
   struct dav_db {
       apr_pool_t *pool;
       apr_dbm_t *file;
  +
  +    /* when used as a property database: */
  +
  +    int version;		/* *minor* version of this db */
  +
  +    dav_buffer ns_table;	/* table of namespace URIs */
  +    short ns_count;		/* number of entries in table */
  +    int ns_table_dirty;		/* ns_table was modified */
  +    apr_hash_t *uri_index;      /* map URIs to (1-based) table indices */
  +
  +    dav_buffer wb_key;		/* work buffer for dav_gdbm_key */
  +
  +    apr_datum_t iter;           /* iteration key */
   };
   
  +/* -------------------------------------------------------------------------
  + *
  + * GENERIC DBM ACCESS
  + *
  + * For the most part, this just uses the APR DBM functions. They are wrapped
  + * a bit with some error handling (using the mod_dav error functions).
  + */
   
   void dav_dbm_get_statefiles(apr_pool_t *p, const char *fname,
   			    const char **state1, const char **state2)
  @@ -246,15 +266,523 @@
       apr_dbm_freedatum(db->file, data);
   }
   
  +/* -------------------------------------------------------------------------
  + *
  + * PROPERTY DATABASE FUNCTIONS
  + */
  +
  +
  +#define DAV_GDBM_NS_KEY		"METADATA"
  +#define DAV_GDBM_NS_KEY_LEN	8
  +
  +typedef struct {
  +    unsigned char major;
  +#define DAV_DBVSN_MAJOR		4
  +    /*
  +    ** V4 -- 0.9.9 ..
  +    **       Prior versions could have keys or values with invalid
  +    **       namespace prefixes as a result of the xmlns="" form not
  +    **       resetting the default namespace to be "no namespace". The
  +    **       namespace would be set to "" which is invalid; it should
  +    **       be set to "no namespace".
  +    **
  +    ** V3 -- 0.9.8
  +    **       Prior versions could have values with invalid namespace
  +    **       prefixes due to an incorrect mapping of input to propdb
  +    **       namespace indices. Version bumped to obsolete the old
  +    **       values.
  +    **
  +    ** V2 -- 0.9.7
  +    **       This introduced the xml:lang value into the property value's
  +    **       record in the propdb.
  +    **
  +    ** V1 -- .. 0.9.6
  +    **       Initial version.
  +    */
  +
  +
  +    unsigned char minor;
  +#define DAV_DBVSN_MINOR		0
  +
  +    short ns_count;
  +
  +} dav_propdb_metadata;
  +
  +struct dav_deadprop_rollback {
  +    apr_datum_t key;
  +    apr_datum_t value;
  +};
  +
  +struct dav_namespace_map {
  +    int *ns_map;
  +};
  +
  +/*
  +** Internal function to build a key
  +**
  +** WARNING: returns a pointer to a "static" buffer holding the key. The
  +**          value must be copied or no longer used if this function is
  +**          called again.
  +*/
  +static apr_datum_t dav_build_key(dav_db *db, const dav_prop_name *name)
  +{
  +    char nsbuf[20];
  +    size_t l_ns;
  +    size_t l_name = strlen(name->name);
  +    apr_datum_t key = { 0 };
  +
  +    /*
  +     * Convert namespace ID to a string. "no namespace" is an empty string,
  +     * so the keys will have the form ":name". Otherwise, the keys will
  +     * have the form "#:name".
  +     */
  +    if (*name->ns == '\0') {
  +	nsbuf[0] = '\0';
  +	l_ns = 0;
  +    }
  +    else {
  +        int ns_id = (int)apr_hash_get(db->uri_index, name->ns,
  +                                      APR_HASH_KEY_STRING);
  +
  +
  +        if (ns_id == 0) {
  +            /* the namespace was not found(!) */
  +            return key;         /* zeroed */
  +        }
  +
  +        l_ns = sprintf(nsbuf, "%d", ns_id - 1);
  +    }
  +
  +    /* assemble: #:name */
  +    dav_set_bufsize(db->pool, &db->wb_key, l_ns + 1 + l_name + 1);
  +    memcpy(db->wb_key.buf, nsbuf, l_ns);
  +    db->wb_key.buf[l_ns] = ':';
  +    memcpy(&db->wb_key.buf[l_ns + 1], name->name, l_name + 1);
  +
  +    /* build the database key */
  +    key.dsize = l_ns + 1 + l_name + 1;
  +    key.dptr = db->wb_key.buf;
  +
  +    return key;
  +}
  +
  +static void dav_append_prop(apr_pool_t *pool,
  +			    const char *name, const char *value,
  +			    apr_text_header *phdr)
  +{
  +    const char *s;
  +    const char *lang = value;
  +
  +    /* skip past the xml:lang value */
  +    value += strlen(lang) + 1;
  +
  +    if (*value == '\0') {
  +	/* the property is an empty value */
  +	if (*name == ':') {
  +	    /* "no namespace" case */
  +	    s = apr_psprintf(pool, "<%s/>" DEBUG_CR, name+1);
  +	}
  +	else {
  +	    s = apr_psprintf(pool, "<ns%s/>" DEBUG_CR, name);
  +	}
  +    }
  +    else if (*lang != '\0') {
  +	if (*name == ':') {
  +	    /* "no namespace" case */
  +	    s = apr_psprintf(pool, "<%s xml:lang=\"%s\">%s</%s>" DEBUG_CR,
  +			    name+1, lang, value, name+1);
  +	}
  +	else {
  +	    s = apr_psprintf(pool, "<ns%s xml:lang=\"%s\">%s</ns%s>" DEBUG_CR,
  +			    name, lang, value, name);
  +	}
  +    }
  +    else if (*name == ':') {
  +	/* "no namespace" case */
  +	s = apr_psprintf(pool, "<%s>%s</%s>" DEBUG_CR, name+1, value, name+1);
  +    }
  +    else {
  +	s = apr_psprintf(pool, "<ns%s>%s</ns%s>" DEBUG_CR, name, value, name);
  +    }
  +
  +    apr_text_append(pool, phdr, s);
  +}
  +
  +static dav_error * dav_propdb_open(apr_pool_t *pool,
  +                                   const dav_resource *resource, int ro,
  +                                   dav_db **pdb)
  +{
  +    dav_db *db;
  +    dav_error *err;
  +    apr_datum_t key;
  +    apr_datum_t value = { 0 };
  +
  +    *pdb = NULL;
  +
  +    /*
  +    ** Return if an error occurred, or there is no database.
  +    **
  +    ** NOTE: db could be NULL if we attempted to open a readonly
  +    **       database that doesn't exist. If we require read/write
  +    **       access, then a database was created and opened.
  +    */
  +    if ((err = dav_dbm_open(pool, resource, ro, &db)) != NULL
  +        || db == NULL)
  +        return err;
  +
  +    db->uri_index = apr_hash_make(pool);
  +
  +    key.dptr = DAV_GDBM_NS_KEY;
  +    key.dsize = DAV_GDBM_NS_KEY_LEN;
  +    if ((err = dav_dbm_fetch(db, key, &value)) != NULL) {
  +        /* ### push a higher-level description? */
  +        return err;
  +    }
  +
  +    if (value.dptr == NULL) {
  +	dav_propdb_metadata m = {
  +	    DAV_DBVSN_MAJOR, DAV_DBVSN_MINOR, 0
  +	};
  +
  +        /*
  +        ** If there is no METADATA key, then the database may be
  +        ** from versions 0.9.0 .. 0.9.4 (which would be incompatible).
  +        ** These can be identified by the presence of an NS_TABLE entry.
  +        */
  +        key.dptr = "NS_TABLE";
  +        key.dsize = 8;
  +        if (dav_dbm_exists(db, key)) {
  +            dav_dbm_close(db);
  +
  +            /* call it a major version error */
  +            return dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR,
  +                                 DAV_ERR_PROP_BAD_MAJOR,
  +                                 "Prop database has the wrong major "
  +                                 "version number and cannot be used.");
  +	}
  +
  +	/* initialize a new metadata structure */
  +	dav_set_bufsize(pool, &db->ns_table, sizeof(m));
  +	memcpy(db->ns_table.buf, &m, sizeof(m));
  +    }
  +    else {
  +	dav_propdb_metadata m;
  +        int ns;
  +        const char *uri;
  +
  +	dav_set_bufsize(pool, &db->ns_table, value.dsize);
  +	memcpy(db->ns_table.buf, value.dptr, value.dsize);
  +
  +	memcpy(&m, value.dptr, sizeof(m));
  +	if (m.major != DAV_DBVSN_MAJOR) {
  +	    dav_dbm_close(db);
  +
  +	    return dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR,
  +				 DAV_ERR_PROP_BAD_MAJOR,
  +				 "Prop database has the wrong major "
  +				 "version number and cannot be used.");
  +	}
  +	db->version = m.minor;
  +	db->ns_count = ntohs(m.ns_count);
  +
  +	dav_dbm_freedatum(db, value);
  +
  +        /* create db->uri_index */
  +        for (ns = 0, uri = db->ns_table.buf + sizeof(dav_propdb_metadata);
  +             ns++ < db->ns_count;
  +             uri += strlen(uri) + 1) {
  +
  +            /* we must copy the key, in case ns_table.buf moves */
  +            apr_hash_set(db->uri_index,
  +                         apr_pstrdup(pool, uri), APR_HASH_KEY_STRING,
  +                         (void *)ns);
  +        }
  +    }
  +
  +    *pdb = db;
  +    return NULL;
  +}
  +
  +static void dav_propdb_close(dav_db *db)
  +{
  +
  +    if (db->ns_table_dirty) {
  +	dav_propdb_metadata m;
  +	apr_datum_t key;
  +	apr_datum_t value;
  +	dav_error *err;
  +
  +	key.dptr = DAV_GDBM_NS_KEY;
  +	key.dsize = DAV_GDBM_NS_KEY_LEN;
  +
  +	value.dptr = db->ns_table.buf;
  +	value.dsize = db->ns_table.cur_len;
  +
  +	/* fill in the metadata that we store into the prop db. */
  +	m.major = DAV_DBVSN_MAJOR;
  +	m.minor = db->version;          /* ### keep current minor version? */
  +	m.ns_count = htons(db->ns_count);
  +
  +	memcpy(db->ns_table.buf, &m, sizeof(m));
  +
  +	err = dav_dbm_store(db, key, value);
  +	/* ### what to do with the error? */
  +    }
  +
  +    dav_dbm_close(db);
  +}
  +
  +static dav_error * dav_propdb_define_namespaces(dav_db *db, dav_xmlns_info *xi)
  +{
  +    int ns = db->ns_count;
  +    const char *uri = db->ns_table.buf + sizeof(dav_propdb_metadata);
  +    char prefix[23];    /* "ns" + 20 digits + '\0' */
  +
  +    prefix[0] = 'n';
  +    prefix[1] = 's';
  +
  +    /* within the prop values, we use "ns%d" for prefixes... register them */
  +    for (ns = 0; ns < db->ns_count; ++ns, uri += strlen(uri) + 1) {
  +        sprintf(&prefix[2], "%d", ns);
  +
  +        /* prefix is on the stack, and ns_table.buf can move, so copy the
  +           two strings (and we simply want the values to last as long as
  +           the provided dav_xmlns_info). */
  +        dav_xmlns_add(xi,
  +                      apr_pstrdup(xi->pool, prefix),
  +                      apr_pstrdup(xi->pool, uri));
  +    }
  +
  +    return NULL;
  +}
  +
  +static dav_error * dav_propdb_output_value(dav_db *db,
  +                                           const dav_prop_name *name,
  +                                           dav_xmlns_info *xi,
  +                                           apr_text_header *phdr,
  +                                           int *found)
  +{
  +    apr_datum_t key = dav_build_key(db, name);
  +    apr_datum_t value;
  +    dav_error *err;
  +
  +    if ((err = dav_dbm_fetch(db, key, &value)) != NULL)
  +        return err;
  +    if (value.dptr == NULL) {
  +        *found = 0;
  +        return NULL;
  +    }
  +    *found = 1;
  +
  +    dav_append_prop(db->pool, key.dptr, value.dptr, phdr);
  +
  +    dav_dbm_freedatum(db, value);
  +
  +    return NULL;
  +}
  +
  +static dav_error * dav_propdb_map_namespaces(
  +    dav_db *db,
  +    const apr_array_header_t *namespaces,
  +    dav_namespace_map **mapping)
  +{
  +    dav_namespace_map *m = apr_palloc(db->pool, sizeof(*m));
  +    int i;
  +    int *pmap;
  +    const char **puri;
  +
  +    /*
  +    ** Iterate over the provided namespaces. If a namespace already appears
  +    ** in our internal map of URI -> ns_id, then store that in the map. If
  +    ** we don't know the namespace yet, then add it to the map and to our
  +    ** table of known namespaces.
  +    */
  +    pmap = apr_palloc(db->pool, namespaces->nelts * sizeof(*pmap));
  +    for (i = namespaces->nelts, puri = (const char **)namespaces->elts;
  +         i-- > 0;
  +         ++puri, ++pmap) {
  +
  +        const char *uri = *puri;
  +        apr_size_t uri_len = strlen(uri);
  +        int ns_id = (int)apr_hash_get(db->uri_index, uri, uri_len);
  +
  +        if (ns_id == 0) {
  +            dav_check_bufsize(db->pool, &db->ns_table, uri_len + 1);
  +            memcpy(db->ns_table.buf + db->ns_table.cur_len, uri, uri_len + 1);
  +            db->ns_table.cur_len += uri_len + 1;
  +
  +            /* copy the uri in case the passed-in namespaces changes in
  +               some way. */
  +            apr_hash_set(db->uri_index, apr_pstrdup(db->pool, uri), uri_len,
  +                         (void *)(db->ns_count + 1));
  +
  +            db->ns_table_dirty = 1;
  +
  +            *pmap = db->ns_count++;
  +        }
  +        else {
  +            *pmap = ns_id - 1;
  +        }
  +    }
  +
  +    m->ns_map = pmap;
  +    *mapping = m;
  +    return NULL;
  +}
  +
  +static dav_error * dav_propdb_store(dav_db *db, const dav_prop_name *name,
  +                                    const apr_xml_elem *elem,
  +                                    dav_namespace_map *mapping)
  +{
  +    apr_datum_t key = dav_build_key(db, name);
  +    apr_datum_t value;
  +
  +    /* Note: mapping->ns_map was set up in dav_propdb_map_namespaces() */
  +
  +    /* ### use a db- subpool for these values? clear on exit? */
  +
  +    /* quote all the values in the element */
  +    /* ### be nice to do this without affecting the element itself */
  +    /* ### of course, the cast indicates Badness is occurring here */
  +    apr_xml_quote_elem(db->pool, (apr_xml_elem *)elem);
  +
  +    /* generate a text blob for the xml:lang plus the contents */
  +    apr_xml_to_text(db->pool, elem, APR_XML_X2T_LANG_INNER, NULL,
  +                    mapping->ns_map,
  +                    (const char **)&value.dptr, &value.dsize);
  +
  +    return dav_dbm_store(db, key, value);
  +}
  +
  +static dav_error * dav_propdb_remove(dav_db *db, const dav_prop_name *name)
  +{
  +    apr_datum_t key = dav_build_key(db, name);
  +    return dav_dbm_delete(db, key);
  +}
  +
  +static int dav_propdb_exists(dav_db *db, const dav_prop_name *name)
  +{
  +    apr_datum_t key = dav_build_key(db, name);
  +    return dav_dbm_exists(db, key);
  +}
  +
  +static const char *dav_get_ns_table_uri(dav_db *db, int ns_id)
  +{
  +    const char *p = db->ns_table.buf + sizeof(dav_propdb_metadata);
  +
  +    while (ns_id--)
  +        p += strlen(p) + 1;
  +
  +    return p;
  +}
  +
  +static void dav_set_name(dav_db *db, dav_prop_name *pname)
  +{
  +    const char *s = db->iter.dptr;
  +
  +    if (s == NULL) {
  +        pname->ns = pname->name = NULL;
  +    }
  +    else if (*s == ':') {
  +        pname->ns = "";
  +        pname->name = s + 1;
  +    }
  +    else {
  +        int id = atoi(s);
  +
  +        pname->ns = dav_get_ns_table_uri(db, id);
  +        if (s[1] == ':') {
  +            pname->name = s + 2;
  +        }
  +        else {
  +            pname->name = ap_strchr_c(s + 2, ':') + 1;
  +        }
  +    }
  +}
  +
  +static dav_error * dav_propdb_next_name(dav_db *db, dav_prop_name *pname)
  +{
  +    dav_error *err;
  +
  +    /* free the previous key. note: if the loop is aborted, then the DBM
  +       will toss the key (via pool cleanup) */
  +    if (db->iter.dptr != NULL)
  +        dav_dbm_freedatum(db, db->iter);
  +
  +    if ((err = dav_dbm_nextkey(db, &db->iter)) != NULL)
  +        return err;
  +
  +    /* skip past the METADATA key */
  +    if (db->iter.dptr != NULL && *db->iter.dptr == 'M')
  +        return dav_propdb_next_name(db, pname);
  +
  +    dav_set_name(db, pname);
  +    return NULL;
  +}
  +
  +static dav_error * dav_propdb_first_name(dav_db *db, dav_prop_name *pname)
  +{
  +    dav_error *err;
  +
  +    if ((err = dav_dbm_firstkey(db, &db->iter)) != NULL)
  +        return err;
  +
  +    /* skip past the METADATA key */
  +    if (db->iter.dptr != NULL && *db->iter.dptr == 'M')
  +        return dav_propdb_next_name(db, pname);
  +
  +    dav_set_name(db, pname);
  +    return NULL;
  +}
  +
  +static dav_error * dav_propdb_get_rollback(dav_db *db,
  +                                           const dav_prop_name *name,
  +                                           dav_deadprop_rollback **prollback)
  +{
  +    dav_deadprop_rollback *rb = apr_pcalloc(db->pool, sizeof(*rb));
  +    apr_datum_t key;
  +    apr_datum_t value;
  +    dav_error *err;
  +
  +    key = dav_build_key(db, name);
  +    rb->key.dptr = apr_pstrdup(db->pool, key.dptr);
  +    rb->key.dsize = key.dsize;
  +
  +    if ((err = dav_dbm_fetch(db, key, &value)) != NULL)
  +        return err;
  +    if (value.dptr != NULL) {
  +        rb->value.dptr = apr_pmemdup(db->pool, value.dptr, value.dsize);
  +        rb->value.dsize = value.dsize;
  +    }
  +
  +    *prollback = rb;
  +    return NULL;
  +}
  +
  +static dav_error * dav_propdb_apply_rollback(dav_db *db,
  +                                             dav_deadprop_rollback *rollback)
  +{
  +    if (rollback->value.dptr == NULL) {
  +        /* don't fail if the thing isn't really there. */
  +        (void) dav_dbm_delete(db, rollback->key);
  +        return NULL;
  +    }
  +
  +    return dav_dbm_store(db, rollback->key, rollback->value);
  +}
  +
   const dav_hooks_db dav_hooks_db_dbm =
   {
  -    dav_dbm_open,
  -    dav_dbm_close,
  -    dav_dbm_fetch,
  -    dav_dbm_store,
  -    dav_dbm_delete,
  -    dav_dbm_exists,
  -    dav_dbm_firstkey,
  -    dav_dbm_nextkey,
  -    dav_dbm_freedatum,
  +    dav_propdb_open,
  +    dav_propdb_close,
  +    dav_propdb_define_namespaces,
  +    dav_propdb_output_value,
  +    dav_propdb_map_namespaces,
  +    dav_propdb_store,
  +    dav_propdb_remove,
  +    dav_propdb_exists,
  +    dav_propdb_first_name,
  +    dav_propdb_next_name,
  +    dav_propdb_get_rollback,
  +    dav_propdb_apply_rollback,
   };
  
  
  
  1.53      +88 -12    httpd-2.0/modules/dav/main/mod_dav.h
  
  Index: mod_dav.h
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/dav/main/mod_dav.h,v
  retrieving revision 1.52
  retrieving revision 1.53
  diff -u -r1.52 -r1.53
  --- mod_dav.h	2001/09/11 12:29:28	1.52
  +++ mod_dav.h	2001/09/18 04:09:19	1.53
  @@ -533,8 +533,8 @@
   ** XML namespace handling
   **
   ** This structure tracks namespace declarations (xmlns:prefix="URI").
  -** It maintains a many-to-one relationship of URIs-to-prefixes. In other
  -** words, a URI may be defined by multiple prefixes, but any specific
  +** It maintains a one-to-many relationship of URIs-to-prefixes. In other
  +** words, one URI may be defined by many prefixes, but any specific
   ** prefix will specify only one URI.
   **
   ** Prefixes using the "g###" pattern can be generated automatically if
  @@ -1020,8 +1020,14 @@
   */
   
   typedef struct dav_db dav_db;
  -typedef apr_datum_t dav_datum;
  +typedef struct dav_namespace_map dav_namespace_map;
  +typedef struct dav_deadprop_rollback dav_deadprop_rollback;
   
  +typedef struct {
  +    const char *ns;     /* "" signals "no namespace" */
  +    const char *name;
  +} dav_prop_name;
  +
   /* hook functions to enable pluggable databases */
   struct dav_hooks_propdb
   {
  @@ -1029,24 +1035,94 @@
   			dav_db **pdb);
       void (*close)(dav_db *db);
   
  +    /*
  +    ** In bulk, define any namespaces that the values and their name
  +    ** elements may need.
  +    **
  +    ** Note: sometimes mod_dav will defer calling this until output_value
  +    ** returns found==1. If the output process needs the dav_xmlns_info
  +    ** filled for its work, then it will need to fill it on demand rather
  +    ** than depending upon this hook to fill in the structure.
  +    **
  +    ** Note: this will *always* be called during an output sequence. Thus,
  +    ** the provider may rely solely on using this to fill the xmlns info.
  +    */
  +    dav_error * (*define_namespaces)(dav_db *db, dav_xmlns_info *xi);
  +
       /*
  -    ** Fetch the value from the database. If the value does not exist,
  -    ** then *pvalue should be zeroed.
  +    ** Output the value from the database (i.e. add an element name and
  +    ** the value into *phdr). Set *found based on whether the name/value
  +    ** was found in the propdb.
       **
       ** Note: it is NOT an error for the key/value pair to not exist.
  +    **
  +    ** The dav_xmlns_info passed to define_namespaces() is also passed to
  +    ** each output_value() call so that namespaces can be added on-demand.
  +    ** It can also be used to look up prefixes or URIs during the output
  +    ** process.
       */
  -    dav_error * (*fetch)(dav_db *db, dav_datum key, dav_datum *pvalue);
  +    dav_error * (*output_value)(dav_db *db, const dav_prop_name *name,
  +                                dav_xmlns_info *xi,
  +                                apr_text_header *phdr, int *found);
   
  -    dav_error * (*store)(dav_db *db, dav_datum key, dav_datum value);
  -    dav_error * (*remove)(dav_db *db, dav_datum key);
  +    /*
  +    ** Build a mapping from "global" namespaces (stored in apr_xml_*)
  +    ** into provider-local namespace identifiers.
  +    **
  +    ** This mapping should be done once per set of namespaces, and the
  +    ** resulting mapping should be passed into the store() hook function.
  +    **
  +    ** Note: usually, there is just a single document/namespaces for all
  +    ** elements passed. However, the generality of creating multiple
  +    ** mappings and passing them to store() is provided here.
  +    **
  +    ** Note: this is only in preparation for a series of store() calls.
  +    ** As a result, the propdb must be open for read/write access when
  +    ** this function is called.
  +    */
  +    dav_error * (*map_namespaces)(dav_db *db,
  +                                  const apr_array_header_t *namespaces,
  +                                  dav_namespace_map **mapping);
  +    
  +    /*
  +    ** Store a property value for a given name. The value->combined field
  +    ** MUST be set for this call.
  +    **
  +    ** ### WARNING: current providers will quote the text within ELEM.
  +    ** ### this implies you can call this function only once with a given
  +    ** ### element structure (a second time will quote it again).
  +    */
  +    dav_error * (*store)(dav_db *db, const dav_prop_name *name,
  +                         const apr_xml_elem *elem,
  +                         dav_namespace_map *mapping);
   
  +    /* remove a given property */
  +    dav_error * (*remove)(dav_db *db, const dav_prop_name *name);
  +
       /* returns 1 if the record specified by "key" exists; 0 otherwise */
  -    int (*exists)(dav_db *db, dav_datum key);
  +    int (*exists)(dav_db *db, const dav_prop_name *name);
   
  -    dav_error * (*firstkey)(dav_db *db, dav_datum *pkey);
  -    dav_error * (*nextkey)(dav_db *db, dav_datum *pkey);
  +    /*
  +    ** Iterate over the property names in the database.
  +    **
  +    ** iter->name.ns == iter->name.name == NULL when there are no more names.
  +    **
  +    ** Note: only one iteration may occur over the propdb at a time.
  +    */
  +    dav_error * (*first_name)(dav_db *db, dav_prop_name *pname);
  +    dav_error * (*next_name)(dav_db *db, dav_prop_name *pname);
   
  -    void (*freedatum)(dav_db *db, dav_datum data);
  +    /*
  +    ** Rollback support: get rollback context, and apply it.
  +    **
  +    ** struct dav_deadprop_rollback is a provider-private structure; it
  +    ** should remember the name, and the name's old value (or the fact that
  +    ** the value was not present, and should be deleted if a rollback occurs).
  +    */
  +    dav_error * (*get_rollback)(dav_db *db, const dav_prop_name *name,
  +                                dav_deadprop_rollback **prollback);
  +    dav_error * (*apply_rollback)(dav_db *db,
  +                                  dav_deadprop_rollback *rollback);
   };
   
   
  
  
  
  1.29      +131 -566  httpd-2.0/modules/dav/main/props.c
  
  Index: props.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/dav/main/props.c,v
  retrieving revision 1.28
  retrieving revision 1.29
  diff -u -r1.28 -r1.29
  --- props.c	2001/05/16 17:24:18	1.28
  +++ props.c	2001/09/18 04:09:19	1.29
  @@ -203,50 +203,12 @@
   */
   #define DAV_DISABLE_WRITABLE_PROPS	1
   
  -#define DAV_GDBM_NS_KEY		"METADATA"
  -#define DAV_GDBM_NS_KEY_LEN	8
  -
   #define DAV_EMPTY_VALUE		"\0"	/* TWO null terms */
   
   /* the namespace URI was not found; no ID is available */
   #define AP_XML_NS_ERROR_NOT_FOUND	(AP_XML_NS_ERROR_BASE)
   
  -typedef struct {
  -    unsigned char major;
  -#define DAV_DBVSN_MAJOR		4
  -    /*
  -    ** V4 -- 0.9.9 ..
  -    **       Prior versions could have keys or values with invalid
  -    **       namespace prefixes as a result of the xmlns="" form not
  -    **       resetting the default namespace to be "no namespace". The
  -    **       namespace would be set to "" which is invalid; it should
  -    **       be set to "no namespace".
  -    **
  -    ** V3 -- 0.9.8
  -    **       Prior versions could have values with invalid namespace
  -    **       prefixes due to an incorrect mapping of input to propdb
  -    **       namespace indices. Version bumped to obsolete the old
  -    **       values.
  -    **
  -    ** V2 -- 0.9.7
  -    **       This introduced the xml:lang value into the property value's
  -    **       record in the propdb.
  -    **
  -    ** V1 -- .. 0.9.6
  -    **       Initial version.
  -    */
  -
  -
  -    unsigned char minor;
  -#define DAV_DBVSN_MINOR		0
  -
  -    short ns_count;
  -
  -} dav_propdb_metadata;
  -
   struct dav_propdb {
  -    int version;		/* *minor* version of this db */
  -
       apr_pool_t *p;		/* the pool we should use */
       request_rec *r;		/* the request record */
   
  @@ -255,17 +217,11 @@
       int deferred;		/* open of db has been deferred */
       dav_db *db;			/* underlying database containing props */
   
  -    dav_buffer ns_table;	/* table of namespace URIs */
  -    short ns_count;		/* number of entries in table */
  -    int ns_table_dirty;		/* ns_table was modified */
  -
       apr_array_header_t *ns_xlate;	/* translation of an elem->ns to URI */
  -    int *ns_map;		/* map elem->ns to propdb ns values */
  -    int incomplete_map;		/* some mappings do not exist */
  +    dav_namespace_map *mapping;         /* namespace mapping */
   
       dav_lockdb *lockdb;		/* the lock database */
   
  -    dav_buffer wb_key;		/* work buffer for dav_gdbm_key */
       dav_buffer wb_lock;		/* work buffer for lockdiscovery property */
   
       /* if we ever run a GET subreq, it will be stored here */
  @@ -273,7 +229,6 @@
   
       /* hooks we should use for processing (based on the target resource) */
       const dav_hooks_db *db_hooks;
  -
   };
   
   /* NOTE: dav_core_props[] and the following enum must stay in sync. */
  @@ -298,32 +253,16 @@
   
   /*
   ** This structure is used to track information needed for a rollback.
  -** If a SET was performed and no prior value existed, then value.dptr
  -** will be NULL.
   */
   typedef struct dav_rollback_item {
  -    dav_datum key;		/* key for the item being saved */
  -    dav_datum value;		/* value before set/replace/delete */
  -
  -    /* or use the following (choice selected by dav_prop_ctx.is_liveprop) */
  -    struct dav_liveprop_rollback *liveprop;	/* liveprop rollback ctx */
  +    /* select one of the two rollback context structures based on the
  +       value of dav_prop_ctx.is_liveprop */
  +    dav_deadprop_rollback *deadprop;
  +    dav_liveprop_rollback *liveprop;
   
   } dav_rollback_item;
   
   
  -#if 0
  -/* ### unused */
  -static const char *dav_get_ns_table_uri(dav_propdb *propdb, int ns)
  -{
  -    const char *p = propdb->ns_table.buf + sizeof(dav_propdb_metadata);
  -
  -    while (ns--)
  -	p += strlen(p) + 1;
  -
  -    return p;
  -}
  -#endif
  -
   static int dav_find_liveprop_provider(dav_propdb *propdb,
                                         const char *ns_uri,
                                         const char *propname,
  @@ -563,183 +502,22 @@
       return NULL;
   }
   
  -static void dav_append_prop(dav_propdb *propdb,
  -			    const char *name, const char *value,
  -			    ap_text_header *phdr)
  +static void dav_output_prop_name(apr_pool_t *pool,
  +                                 const dav_prop_name *name,
  +                                 dav_xmlns_info *xi,
  +                                 apr_text_header *phdr)
   {
       const char *s;
  -    const char *lang = value;
  -
  -    /* skip past the xml:lang value */
  -    value += strlen(lang) + 1;
   
  -    if (*value == '\0') {
  -	/* the property is an empty value */
  -	if (*name == ':') {
  -	    /* "no namespace" case */
  -	    s = apr_psprintf(propdb->p, "<%s/>" DEBUG_CR, name+1);
  -	}
  -	else {
  -	    s = apr_psprintf(propdb->p, "<ns%s/>" DEBUG_CR, name);
  -	}
  -    }
  -    else if (*lang != '\0') {
  -	if (*name == ':') {
  -	    /* "no namespace" case */
  -	    s = apr_psprintf(propdb->p, "<%s xml:lang=\"%s\">%s</%s>" DEBUG_CR,
  -			    name+1, lang, value, name+1);
  -	}
  -	else {
  -	    s = apr_psprintf(propdb->p, "<ns%s xml:lang=\"%s\">%s</ns%s>" DEBUG_CR,
  -			    name, lang, value, name);
  -	}
  -    }
  -    else if (*name == ':') {
  -	/* "no namespace" case */
  -	s = apr_psprintf(propdb->p, "<%s>%s</%s>" DEBUG_CR, name+1, value, name+1);
  -    }
  +    if (*name->ns == '\0')
  +        s = apr_psprintf(pool, "<%s/>" DEBUG_CR, name->name);
       else {
  -	s = apr_psprintf(propdb->p, "<ns%s>%s</ns%s>" DEBUG_CR, name, value, name);
  -    }
  -    ap_text_append(propdb->p, phdr, s);
  -}
  -
  -/*
  -** Prepare the ns_map variable in the propdb structure. This entails copying
  -** all URIs from the "input" namespace list (in propdb->ns_xlate) into the
  -** propdb's list of namespaces. As each URI is copied (or pre-existing
  -** URI looked up), the index mapping is stored into the ns_map variable.
  -**
  -** Note: we must copy all declared namespaces because we cannot easily
  -**   determine which input namespaces were actually used within the property
  -**   values that are being stored within the propdb. Theoretically, we can
  -**   determine this at the point where we serialize the property values
  -**   back into strings. This would require a bit more work, and will be
  -**   left to future optimizations.
  -**
  -** ### we should always initialize the propdb namespace array with "DAV:"
  -** ### since we know it will be entered anyhow (by virtue of it always
  -** ### occurring in the ns_xlate array). That will allow us to use
  -** ### AP_XML_NS_DAV_ID for propdb ns values, too.
  -*/
  -static void dav_prep_ns_map(dav_propdb *propdb, int add_ns)
  -{
  -    int i;
  -    const char **puri;
  -    const int orig_count = propdb->ns_count;
  -    int *pmap;
  -    int updating = 0;	/* are we updating an existing ns_map? */
  -
  -    if (propdb->ns_map) {
  -	if (add_ns && propdb->incomplete_map) {
  -	    /* we must revisit the map and insert new entries */
  -	    updating = 1;
  -	    propdb->incomplete_map = 0;
  -	}
  -	else {
  -	    /* nothing to do: we have a proper ns_map */
  -	    return;
  -	}
  -    }
  -    else {
  -	propdb->ns_map = apr_palloc(propdb->p, propdb->ns_xlate->nelts * sizeof(*propdb->ns_map));
  -    }
  -
  -    pmap = propdb->ns_map;
  +        const char *prefix = dav_xmlns_add_uri(xi, name->ns);
   
  -    /* ### stupid O(n * orig_count) algorithm */
  -    for (i = propdb->ns_xlate->nelts, puri = (const char **)propdb->ns_xlate->elts;
  -	 i-- > 0;
  -	 ++puri, ++pmap) {
  -
  -	const char *uri = *puri;
  -	const size_t uri_len = strlen(uri);
  -
  -	if (updating) {
  -	    /* updating an existing mapping... we can skip a lot of stuff */
  -
  -	    if (*pmap != AP_XML_NS_ERROR_NOT_FOUND) {
  -		/* This entry has been filled in, so we can skip it */
  -		continue;
  -	    }
  -	}
  -	else {
  -	    int j;
  -	    size_t len;
  -	    const char *p;
  -
  -	    /*
  -	    ** GIVEN: uri (a namespace URI from the request input)
  -	    **
  -	    ** FIND: an equivalent URI in the propdb namespace table
  -	    */
  -
  -	    /* only scan original entries (we may have added some in here) */
  -	    for (p = propdb->ns_table.buf + sizeof(dav_propdb_metadata),
  -		     j = 0;
  -		 j < orig_count;
  -		 ++j, p += len + 1) {
  -
  -		len = strlen(p);
  -
  -		if (uri_len != len)
  -		    continue;
  -		if (memcmp(uri, p, len) == 0) {
  -		    *pmap = j;
  -		    goto next_input_uri;
  -		}
  -	    }
  -
  -	    if (!add_ns) {
  -		*pmap = AP_XML_NS_ERROR_NOT_FOUND;
  -
  -		/*
  -		** This flag indicates that we have an ns_map with missing
  -		** entries. If dav_prep_ns_map() is called with add_ns==1 AND
  -		** this flag is set, then we zip thru the array and add those
  -		** URIs (effectively updating the ns_map as if add_ns=1 was
  -		** passed when the initial prep was called).
  -		*/
  -		propdb->incomplete_map = 1;
  -
  -		continue;
  -	    }
  -	}
  -
  -	/*
  -	** The input URI was not found in the propdb namespace table, and
  -	** we are supposed to add it. Append it to the table and store
  -	** the index into the ns_map.
  -	*/
  -	dav_check_bufsize(propdb->p, &propdb->ns_table, uri_len + 1);
  -	memcpy(propdb->ns_table.buf + propdb->ns_table.cur_len, uri, uri_len + 1);
  -	propdb->ns_table.cur_len += uri_len + 1;
  -
  -	propdb->ns_table_dirty = 1;
  -
  -	*pmap = propdb->ns_count++;
  -
  -   next_input_uri:
  -	;
  -    }
  -}
  -
  -/* find the "DAV:" namespace in our table and return its ID. */
  -static int dav_find_dav_id(dav_propdb *propdb)
  -{
  -    const char *p = propdb->ns_table.buf + sizeof(dav_propdb_metadata);
  -    int ns;
  -
  -    for (ns = 0; ns < propdb->ns_count; ++ns) {
  -	size_t len = strlen(p);
  -
  -	if (len == 4 && memcmp(p, "DAV:", 5) == 0)
  -	    return ns;
  -	p += len + 1;
  +        s = apr_psprintf(pool, "<%s:%s/>" DEBUG_CR, prefix, name->name);
       }
   
  -    /* the "DAV:" namespace is not present */
  -    return -1;
  +    apr_text_append(pool, phdr, s);
   }
   
   static void dav_insert_xmlns(apr_pool_t *p, const char *pre_prefix, int ns,
  @@ -751,96 +529,9 @@
       ap_text_append(p, phdr, s);
   }
   
  -/* return all known namespaces (in this propdb) */
  -static void dav_get_propdb_xmlns(dav_propdb *propdb, ap_text_header *phdr)
  -{
  -    int i;
  -    const char *p = propdb->ns_table.buf + sizeof(dav_propdb_metadata);
  -    size_t len;
  -
  -    /* note: ns_count == 0 when we have no propdb file */
  -    for (i = 0; i < propdb->ns_count; ++i, p += len + 1) {
  -
  -	len = strlen(p);
  -
  -	dav_insert_xmlns(propdb->p, "ns", i, p, phdr);
  -    }
  -}
  -
  -/* add a namespace decl from one of the namespace tables */
  -static void dav_add_marked_xmlns(dav_propdb *propdb, char *marks, int ns,
  -				 apr_array_header_t *ns_table,
  -				 const char *pre_prefix,
  -				 ap_text_header *phdr)
  -{
  -    if (marks[ns])
  -	return;
  -    marks[ns] = 1;
  -
  -    dav_insert_xmlns(propdb->p,
  -		     pre_prefix, ns, AP_XML_GET_URI_ITEM(ns_table, ns),
  -		     phdr);
  -}
  -
  -/*
  -** Internal function to build a key
  -**
  -** WARNING: returns a pointer to a "static" buffer holding the key. The
  -**          value must be copied or no longer used if this function is
  -**          called again.
  -*/
  -static dav_datum dav_gdbm_key(dav_propdb *propdb, const ap_xml_elem *elem)
  -{
  -    int ns;
  -    char nsbuf[20];
  -    size_t l_ns;
  -    size_t l_name = strlen(elem->name);
  -    dav_datum key = { 0 };
  -
  -    /*
  -     * Convert namespace ID to a string. "no namespace" is an empty string,
  -     * so the keys will have the form ":name". Otherwise, the keys will
  -     * have the form "#:name".
  -     */
  -    if (elem->ns == AP_XML_NS_NONE) {
  -	nsbuf[0] = '\0';
  -	l_ns = 0;
  -    }
  -    else {
  -	if (propdb->ns_map == NULL) {
  -	    /*
  -	     * Note that we prep the map and do NOT add namespaces. If that
  -	     * is required, then the caller should have called prep
  -	     * beforehand, passing the correct values.
  -	     */
  -	    dav_prep_ns_map(propdb, 0);
  -	}
  -
  -	ns = propdb->ns_map[elem->ns];
  -	if (AP_XML_NS_IS_ERROR(ns))
  -	    return key;		/* zeroed */
  -
  -	l_ns = sprintf(nsbuf, "%d", ns);
  -    }
  -
  -    /* assemble: #:name */
  -    dav_set_bufsize(propdb->p, &propdb->wb_key, l_ns + 1 + l_name + 1);
  -    memcpy(propdb->wb_key.buf, nsbuf, l_ns);
  -    propdb->wb_key.buf[l_ns] = ':';
  -    memcpy(&propdb->wb_key.buf[l_ns + 1], elem->name, l_name + 1);
  -
  -    /* build the database key */
  -    key.dsize = l_ns + 1 + l_name + 1;
  -    key.dptr = propdb->wb_key.buf;
  -
  -    return key;
  -}
  -
   static dav_error *dav_really_open_db(dav_propdb *propdb, int ro)
   {
       dav_error *err;
  -    dav_datum key;
  -    dav_datum value = { 0 };
   
       /* we're trying to open the db; turn off the 'deferred' flag */
       propdb->deferred = 0;
  @@ -861,64 +552,6 @@
       **       access, then a database was created and opened.
       */
   
  -    if (propdb->db != NULL) {
  -	key.dptr = DAV_GDBM_NS_KEY;
  -	key.dsize = DAV_GDBM_NS_KEY_LEN;
  -	if ((err = (*propdb->db_hooks->fetch)(propdb->db, key,
  -					      &value)) != NULL) {
  -	    /* ### push a higher-level description? */
  -	    return err;
  -	}
  -    }
  -    if (value.dptr == NULL) {
  -	dav_propdb_metadata m = {
  -	    DAV_DBVSN_MAJOR, DAV_DBVSN_MINOR, 0
  -	};
  -
  -	if (propdb->db != NULL) {
  -	    /*
  -	     * If there is no METADATA key, then the database may be
  -	     * from versions 0.9.0 .. 0.9.4 (which would be incompatible).
  -	     * These can be identified by the presence of an NS_TABLE entry.
  -	     */
  -	    key.dptr = "NS_TABLE";
  -	    key.dsize = 8;
  -	    if ((*propdb->db_hooks->exists)(propdb->db, key)) {
  -		(*propdb->db_hooks->close)(propdb->db);
  -
  -		/* call it a major version error */
  -		return dav_new_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
  -				     DAV_ERR_PROP_BAD_MAJOR,
  -				     "Prop database has the wrong major "
  -				     "version number and cannot be used.");
  -	    }
  -	}
  -
  -	/* initialize a new metadata structure */
  -	dav_set_bufsize(propdb->p, &propdb->ns_table, sizeof(m));
  -	memcpy(propdb->ns_table.buf, &m, sizeof(m));
  -    }
  -    else {
  -	dav_propdb_metadata m;
  -
  -	dav_set_bufsize(propdb->p, &propdb->ns_table, value.dsize);
  -	memcpy(propdb->ns_table.buf, value.dptr, value.dsize);
  -
  -	memcpy(&m, value.dptr, sizeof(m));
  -	if (m.major != DAV_DBVSN_MAJOR) {
  -	    (*propdb->db_hooks->close)(propdb->db);
  -
  -	    return dav_new_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR,
  -				 DAV_ERR_PROP_BAD_MAJOR,
  -				 "Prop database has the wrong major "
  -				 "version number and cannot be used.");
  -	}
  -	propdb->version = m.minor;
  -	propdb->ns_count = ntohs(m.ns_count);
  -
  -	(*propdb->db_hooks->freedatum)(propdb->db, value);
  -    }
  -
       return NULL;
   }
   
  @@ -940,7 +573,6 @@
       }
   #endif
   
  -    propdb->version = DAV_DBVSN_MINOR;
       propdb->r = r;
       propdb->p = r->pool; /* ### get rid of this */
       propdb->resource = resource;
  @@ -966,29 +598,6 @@
       if (propdb->db == NULL)
   	return;
   
  -    if (propdb->ns_table_dirty) {
  -	dav_propdb_metadata m;
  -	dav_datum key;
  -	dav_datum value;
  -	dav_error *err;
  -
  -	key.dptr = DAV_GDBM_NS_KEY;
  -	key.dsize = DAV_GDBM_NS_KEY_LEN;
  -
  -	value.dptr = propdb->ns_table.buf;
  -	value.dsize = propdb->ns_table.cur_len;
  -
  -	/* fill in the metadata that we store into the prop db. */
  -	m.major = DAV_DBVSN_MAJOR;
  -	m.minor = propdb->version;	/* ### keep current minor version? */
  -	m.ns_count = htons(propdb->ns_count);
  -
  -	memcpy(propdb->ns_table.buf, &m, sizeof(m));
  -
  -	err = (*propdb->db_hooks->store)(propdb->db, key, value);
  -	/* ### what to do with the error? */
  -    }
  -
       (*propdb->db_hooks->close)(propdb->db);
   }
   
  @@ -1011,9 +620,6 @@
               (void) dav_really_open_db(propdb, 1 /*ro*/);
           }
   
  -        /* generate all the namespaces that are in the propdb */
  -        dav_get_propdb_xmlns(propdb, &hdr_ns);
  -
           /* initialize the result with some start tags... */
           ap_text_append(propdb->p, &hdr,
   		       "<D:propstat>" DEBUG_CR
  @@ -1021,74 +627,60 @@
   
           /* if there ARE properties, then scan them */
           if (propdb->db != NULL) {
  -	    dav_datum key;
  -	    int dav_id = dav_find_dav_id(propdb);
  +            dav_xmlns_info *xi = dav_xmlns_create(propdb->p);
  +            dav_prop_name name;
   
  -	    (void) (*db_hooks->firstkey)(propdb->db, &key);
  -	    while (key.dptr) {
  -	        dav_datum prevkey;
  -
  -	        /* any keys with leading capital letters should be skipped
  -	           (real keys start with a number or a colon) */
  -	        if (*key.dptr >= 'A' && *key.dptr <= 'Z')
  -		    goto next_key;
  +            /* define (up front) any namespaces the db might need */
  +            (void) (*db_hooks->define_namespaces)(propdb->db, xi);
   
  +            /* get the first property name, beginning the scan */
  +	    (void) (*db_hooks->first_name)(propdb->db, &name);
  +	    while (name.ns != NULL) {
  +
   	        /*
   	        ** We also look for <DAV:getcontenttype> and
   	        ** <DAV:getcontentlanguage>. If they are not stored as dead
   	        ** properties, then we need to perform a subrequest to get
   	        ** their values (if any).
   	        */
  -	        if (dav_id != -1
  -		    && *key.dptr != ':'
  -		    && dav_id == atoi(key.dptr)) {
  -
  -		    const char *colon;
  -
  -		    /* find the colon */
  -		    if ( key.dptr[1] == ':' ) {
  -		        colon = key.dptr + 1;
  -		    }
  -		    else {
  -		        colon = strchr(key.dptr + 2, ':');
  -		    }
  -
  -		    if (colon[1] == 'g') {
  -		        if (strcmp(colon + 1, "getcontenttype") == 0) {
  -			    found_contenttype = 1;
  -		        }
  -		        else if (strcmp(colon + 1, "getcontentlanguage") == 0) {
  -			    found_contentlang = 1;
  -		        }
  -		    }
  +                if (*name.ns == 'D' && strcmp(name.ns, "DAV:") == 0
  +                    && *name.name == 'g') {
  +                    if (strcmp(name.name, "getcontenttype") == 0) {
  +                        found_contenttype = 1;
  +                    }
  +                    else if (strcmp(name.name, "getcontentlanguage") == 0) {
  +                        found_contentlang = 1;
  +                    }
   	        }
   
   	        if (what == DAV_PROP_INSERT_VALUE) {
  -		    dav_datum value;
  +                    dav_error *err;
  +                    int found;
   
  -		    (void) (*db_hooks->fetch)(propdb->db, key, &value);
  -		    if (value.dptr == NULL) {
  +                    if ((err = (*db_hooks->output_value)(propdb->db, &name,
  +                                                         xi, &hdr,
  +                                                         &found)) != NULL) {
   		        /* ### anything better to do? */
   		        /* ### probably should enter a 500 error */
   		        goto next_key;
  -		    }
  -
  -		    /* put the prop name and value into the result */
  -		    dav_append_prop(propdb, key.dptr, value.dptr, &hdr);
  -
  -		    (*db_hooks->freedatum)(propdb->db, value);
  +                    }
  +                    /* assert: found == 1 */
   	        }
   	        else {
  -		    /* simple, empty element if a value isn't needed */
  -		    dav_append_prop(propdb, key.dptr, DAV_EMPTY_VALUE, &hdr);
  +                    /* the value was not requested, so just add an empty
  +                       tag specifying the property name. */
  +                    dav_output_prop_name(propdb->p, &name, xi, &hdr);
   	        }
   
   	      next_key:
  -	        prevkey = key;
  -	        (void) (*db_hooks->nextkey)(propdb->db, &key);
  -	        (*db_hooks->freedatum)(propdb->db, prevkey);
  +	        (void) (*db_hooks->next_name)(propdb->db, &name);
   	    }
  -        }
  +
  +            /* all namespaces have been entered into xi. generate them into
  +               the output now. */
  +            dav_xmlns_generate(xi, &hdr_ns);
  +
  +        } /* propdb->db != NULL */
   
           /* add namespaces for all the liveprop providers */
           dav_add_all_liveprop_xmlns(propdb->p, &hdr_ns);
  @@ -1144,10 +736,10 @@
       ap_text_header hdr_bad = { 0 };
       ap_text_header hdr_ns = { 0 };
       int have_good = 0;
  -    int propdb_xmlns_done = 0;
       dav_get_props_result result = { 0 };
  -    char *marks_input;
       char *marks_liveprop;
  +    dav_xmlns_info *xi;
  +    int xi_filled = 0;
   
       /* ### NOTE: we should pass in TWO buffers -- one for keys, one for
          the marks */
  @@ -1159,19 +751,18 @@
   
       /* ### the marks should be in a buffer! */
       /* allocate zeroed-memory for the marks. These marks indicate which
  -       input namespaces we've generated into the output xmlns buffer */
  -    marks_input = apr_pcalloc(propdb->p, propdb->ns_xlate->nelts);
  +       liveprop namespaces we've generated into the output xmlns buffer */
   
       /* same for the liveprops */
       marks_liveprop = apr_pcalloc(propdb->p, dav_get_liveprop_ns_count() + 1);
   
  +    xi = dav_xmlns_create(propdb->p);
  +
       for (elem = elem->first_child; elem; elem = elem->next) {
  -        dav_datum key = { 0 };
  -	dav_datum value = { 0 };
   	dav_elem_private *priv;
   	dav_error *err;
   	dav_prop_insert inserted;
  -        int is_liveprop = 0;
  +        dav_prop_name name;
   
           /*
           ** First try live property providers; if they don't handle
  @@ -1188,7 +779,6 @@
   	    dav_find_liveprop(propdb, elem);
   
           if (priv->propid != DAV_PROPID_CORE_UNKNOWN) {
  -            is_liveprop = 1;
   
   	    /* insert the property. returns 1 if an insertion was done. */
   	    if ((err = dav_insert_liveprop(propdb, elem, DAV_PROP_INSERT_VALUE,
  @@ -1221,91 +811,75 @@
   		    }
   		}
   
  +                /* property added. move on to the next property. */
   		continue;
   	    }
               else if (inserted == DAV_PROP_INSERT_NOTDEF) {
  -                /* allow property to be handled as a dead property */
  -                is_liveprop = 0;
  +                /* nothing to do. fall thru to allow property to be handled
  +                   as a dead property */
  +            }
  +#if DAV_DEBUG
  +            else {
  +#if 0
  +                /* ### need to change signature to return an error */
  +                return dav_new_error(propdb->p, HTTP_INTERNAL_SERVER_ERROR, 0,
  +                                     "INTERNAL DESIGN ERROR: insert_liveprop "
  +                                     "did not insert what was asked for.");
  +#endif
               }
  +#endif
           }
   
  -	/*
  -	** If not handled as a live property, look in the dead property
  -        ** database.
  -	*/
  -        if (!is_liveprop) {
  -            /* make sure propdb is really open */
  -            if (propdb->deferred) {
  -                /* ### what to do with db open error? */
  -                (void) dav_really_open_db(propdb, 1 /*ro*/);
  -            }
  +	/* The property wasn't a live property, so look in the dead property
  +           database. */
   
  -            /* if not done yet,
  -             * generate all the namespaces that are in the propdb
  -             */
  -            if (!propdb_xmlns_done) {
  -                dav_get_propdb_xmlns(propdb, &hdr_ns);
  -                propdb_xmlns_done = 1;
  -            }
  +        /* make sure propdb is really open */
  +        if (propdb->deferred) {
  +            /* ### what to do with db open error? */
  +            (void) dav_really_open_db(propdb, 1 /*ro*/);
  +        }
   
  -	    /*
  -	    ** Note: the key may be NULL if we have no properties that are in
  -	    ** a namespace that matches the requested prop's namespace.
  -	    */
  -	    key = dav_gdbm_key(propdb, elem);
  +        if (elem->ns == APR_XML_NS_NONE)
  +            name.ns = "";
  +        else
  +            name.ns = APR_XML_GET_URI_ITEM(propdb->ns_xlate, elem->ns);
  +        name.name = elem->name;
   
  -	    /* fetch IF we have a db and a key. otherwise, value is NULL */
  -	    if (propdb->db != NULL && key.dptr != NULL) {
  -	        (void) (*db_hooks->fetch)(propdb->db, key, &value);
  -	    }
  -        }
  +        /* only bother to look if a database exists */
  +        if (propdb->db != NULL) {
  +            int found;
   
  -	if (value.dptr == NULL) {
  -	    /* not found. add a record to the "bad" propstats */
  +            if ((err = (*db_hooks->output_value)(propdb->db, &name,
  +                                                 xi, &hdr_good,
  +                                                 &found)) != NULL) {
  +                /* ### what to do? continue doesn't seem right... */
  +                continue;
  +            }
   
  -	    /* make sure we've started our "bad" propstat */
  -	    if (hdr_bad.first == NULL) {
  -		ap_text_append(propdb->p, &hdr_bad,
  -			       "<D:propstat>" DEBUG_CR
  -			       "<D:prop>" DEBUG_CR);
  -	    }
  +            if (found) {
  +                have_good = 1;
   
  -	    /* note: key.dptr may be NULL if the propdb doesn't have an
  -	       equivalent namespace stored */
  -	    if (key.dptr == NULL) {
  -		const char *s;
  -
  -		if (elem->ns == AP_XML_NS_NONE) {
  -		    /*
  -		     * elem has a prefix already (xml...:name) or the elem
  -		     * simply has no namespace.
  -		     */
  -		    s = apr_psprintf(propdb->p, "<%s/>" DEBUG_CR, elem->name);
  -		}
  -		else {
  -		    /* ensure that an xmlns is generated for the
  -		       input namespace */
  -		    dav_add_marked_xmlns(propdb, marks_input, elem->ns,
  -					 propdb->ns_xlate, "i", &hdr_ns);
  -		    s = apr_psprintf(propdb->p, "<i%d:%s/>" DEBUG_CR,
  -				    elem->ns, elem->name);
  -		}
  -		ap_text_append(propdb->p, &hdr_bad, s);
  -	    }
  -	    else {
  -		/* add in the bad prop using our namespace decl */
  -		dav_append_prop(propdb, key.dptr, DAV_EMPTY_VALUE, &hdr_bad);
  -	    }
  -	}
  -	else {
  -	    /* found it. put the value into the "good" propstats */
  +                /* if we haven't added the db's namespaces, then do so... */
  +                if (!xi_filled) {
  +                    (void) (*db_hooks->define_namespaces)(propdb->db, xi);
  +                    xi_filled = 1;
  +                }
  +                continue;
  +            }
  +        }
   
  -	    have_good = 1;
  +        /* not found as a live OR dead property. add a record to the "bad"
  +           propstats */
   
  -	    dav_append_prop(propdb, key.dptr, value.dptr, &hdr_good);
  +        /* make sure we've started our "bad" propstat */
  +        if (hdr_bad.first == NULL) {
  +            ap_text_append(propdb->p, &hdr_bad,
  +                           "<D:propstat>" DEBUG_CR
  +                           "<D:prop>" DEBUG_CR);
  +        }
   
  -	    (*db_hooks->freedatum)(propdb->db, value);
  -	}
  +        /* output this property's name (into the bad propstats) */
  +        dav_output_prop_name(propdb->p, &name, xi, &hdr_bad);
       }
   
       ap_text_append(propdb->p, &hdr_good,
  @@ -1318,6 +892,7 @@
   
       /* we may not have any "bad" results */
       if (hdr_bad.first != NULL) {
  +        /* "close" the bad propstat */
   	ap_text_append(propdb->p, &hdr_bad,
   		       "</D:prop>" DEBUG_CR
   		       "<D:status>HTTP/1.1 404 Not Found</D:status>" DEBUG_CR
  @@ -1333,7 +908,10 @@
   	}
       }
   
  +    /* add in all the various namespaces, and return them */
  +    dav_xmlns_generate(xi, &hdr_ns);
       result.xmlns = hdr_ns.first;
  +
       return result;
   }
   
  @@ -1436,7 +1014,9 @@
   	** Prep the element => propdb namespace index mapping, inserting
   	** namespace URIs into the propdb that don't exist.
   	*/
  -	dav_prep_ns_map(propdb, 1);
  +        (void) (*propdb->db_hooks->map_namespaces)(propdb->db,
  +                                                   propdb->ns_xlate,
  +                                                   &propdb->mapping);
       }
       else if (ctx->operation == DAV_PROP_OP_DELETE) {
   	/*
  @@ -1454,11 +1034,9 @@
   {
       dav_propdb *propdb = ctx->propdb;
       dav_error *err = NULL;
  -    dav_rollback_item *rollback;
       dav_elem_private *priv = ctx->prop->priv;
   
  -    rollback = apr_pcalloc(propdb->p, sizeof(*rollback));
  -    ctx->rollback = rollback;
  +    ctx->rollback = apr_pcalloc(propdb->p, sizeof(*ctx->rollback));
   
       if (ctx->is_liveprop) {
   	err = (*priv->provider->patch_exec)(propdb->resource,
  @@ -1467,38 +1045,32 @@
   					    &ctx->rollback->liveprop);
       }
       else {
  -	dav_datum key;
  +        dav_prop_name name;
   
  -	/* we're going to need the key for all operations */
  -	key = dav_gdbm_key(propdb, ctx->prop);
  +        if (ctx->prop->ns == APR_XML_NS_NONE)
  +            name.ns = "";
  +        else
  +            name.ns = APR_XML_GET_URI_ITEM(propdb->ns_xlate, ctx->prop->ns);
  +        name.name = ctx->prop->name;
   
   	/* save the old value so that we can do a rollback. */
  -	rollback->key = key;
  -	if ((err = (*propdb->db_hooks->fetch)(propdb->db, key,
  -					      &rollback->value)) != NULL)
  +	if ((err = (*propdb->db_hooks
  +                    ->get_rollback)(propdb->db, &name,
  +                                    &ctx->rollback->deadprop)) != NULL)
   	    goto error;
   
   	if (ctx->operation == DAV_PROP_OP_SET) {
   
  -	    dav_datum value;
  +	    /* Note: propdb->mapping was set in dav_prop_validate() */
  +            err = (*propdb->db_hooks->store)(propdb->db, &name, ctx->prop,
  +                                             propdb->mapping);
   
  -	    /* Note: propdb->ns_map was set in dav_prop_validate() */
  -
  -	    /* quote all the values in the element */
  -	    ap_xml_quote_elem(propdb->p, ctx->prop);
  -
  -	    /* generate a text blob for the xml:lang plus the contents */
  -	    ap_xml_to_text(propdb->p, ctx->prop, AP_XML_X2T_LANG_INNER, NULL,
  -			   propdb->ns_map,
  -			   (const char **)&value.dptr, &value.dsize);
  -
  -	    err = (*propdb->db_hooks->store)(propdb->db, key, value);
  -
   	    /*
   	    ** If an error occurred, then assume that we didn't change the
   	    ** value. Remove the rollback item so that we don't try to set
   	    ** its value during the rollback.
   	    */
  +            /* ### euh... where is the removal? */
   	}
   	else if (ctx->operation == DAV_PROP_OP_DELETE) {
   
  @@ -1507,7 +1079,7 @@
   	    ** we are deleting it for a second time.
   	    */
   	    /* ### but what about other errors? */
  -	    (void) (*propdb->db_hooks->remove)(propdb->db, key);
  +	    (void) (*propdb->db_hooks->remove)(propdb->db, &name);
   	}
       }
   
  @@ -1562,16 +1134,9 @@
   						ctx->liveprop_ctx,
   						ctx->rollback->liveprop);
       }
  -    else if (ctx->rollback->value.dptr == NULL) {
  -	/* don't fail if the thing isn't really there */
  -        /* ### but what about other errors? */
  -	(void) (*ctx->propdb->db_hooks->remove)(ctx->propdb->db,
  -						ctx->rollback->key);
  -    }
       else {
  -	err = (*ctx->propdb->db_hooks->store)(ctx->propdb->db,
  -					      ctx->rollback->key,
  -					      ctx->rollback->value);
  +        err = (*ctx->propdb->db_hooks
  +               ->apply_rollback)(ctx->propdb->db, ctx->rollback->deadprop);
       }
   
       if (err != NULL) {
  
  
  

Mime
View raw message