~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to modules/dav/fs/dbm.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Licensed to the Apache Software Foundation (ASF) under one or more
 
2
 * contributor license agreements.  See the NOTICE file distributed with
 
3
 * this work for additional information regarding copyright ownership.
 
4
 * The ASF licenses this file to You under the Apache License, Version 2.0
 
5
 * (the "License"); you may not use this file except in compliance with
 
6
 * the License.  You may obtain a copy of the License at
 
7
 *
 
8
 *     http://www.apache.org/licenses/LICENSE-2.0
 
9
 *
 
10
 * Unless required by applicable law or agreed to in writing, software
 
11
 * distributed under the License is distributed on an "AS IS" BASIS,
 
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
13
 * See the License for the specific language governing permissions and
 
14
 * limitations under the License.
 
15
 */
 
16
 
 
17
/*
 
18
** DAV extension module for Apache 2.0.*
 
19
**  - Database support using DBM-style databases,
 
20
**    part of the filesystem repository implementation
 
21
*/
 
22
 
 
23
/*
 
24
** This implementation uses a SDBM database per file and directory to
 
25
** record the properties. These databases are kept in a subdirectory (of
 
26
** the directory in question or the directory that holds the file in
 
27
** question) named by the macro DAV_FS_STATE_DIR (.DAV). The filename of the
 
28
** database is equivalent to the target filename, and is
 
29
** DAV_FS_STATE_FILE_FOR_DIR (.state_for_dir) for the directory itself.
 
30
*/
 
31
 
 
32
#include "apr_strings.h"
 
33
#include "apr_file_io.h"
 
34
 
 
35
#include "apr_dbm.h"
 
36
 
 
37
#define APR_WANT_BYTEFUNC
 
38
#include "apr_want.h"       /* for ntohs and htons */
 
39
 
 
40
#include "mod_dav.h"
 
41
#include "repos.h"
 
42
 
 
43
 
 
44
struct dav_db {
 
45
    apr_pool_t *pool;
 
46
    apr_dbm_t *file;
 
47
 
 
48
    /* when used as a property database: */
 
49
 
 
50
    int version;                /* *minor* version of this db */
 
51
 
 
52
    dav_buffer ns_table;        /* table of namespace URIs */
 
53
    short ns_count;             /* number of entries in table */
 
54
    int ns_table_dirty;         /* ns_table was modified */
 
55
    apr_hash_t *uri_index;      /* map URIs to (1-based) table indices */
 
56
 
 
57
    dav_buffer wb_key;          /* work buffer for dav_gdbm_key */
 
58
 
 
59
    apr_datum_t iter;           /* iteration key */
 
60
};
 
61
 
 
62
/* -------------------------------------------------------------------------
 
63
 *
 
64
 * GENERIC DBM ACCESS
 
65
 *
 
66
 * For the most part, this just uses the APR DBM functions. They are wrapped
 
67
 * a bit with some error handling (using the mod_dav error functions).
 
68
 */
 
69
 
 
70
void dav_dbm_get_statefiles(apr_pool_t *p, const char *fname,
 
71
                            const char **state1, const char **state2)
 
72
{
 
73
    if (fname == NULL)
 
74
        fname = DAV_FS_STATE_FILE_FOR_DIR;
 
75
 
 
76
    apr_dbm_get_usednames(p, fname, state1, state2);
 
77
}
 
78
 
 
79
static dav_error * dav_fs_dbm_error(dav_db *db, apr_pool_t *p,
 
80
                                    apr_status_t status)
 
81
{
 
82
    int save_errno = errno;
 
83
    int errcode;
 
84
    const char *errstr;
 
85
    dav_error *err;
 
86
    char errbuf[200];
 
87
 
 
88
    if (status == APR_SUCCESS)
 
89
        return NULL;
 
90
 
 
91
    p = db ? db->pool : p;
 
92
 
 
93
    /* There might not be a <db> if we had problems creating it. */
 
94
    if (db == NULL) {
 
95
        errcode = 1;
 
96
        errstr = "Could not open property database.";
 
97
    }
 
98
    else {
 
99
        (void) apr_dbm_geterror(db->file, &errcode, errbuf, sizeof(errbuf));
 
100
        errstr = apr_pstrdup(p, errbuf);
 
101
    }
 
102
 
 
103
    err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, errcode, errstr);
 
