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)
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,
};
*/
#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 */
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 */
/* 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. */
/*
** 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,
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);
-}
+ const char *prefix = dav_xmlns_add_uri(xi, name->ns);
-/*
-** 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));
+ s = apr_psprintf(pool, "<%s:%s/>" DEBUG_CR, prefix, name->name);
}
- pmap = propdb->ns_map;
-
- /* ### 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;
- }
-
- /* 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,
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;
** 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;
}
}
#endif
- propdb->version = DAV_DBVSN_MINOR;
propdb->r = r;
propdb->p = r->pool; /* ### get rid of this */
propdb->resource = resource;
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);
}
(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
/* 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;
+ /* define (up front) any namespaces the db might need */
+ (void) (*db_hooks->define_namespaces)(propdb->db, xi);
- /* 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;
+ /* 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
** 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);
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 */
/* ### 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
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,
}
}
+ /* 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,
/* 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
}
}
+ /* add in all the various namespaces, and return them */
+ dav_xmlns_generate(xi, &hdr_ns);
result.xmlns = hdr_ns.first;
+
return result;
}
** 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) {
/*
{
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,
&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->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);
+ /* Note: propdb->mapping was set in dav_prop_validate() */
+ err = (*propdb->db_hooks->store)(propdb->db, &name, ctx->prop,
+ propdb->mapping);
/*
** 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) {
** 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);
}
}
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) {