104
    err->save_errno = save_errno;
 
105
    return err;
 
106
}
 
107
 
 
108
/* ensure that our state subdirectory is present */
 
109
/* ### does this belong here or in dav_fs_repos.c ?? */
 
110
void dav_fs_ensure_state_dir(apr_pool_t * p, const char *dirname)
 
111
{
 
112
    const char *pathname = apr_pstrcat(p, dirname, "/" DAV_FS_STATE_DIR, NULL);
 
113
 
 
114
    /* ### do we need to deal with the umask? */
 
115
 
 
116
    /* just try to make it, ignoring any resulting errors */
 
117
    (void) apr_dir_make(pathname, APR_OS_DEFAULT, p);
 
118
}
 
119
 
 
120
/* dav_dbm_open_direct:  Opens a *dbm database specified by path.
 
121
 *    ro = boolean read-only flag.
 
122
 */
 
123
dav_error * dav_dbm_open_direct(apr_pool_t *p, const char *pathname, int ro,
 
124
                                dav_db **pdb)
 
125
{
 
126
    apr_status_t status;
 
127
    apr_dbm_t *file;
 
128
 
 
129
    *pdb = NULL;
 
130
 
 
131
    if ((status = apr_dbm_open(&file, pathname,
 
132
                               ro ? APR_DBM_READONLY : APR_DBM_RWCREATE,
 
133
                               APR_OS_DEFAULT, p))
 
134
                != APR_SUCCESS
 
135
        && !ro) {
 
136
        /* ### do something with 'status' */
 
137
 
 
138
        /* we can't continue if we couldn't open the file
 
139
           and we need to write */
 
140
        return dav_fs_dbm_error(NULL, p, status);
 
141
    }
 
142
 
 
143
    /* may be NULL if we tried to open a non-existent db as read-only */
 
144
    if (file != NULL) {
 
145
        /* we have an open database... return it */
 
146
        *pdb = apr_pcalloc(p, sizeof(**pdb));
 
147
        (*pdb)->pool = p;
 
148
        (*pdb)->file = file;
 
149
    }
 
150
 
 
151
    return NULL;
 
152
}
 
153
 
 
154
static dav_error * dav_dbm_open(apr_pool_t * p, const dav_resource *resource,
 
155
                                int ro, dav_db **pdb)
 
156
{
 
157
    const char *dirpath;
 
158
    const char *fname;
 
159
    const char *pathname;
 
160
 
 
161
    /* Get directory and filename for resource */
 
162
    /* ### should test this result value... */
 
163
    (void) dav_fs_dir_file_name(resource, &dirpath, &fname);
 
164
 
 
165
    /* If not opening read-only, ensure the state dir exists */
 
166
    if (!ro) {
 
167
        /* ### what are the perf implications of always checking this? */
 
168
        dav_fs_ensure_state_dir(p, dirpath);
 
169
    }
 
170
 
 
171
    pathname = apr_pstrcat(p, dirpath, "/" DAV_FS_STATE_DIR "/",
 
172
                              fname ? fname : DAV_FS_STATE_FILE_FOR_DIR,
 
173
                              NULL);
 
174
 
 
175
    /* ### readers cannot open while a writer has this open; we should
 
176
       ### perform a few retries with random pauses. */
 
177
 
 
178
    /* ### do we need to deal with the umask? */
 
179
 
 
180
    return dav_dbm_open_direct(p, pathname, ro, pdb);
 
181
}
 
182
 
 
183
void dav_dbm_close(dav_db *db)
 
184
{
 
185
    apr_dbm_close(db->file);
 
186
}
 
187
 
 
188
dav_error * dav_dbm_fetch(dav_db *db, apr_datum_t key, apr_datum_t *pvalue)
 
189
{
 
190
    apr_status_t status = apr_dbm_fetch(db->file, key, pvalue);
 
191
 
 
192
    return dav_fs_dbm_error(db, NULL, status);
 
193
}
 
194
 
 
195
dav_error * dav_dbm_store(dav_db *db, apr_datum_t key, apr_datum_t value)
 
196
{
 
197
    apr_status_t status = apr_dbm_store(db->file, key, value);
 
198
 
 
199
    return dav_fs_dbm_error(db, NULL, status);
 
200
}
 
201
 
 
202
dav_error * dav_dbm_delete(dav_db *db, apr_datum_t key)
 
203
{
 
204
    apr_status_t status = apr_dbm_delete(db->file, key);
 
205
 
 
206
    return dav_fs_dbm_error(db, NULL, status);
 
207
}
 
208
 
 
209
int dav_dbm_exists(dav_db *db, apr_datum_t key)
 
210
{
 
211
    return apr_dbm_exists(db->file, key);
 
212
}
 
213
 
 
214
static dav_error * dav_dbm_firstkey(dav_db *db, apr_datum_t *pkey)
 
215
{
 
216
    apr_status_t status = apr_dbm_firstkey(db->file, pkey);
 
217
 
 
218
    return dav_fs_dbm_error(db, NULL, status);
 
219
}
 
220
 
 
221
static dav_error * dav_dbm_nextkey(dav_db *db, apr_datum_t *pkey)
 
222
{
 
223
    apr_status_t status = apr_dbm_nextkey(db->file, pkey);
 
224
 
 
225
    return dav_fs_dbm_error(db, NULL, status);
 
226
}
 
227
 
 
228
void dav_dbm_freedatum(dav_db *db, apr_datum_t data)
 
229
{
 
230
    apr_dbm_freedatum(db->file, data);
 
231
}
 
232
 
 
233
/* -------------------------------------------------------------------------
 
234
 *
 
235
 * PROPERTY DATABASE FUNCTIONS
 
236
 */
 
237
 
 
238
 
 
239
#define DAV_GDBM_NS_KEY         "METADATA"
 
240
#define DAV_GDBM_NS_KEY_LEN     8
 
241
 
 
242
typedef struct {
 
243
    unsigned char major;
 
244
#define DAV_DBVSN_MAJOR         4
 
245
    /*
 
246
    ** V4 -- 0.9.9 ..
 
247
    **       Prior versions could have keys or values with invalid
 
248
    **       namespace prefixes as a result of the xmlns="" form not
 
249
    **       resetting the default namespace to be "no namespace". The
 
250
    **       namespace would be set to "" which is invalid; it should
 
251
    **       be set to "no namespace".
 
252
    **
 
253
    ** V3 -- 0.9.8
 
254
    **       Prior versions could have values with invalid namespace
 
255
    **       prefixes due to an incorrect mapping of input to propdb
 
256
    **       namespace indices. Version bumped to obsolete the old
 
257
    **       values.
 
258
    **
 
259
    ** V2 -- 0.9.7
 
260
    **       This introduced the xml:lang value into the property value's
 
261
    **       record in the propdb.
 
262
    **
 
263
    ** V1 -- .. 0.9.6
 
264
    **       Initial version.
 
265
    */
 
266
 
 
267
 
 
268
    unsigned char minor;
 
269
#define DAV_DBVSN_MINOR         0
 
270
 
 
271
    short ns_count;
 
272
 
 
273
} dav_propdb_metadata;
 
274
 
 
275
struct dav_deadprop_rollback {
 
276
    apr_datum_t key;
 
277
    apr_datum_t value;
 
278
};
 
279
 
 
280
struct dav_namespace_map {
 
281
    int *ns_map;
 
282
};
 
283
 
 
284
/*
 
285
** Internal function to build a key
 
286
**
 
287
** WARNING: returns a pointer to a "static" buffer holding the key. The
 
288
**          value must be copied or no longer used if this function is
 
289
**          called again.
 
290
*/
 
291
static apr_datum_t dav_build_key(dav_db *db, const dav_prop_name *name)
 
292
{
 
293
    char nsbuf[20];
 
294
    apr_size_t l_ns, l_name = strlen(name->name);
 
295
    apr_datum_t key = { 0 };
 
296
 
 
297
    /*
 
298
     * Convert namespace ID to a string. "no namespace" is an empty string,
 
299
     * so the keys will have the form ":name". Otherwise, the keys will
 
300
     * have the form "#:name".
 
301
     */
 
302
    if (*name->ns == '\0') {
 
303
        nsbuf[0] = '\0';
 
304
        l_ns = 0;
 
305
    }
 
306
    else {
 
307
        long ns_id = (long)apr_hash_get(db->uri_index, name->ns,
 
308
                                      APR_HASH_KEY_STRING);
 
309
 
 
310
 
 
311
        if (ns_id == 0) {
 
312
            /* the namespace was not found(!) */
 
313
            return key;         /* zeroed */
 
314
        }
 
315
 
 
316
        l_ns = sprintf(nsbuf, "%ld", ns_id - 1);
 
317
    }
 
318
 
 
319
    /* assemble: #:name */
 
320
    dav_set_bufsize(db->pool, &db->wb_key, l_ns + 1 + l_name + 1);
 
321
    memcpy(db->wb_key.buf, nsbuf, l_ns);
 
322
    db->wb_key.buf[l_ns] = ':';
 
323
    memcpy(&db->wb_key.buf[l_ns + 1], name->name, l_name + 1);
 
324
 
 
325
    /* build the database key */
 
326
    key.dsize = l_ns + 1 + l_name + 1;
 
327
    key.dptr = db->wb_key.buf;
 
328
 
 
329
    return key;
 
330
}
 
331
 
 
332
static void dav_append_prop(apr_pool_t *pool,
 
333
                            const char *name, const char *value,
 
334
                            apr_text_header *phdr)
 
335
{
 
336
    const char *s;
 
337
    const char *lang = value;
 
338
 
 
339
    /* skip past the xml:lang value */
 
340
    value += strlen(lang) + 1;
 
341
 
 
342
    if (*value == '\0') {
 
343
        /* the property is an empty value */
 
344
        if (*name == ':') {
 
345
            /* "no namespace" case */
 
346
            s = apr_psprintf(pool, "<%s/>" DEBUG_CR, name+1);
 
347
        }
 
348
        else {
 
349
            s = apr_psprintf(pool, "<ns%s/>" DEBUG_CR, name);
 
350
        }
 
351
    }
 
352
    else if (*lang != '\0') {
 
353
        if (*name == ':') {
 
354
            /* "no namespace" case */
 
355
            s = apr_psprintf(pool, "<%s xml:lang=\"%s\">%s</%s>" DEBUG_CR,
 
356
                             name+1, lang, value, name+1);
 
357
        }
 
358
        else {
 
359
            s = apr_psprintf(pool, "<ns%s xml:lang=\"%s\">%s</ns%s>" DEBUG_CR,
 
360
                             name, lang, value, name);
 
361
        }
 
362
    }
 
363
    else if (*name == ':') {
 
364
        /* "no namespace" case */
 
365
        s = apr_psprintf(pool, "<%s>%s</%s>" DEBUG_CR, name+1, value, name+1);
 
366
    }
 
367
    else {
 
368
        s = apr_psprintf(pool, "<ns%s>%s</ns%s>" DEBUG_CR, name, value, name);
 
369
    }
 
370
 
 
371
    apr_text_append(pool, phdr, s);
 
372
}
 
373
 
 
374
static dav_error * dav_propdb_open(apr_pool_t *pool,
 
375
                                   const dav_resource *resource, int ro,
 
376
                                   dav_db **pdb)
 
377
{
 
378
    dav_db *db;
 
379
    dav_error *err;
 
380
    apr_datum_t key;
 
381
    apr_datum_t value = { 0 };
 
382
 
 
383
    *pdb = NULL;
 
384
 
 
385
    /*
 
386
    ** Return if an error occurred, or there is no database.
 
387
    **
 
388
    ** NOTE: db could be NULL if we attempted to open a readonly
 
389
    **       database that doesn't exist. If we require read/write
 
390
    **       access, then a database was created and opened.
 
391
    */
 
392
    if ((err = dav_dbm_open(pool, resource, ro, &db)) != NULL
 
393
        || db == NULL)
 
394
        return err;
 
395
 
 
396
    db->uri_index = apr_hash_make(pool);
 
397
 
 
398
    key.dptr = DAV_GDBM_NS_KEY;
 
399
    key.dsize = DAV_GDBM_NS_KEY_LEN;
 
400
    if ((err = dav_dbm_fetch(db, key, &value)) != NULL) {
 
401
        /* ### push a higher-level description? */
 
402
        return err;
 
403
    }
 
404
 
 
405
    if (value.dptr == NULL) {
 
406
        dav_propdb_metadata m = {
 
407
            DAV_DBVSN_MAJOR, DAV_DBVSN_MINOR, 0
 
408
        };
 
409
 
 
410
        /*
 
411
        ** If there is no METADATA key, then the database may be
 
412
        ** from versions 0.9.0 .. 0.9.4 (which would be incompatible).
 
413
        ** These can be identified by the presence of an NS_TABLE entry.
 
414
        */
 
415
        key.dptr = "NS_TABLE";
 
416
        key.dsize = 8;
 
417
        if (dav_dbm_exists(db, key)) {
 
418
            dav_dbm_close(db);
 
419
 
 
420
            /* call it a major version error */
 
421
            return dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR,
 
422
                                 DAV_ERR_PROP_BAD_MAJOR,
 
423
                                 "Prop database has the wrong major "
 
424
                                 "version number and cannot be used.");
 
425
        }
 
426
 
 
427
        /* initialize a new metadata structure */
 
428
        dav_set_bufsize(pool, &db->ns_table, sizeof(m));
 
429
        memcpy(db->ns_table.buf, &m, sizeof(m));
 
430
    }
 
431
    else {
 
432
        dav_propdb_metadata m;
 
433
        long ns;
 
434
        const char *uri;
 
435
 
 
436
        dav_set_bufsize(pool, &db->ns_table, value.dsize);
 
437
        memcpy(db->ns_table.buf, value.dptr, value.dsize);
 
438
 
 
439
        memcpy(&m, value.dptr, sizeof(m));
 
440
        if (m.major != DAV_DBVSN_MAJOR) {
 
441
            dav_dbm_close(db);
 
442
 
 
443
            return dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR,
 
444
                                 DAV_ERR_PROP_BAD_MAJOR,
 
445
                                 "Prop database has the wrong major "
 
446
                                 "version number and cannot be used.");
 
447
        }
 
448
        db->version = m.minor;
 
449
        db->ns_count = ntohs(m.ns_count);
 
450
 
 
451
        dav_dbm_freedatum(db, value);
 
452
 
 
453
        /* create db->uri_index */
 
454
        for (ns = 0, uri = db->ns_table.buf + sizeof(dav_propdb_metadata);
 
455
             ns++ < db->ns_count;
 
456
             uri += strlen(uri) + 1) {
 
457
 
 
458
            /* we must copy the key, in case ns_table.buf moves */
 
459
            apr_hash_set(db->uri_index,
 
460
                         apr_pstrdup(pool, uri), APR_HASH_KEY_STRING,
 
461
                         (void *)ns);
 
462
        }
 
463
    }
 
464
 
 
465
    *pdb = db;
 
466
    return NULL;
 
467
}
 
468
 
 
469
static void dav_propdb_close(dav_db *db)
 
470
{
 
471
 
 
472
    if (db->ns_table_dirty) {
 
473
        dav_propdb_metadata m;
 
474
        apr_datum_t key;
 
475
        apr_datum_t value;
 
476
        dav_error *err;
 
477
 
 
478
        key.dptr = DAV_GDBM_NS_KEY;
 
479
        key.dsize = DAV_GDBM_NS_KEY_LEN;
 
480
 
 
481
        value.dptr = db->ns_table.buf;
 
482
        value.dsize = db->ns_table.cur_len;
 
483
 
 
484
        /* fill in the metadata that we store into the prop db. */
 
485
        m.major = DAV_DBVSN_MAJOR;
 
486
        m.minor = db->version;          /* ### keep current minor version? */
 
487
        m.ns_count = htons(db->ns_count);
 
488
 
 
489
        memcpy(db->ns_table.buf, &m, sizeof(m));
 
490
 
 
491
        err = dav_dbm_store(db, key, value);
 
492
        /* ### what to do with the error? */
 
493
    }
 
494
 
 
495
    dav_dbm_close(db);
 
496
}
 
497
 
 
498
static dav_error * dav_propdb_define_namespaces(dav_db *db, dav_xmlns_info *xi)
 
499
{
 
500
    int ns;
 
501
    const char *uri = db->ns_table.buf + sizeof(dav_propdb_metadata);
 
502
 
 
503
    /* within the prop values, we use "ns%d" for prefixes... register them */
 
504
    for (ns = 0; ns < db->ns_count; ++ns, uri += strlen(uri) + 1) {
 
505
 
 
506
        /* Empty URIs signify the empty namespace. These do not get a
 
507
           namespace prefix. when we generate the value, we will simply
 
508
           leave off the prefix, which is defined by mod_dav to be the
 
509
           empty namespace. */
 
510
        if (*uri == '\0')
 
511
            continue;
 
512
 
 
513
        /* ns_table.buf can move, so copy its value (we want the values to
 
514
           last as long as the provided dav_xmlns_info). */
 
515
        dav_xmlns_add(xi,
 
516
                      apr_psprintf(xi->pool, "ns%d", ns),
 
517
                      apr_pstrdup(xi->pool, uri));
 
518
    }
 
519
 
 
520
    return NULL;
 
521
}
 
522
 
 
523
static dav_error * dav_propdb_output_value(dav_db *db,
 
524
                                           const dav_prop_name *name,
 
525
                                           dav_xmlns_info *xi,
 
526
                                           apr_text_header *phdr,
 
527
                                           int *found)
 
528
{
 
529
    apr_datum_t key = dav_build_key(db, name);
 
530
    apr_datum_t value;
 
531
    dav_error *err;
 
532
 
 
533
    if ((err = dav_dbm_fetch(db, key, &value)) != NULL)
 
534
        return err;
 
535
    if (value.dptr == NULL) {
 
536
        *found = 0;
 
537
        return NULL;
 
538
    }
 
539
    *found = 1;
 
540
 
 
541
    dav_append_prop(db->pool, key.dptr, value.dptr, phdr);
 
542
 
 
543
    dav_dbm_freedatum(db, value);
 
544
 
 
545
    return NULL;
 
546
}
 
547
 
 
548
static dav_error * dav_propdb_map_namespaces(
 
549
    dav_db *db,
 
550
    const apr_array_header_t *namespaces,
 
551
    dav_namespace_map **mapping)
 
552
{
 
553
    dav_namespace_map *m = apr_palloc(db->pool, sizeof(*m));
 
554
    int i;
 
555
    int *pmap;
 
556
    const char **puri;
 
557
 
 
558
    /*
 
559
    ** Iterate over the provided namespaces. If a namespace already appears
 
560
    ** in our internal map of URI -> ns_id, then store that in the map. If
 
561
    ** we don't know the namespace yet, then add it to the map and to our
 
562
    ** table of known namespaces.
 
563
    */
 
564
    m->ns_map = pmap = apr_palloc(db->pool, namespaces->nelts * sizeof(*pmap));
 
565
    for (i = namespaces->nelts, puri = (const char **)namespaces->elts;
 
566
         i-- > 0;
 
567
         ++puri, ++pmap) {
 
568
 
 
569
        const char *uri = *puri;
 
570
        apr_size_t uri_len = strlen(uri);
 
571
        long ns_id = (long)apr_hash_get(db->uri_index, uri, uri_len);
 
572
 
 
573
        if (ns_id == 0) {
 
574
            dav_check_bufsize(db->pool, &db->ns_table, uri_len + 1);
 
575
            memcpy(db->ns_table.buf + db->ns_table.cur_len, uri, uri_len + 1);
 
576
            db->ns_table.cur_len += uri_len + 1;
 
577
 
 
578
            /* copy the uri in case the passed-in namespaces changes in
 
579
               some way. */
 
580
            apr_hash_set(db->uri_index, apr_pstrdup(db->pool, uri), uri_len,
 
581
                         (void *)((long)(db->ns_count + 1)));
 
582
 
 
583
            db->ns_table_dirty = 1;
 
584
 
 
585
            *pmap = db->ns_count++;
 
586
        }
 
587
        else {
 
588
            *pmap = ns_id - 1;
 
589
        }
 
590
    }
 
591
 
 
592
    *mapping = m;
 
593
    return NULL;
 
594
}
 
595
 
 
596
static dav_error * dav_propdb_store(dav_db *db, const dav_prop_name *name,
 
597
                                    const apr_xml_elem *elem,
 
598
                                    dav_namespace_map *mapping)
 
599
{
 
600
    apr_datum_t key = dav_build_key(db, name);
 
601
    apr_datum_t value;
 
602
 
 
603
    /* Note: mapping->ns_map was set up in dav_propdb_map_namespaces() */
 
604
 
 
605
    /* ### use a db- subpool for these values? clear on exit? */
 
606
 
 
607
    /* quote all the values in the element */
 
608
    /* ### be nice to do this without affecting the element itself */
 
609
    /* ### of course, the cast indicates Badness is occurring here */
 
610
    apr_xml_quote_elem(db->pool, (apr_xml_elem *)elem);
 
611
 
 
612
    /* generate a text blob for the xml:lang plus the contents */
 
613
    apr_xml_to_text(db->pool, elem, APR_XML_X2T_LANG_INNER, NULL,
 
614
                    mapping->ns_map,
 
615
                    (const char **)&value.dptr, &value.dsize);
 
616
 
 
617
    return dav_dbm_store(db, key, value);
 
618
}
 
619
 
 
620
static dav_error * dav_propdb_remove(dav_db *db, const dav_prop_name *name)
 
621
{
 
622
    apr_datum_t key = dav_build_key(db, name);
 
623
    return dav_dbm_delete(db, key);
 
624
}
 
625
 
 
626
static int dav_propdb_exists(dav_db *db, const dav_prop_name *name)
 
627
{
 
628
    apr_datum_t key = dav_build_key(db, name);
 
629
    return dav_dbm_exists(db, key);
 
630
}
 
631
 
 
632
static const char *dav_get_ns_table_uri(dav_db *db, int ns_id)
 
633
{
 
634
    const char *p = db->ns_table.buf + sizeof(dav_propdb_metadata);
 
635
 
 
636
    while (ns_id--)
 
637
        p += strlen(p) + 1;
 
638
 
 
639
    return p;
 
640
}
 
641
 
 
642
static void dav_set_name(dav_db *db, dav_prop_name *pname)
 
643
{
 
644
    const char *s = db->iter.dptr;
 
645
 
 
646
    if (s == NULL) {
 
647
        pname->ns = pname->name = NULL;
 
648
    }
 
649
    else if (*s == ':') {
 
650
        pname->ns = "";
 
651
        pname->name = s + 1;
 
652
    }
 
653
    else {
 
654
        int id = atoi(s);
 
655
 
 
656
        pname->ns = dav_get_ns_table_uri(db, id);
 
657
        if (s[1] == ':') {
 
658
            pname->name = s + 2;
 
659
        }
 
660
        else {
 
661
            pname->name = ap_strchr_c(s + 2, ':') + 1;
 
662
        }
 
663
    }
 
664
}
 
665
 
 
666
static dav_error * dav_propdb_next_name(dav_db *db, dav_prop_name *pname)
 
667
{
 
668
    dav_error *err;
 
669
 
 
670
    /* free the previous key. note: if the loop is aborted, then the DBM
 
671
       will toss the key (via pool cleanup) */
 
672
    if (db->iter.dptr != NULL)
 
673
        dav_dbm_freedatum(db, db->iter);
 
674
 
 
675
    if ((err = dav_dbm_nextkey(db, &db->iter)) != NULL)
 
676
        return err;
 
677
 
 
678
    /* skip past the METADATA key */
 
679
    if (db->iter.dptr != NULL && *db->iter.dptr == 'M')
 
680
        return dav_propdb_next_name(db, pname);
 
681
 
 
682
    dav_set_name(db, pname);
 
683
    return NULL;
 
684
}
 
685
 
 
686
static dav_error * dav_propdb_first_name(dav_db *db, dav_prop_name *pname)
 
687
{
 
688
    dav_error *err;
 
689
 
 
690
    if ((err = dav_dbm_firstkey(db, &db->iter)) != NULL)
 
691
        return err;
 
692
 
 
693
    /* skip past the METADATA key */
 
694
    if (db->iter.dptr != NULL && *db->iter.dptr == 'M')
 
695
        return dav_propdb_next_name(db, pname);
 
696
 
 
697
    dav_set_name(db, pname);
 
698
    return NULL;
 
699
}
 
700
 
 
701
static dav_error * dav_propdb_get_rollback(dav_db *db,
 
702
                                           const dav_prop_name *name,
 
703
                                           dav_deadprop_rollback **prollback)
 
704
{
 
705
    dav_deadprop_rollback *rb = apr_pcalloc(db->pool, sizeof(*rb));
 
706
    apr_datum_t key;
 
707
    apr_datum_t value;
 
708
    dav_error *err;
 
709
 
 
710
    key = dav_build_key(db, name);
 
711
    rb->key.dptr = apr_pstrdup(db->pool, key.dptr);
 
712
    rb->key.dsize = key.dsize;
 
713
 
 
714
    if ((err = dav_dbm_fetch(db, key, &value)) != NULL)
 
715
        return err;
 
716
    if (value.dptr != NULL) {
 
717
        rb->value.dptr = apr_pmemdup(db->pool, value.dptr, value.dsize);
 
718
        rb->value.dsize = value.dsize;
 
719
    }
 
720
 
 
721
    *prollback = rb;
 
722
    return NULL;
 
723
}
 
724
 
 
725
static dav_error * dav_propdb_apply_rollback(dav_db *db,
 
726
                                             dav_deadprop_rollback *rollback)
 
727
{
 
728
    if (rollback->value.dptr == NULL) {
 
729
        /* don't fail if the thing isn't really there. */
 
730
        (void) dav_dbm_delete(db, rollback->key);
 
731
        return NULL;
 
732
    }
 
733
 
 
734
    return dav_dbm_store(db, rollback->key, rollback->value);
 
735
}
 
736
 
 
737
const dav_hooks_db dav_hooks_db_dbm =
 
738
{
 
739
    dav_propdb_open,
 
740
    dav_propdb_close,
 
741
    dav_propdb_define_namespaces,
 
742
    dav_propdb_output_value,
 
743
    dav_propdb_map_namespaces,
 
744
    dav_propdb_store,
 
745
    dav_propdb_remove,
 
746
    dav_propdb_exists,
 
747
    dav_propdb_first_name,
 
748
    dav_propdb_next_name,
 
749
    dav_propdb_get_rollback,
 
750
    dav_propdb_apply_rollback,
 
751
 
 
752
    NULL /* ctx */
 
753
};