00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include "apr_strings.h"
00033 #include "apr_file_io.h"
00034
00035 #include "apr_dbm.h"
00036
00037 #define APR_WANT_BYTEFUNC
00038 #include "apr_want.h"
00039
00040 #include "mod_dav.h"
00041 #include "repos.h"
00042
00043
00044 struct dav_db {
00045 apr_pool_t *pool;
00046 apr_dbm_t *file;
00047
00048
00049
00050 int version;
00051
00052 dav_buffer ns_table;
00053 short ns_count;
00054 int ns_table_dirty;
00055 apr_hash_t *uri_index;
00056
00057 dav_buffer wb_key;
00058
00059 apr_datum_t iter;
00060 };
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070 void dav_dbm_get_statefiles(apr_pool_t *p, const char *fname,
00071 const char **state1, const char **state2)
00072 {
00073 if (fname == NULL)
00074 fname = DAV_FS_STATE_FILE_FOR_DIR;
00075
00076 apr_dbm_get_usednames(p, fname, state1, state2);
00077 }
00078
00079 static dav_error * dav_fs_dbm_error(dav_db *db, apr_pool_t *p,
00080 apr_status_t status)
00081 {
00082 int save_errno = errno;
00083 int errcode;
00084 const char *errstr;
00085 dav_error *err;
00086 char errbuf[200];
00087
00088 if (status == APR_SUCCESS)
00089 return NULL;
00090
00091 p = db ? db->pool : p;
00092
00093
00094 if (db == NULL) {
00095 errcode = 1;
00096 errstr = "Could not open property database.";
00097 }
00098 else {
00099 (void) apr_dbm_geterror(db->file, &errcode, errbuf, sizeof(errbuf));
00100 errstr = apr_pstrdup(p, errbuf);
00101 }
00102
00103 err = dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, errcode, errstr);
00104 err->save_errno = save_errno;
00105 return err;
00106 }
00107
00108
00109
00110 void dav_fs_ensure_state_dir(apr_pool_t * p, const char *dirname)
00111 {
00112 const char *pathname = apr_pstrcat(p, dirname, "/" DAV_FS_STATE_DIR, NULL);
00113
00114
00115
00116
00117 (void) apr_dir_make(pathname, APR_OS_DEFAULT, p);
00118 }
00119
00120
00121
00122
00123 dav_error * dav_dbm_open_direct(apr_pool_t *p, const char *pathname, int ro,
00124 dav_db **pdb)
00125 {
00126 apr_status_t status;
00127 apr_dbm_t *file;
00128
00129 *pdb = NULL;
00130
00131 if ((status = apr_dbm_open(&file, pathname,
00132 ro ? APR_DBM_READONLY : APR_DBM_RWCREATE,
00133 APR_OS_DEFAULT, p))
00134 != APR_SUCCESS
00135 && !ro) {
00136
00137
00138
00139
00140 return dav_fs_dbm_error(NULL, p, status);
00141 }
00142
00143
00144 if (file != NULL) {
00145
00146 *pdb = apr_pcalloc(p, sizeof(**pdb));
00147 (*pdb)->pool = p;
00148 (*pdb)->file = file;
00149 }
00150
00151 return NULL;
00152 }
00153
00154 static dav_error * dav_dbm_open(apr_pool_t * p, const dav_resource *resource,
00155 int ro, dav_db **pdb)
00156 {
00157 const char *dirpath;
00158 const char *fname;
00159 const char *pathname;
00160
00161
00162
00163 (void) dav_fs_dir_file_name(resource, &dirpath, &fname);
00164
00165
00166 if (!ro) {
00167
00168 dav_fs_ensure_state_dir(p, dirpath);
00169 }
00170
00171 pathname = apr_pstrcat(p, dirpath, "/" DAV_FS_STATE_DIR "/",
00172 fname ? fname : DAV_FS_STATE_FILE_FOR_DIR,
00173 NULL);
00174
00175
00176
00177
00178
00179
00180 return dav_dbm_open_direct(p, pathname, ro, pdb);
00181 }
00182
00183 void dav_dbm_close(dav_db *db)
00184 {
00185 apr_dbm_close(db->file);
00186 }
00187
00188 dav_error * dav_dbm_fetch(dav_db *db, apr_datum_t key, apr_datum_t *pvalue)
00189 {
00190 apr_status_t status = apr_dbm_fetch(db->file, key, pvalue);
00191
00192 return dav_fs_dbm_error(db, NULL, status);
00193 }
00194
00195 dav_error * dav_dbm_store(dav_db *db, apr_datum_t key, apr_datum_t value)
00196 {
00197 apr_status_t status = apr_dbm_store(db->file, key, value);
00198
00199 return dav_fs_dbm_error(db, NULL, status);
00200 }
00201
00202 dav_error * dav_dbm_delete(dav_db *db, apr_datum_t key)
00203 {
00204 apr_status_t status = apr_dbm_delete(db->file, key);
00205
00206 return dav_fs_dbm_error(db, NULL, status);
00207 }
00208
00209 int dav_dbm_exists(dav_db *db, apr_datum_t key)
00210 {
00211 return apr_dbm_exists(db->file, key);
00212 }
00213
00214 static dav_error * dav_dbm_firstkey(dav_db *db, apr_datum_t *pkey)
00215 {
00216 apr_status_t status = apr_dbm_firstkey(db->file, pkey);
00217
00218 return dav_fs_dbm_error(db, NULL, status);
00219 }
00220
00221 static dav_error * dav_dbm_nextkey(dav_db *db, apr_datum_t *pkey)
00222 {
00223 apr_status_t status = apr_dbm_nextkey(db->file, pkey);
00224
00225 return dav_fs_dbm_error(db, NULL, status);
00226 }
00227
00228 void dav_dbm_freedatum(dav_db *db, apr_datum_t data)
00229 {
00230 apr_dbm_freedatum(db->file, data);
00231 }
00232
00233
00234
00235
00236
00237
00238
00239 #define DAV_GDBM_NS_KEY "METADATA"
00240 #define DAV_GDBM_NS_KEY_LEN 8
00241
00242 typedef struct {
00243 unsigned char major;
00244 #define DAV_DBVSN_MAJOR 4
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268 unsigned char minor;
00269 #define DAV_DBVSN_MINOR 0
00270
00271 short ns_count;
00272
00273 } dav_propdb_metadata;
00274
00275 struct dav_deadprop_rollback {
00276 apr_datum_t key;
00277 apr_datum_t value;
00278 };
00279
00280 struct dav_namespace_map {
00281 int *ns_map;
00282 };
00283
00284
00285
00286
00287
00288
00289
00290
00291 static apr_datum_t dav_build_key(dav_db *db, const dav_prop_name *name)
00292 {
00293 char nsbuf[20];
00294 apr_size_t l_ns, l_name = strlen(name->name);
00295 apr_datum_t key = { 0 };
00296
00297
00298
00299
00300
00301
00302 if (*name->ns == '\0') {
00303 nsbuf[0] = '\0';
00304 l_ns = 0;
00305 }
00306 else {
00307 int ns_id = (int)apr_hash_get(db->uri_index, name->ns,
00308 APR_HASH_KEY_STRING);
00309
00310
00311 if (ns_id == 0) {
00312
00313 return key;
00314 }
00315
00316 l_ns = sprintf(nsbuf, "%d", ns_id - 1);
00317 }
00318
00319
00320 dav_set_bufsize(db->pool, &db->wb_key, l_ns + 1 + l_name + 1);
00321 memcpy(db->wb_key.buf, nsbuf, l_ns);
00322 db->wb_key.buf[l_ns] = ':';
00323 memcpy(&db->wb_key.buf[l_ns + 1], name->name, l_name + 1);
00324
00325
00326 key.dsize = l_ns + 1 + l_name + 1;
00327 key.dptr = db->wb_key.buf;
00328
00329 return key;
00330 }
00331
00332 static void dav_append_prop(apr_pool_t *pool,
00333 const char *name, const char *value,
00334 apr_text_header *phdr)
00335 {
00336 const char *s;
00337 const char *lang = value;
00338
00339
00340 value += strlen(lang) + 1;
00341
00342 if (*value == '\0') {
00343
00344 if (*name == ':') {
00345
00346 s = apr_psprintf(pool, "<%s/>" DEBUG_CR, name+1);
00347 }
00348 else {
00349 s = apr_psprintf(pool, "<ns%s/>" DEBUG_CR, name);
00350 }
00351 }
00352 else if (*lang != '\0') {
00353 if (*name == ':') {
00354
00355 s = apr_psprintf(pool, "<%s xml:lang=\"%s\">%s</%s>" DEBUG_CR,
00356 name+1, lang, value, name+1);
00357 }
00358 else {
00359 s = apr_psprintf(pool, "<ns%s xml:lang=\"%s\">%s</ns%s>" DEBUG_CR,
00360 name, lang, value, name);
00361 }
00362 }
00363 else if (*name == ':') {
00364
00365 s = apr_psprintf(pool, "<%s>%s</%s>" DEBUG_CR, name+1, value, name+1);
00366 }
00367 else {
00368 s = apr_psprintf(pool, "<ns%s>%s</ns%s>" DEBUG_CR, name, value, name);
00369 }
00370
00371 apr_text_append(pool, phdr, s);
00372 }
00373
00374 static dav_error * dav_propdb_open(apr_pool_t *pool,
00375 const dav_resource *resource, int ro,
00376 dav_db **pdb)
00377 {
00378 dav_db *db;
00379 dav_error *err;
00380 apr_datum_t key;
00381 apr_datum_t value = { 0 };
00382
00383 *pdb = NULL;
00384
00385
00386
00387
00388
00389
00390
00391
00392 if ((err = dav_dbm_open(pool, resource, ro, &db)) != NULL
00393 || db == NULL)
00394 return err;
00395
00396 db->uri_index = apr_hash_make(pool);
00397
00398 key.dptr = DAV_GDBM_NS_KEY;
00399 key.dsize = DAV_GDBM_NS_KEY_LEN;
00400 if ((err = dav_dbm_fetch(db, key, &value)) != NULL) {
00401
00402 return err;
00403 }
00404
00405 if (value.dptr == NULL) {
00406 dav_propdb_metadata m = {
00407 DAV_DBVSN_MAJOR, DAV_DBVSN_MINOR, 0
00408 };
00409
00410
00411
00412
00413
00414
00415 key.dptr = "NS_TABLE";
00416 key.dsize = 8;
00417 if (dav_dbm_exists(db, key)) {
00418 dav_dbm_close(db);
00419
00420
00421 return dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR,
00422 DAV_ERR_PROP_BAD_MAJOR,
00423 "Prop database has the wrong major "
00424 "version number and cannot be used.");
00425 }
00426
00427
00428 dav_set_bufsize(pool, &db->ns_table, sizeof(m));
00429 memcpy(db->ns_table.buf, &m, sizeof(m));
00430 }
00431 else {
00432 dav_propdb_metadata m;
00433 int ns;
00434 const char *uri;
00435
00436 dav_set_bufsize(pool, &db->ns_table, value.dsize);
00437 memcpy(db->ns_table.buf, value.dptr, value.dsize);
00438
00439 memcpy(&m, value.dptr, sizeof(m));
00440 if (m.major != DAV_DBVSN_MAJOR) {
00441 dav_dbm_close(db);
00442
00443 return dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR,
00444 DAV_ERR_PROP_BAD_MAJOR,
00445 "Prop database has the wrong major "
00446 "version number and cannot be used.");
00447 }
00448 db->version = m.minor;
00449 db->ns_count = ntohs(m.ns_count);
00450
00451 dav_dbm_freedatum(db, value);
00452
00453
00454 for (ns = 0, uri = db->ns_table.buf + sizeof(dav_propdb_metadata);
00455 ns++ < db->ns_count;
00456 uri += strlen(uri) + 1) {
00457
00458
00459 apr_hash_set(db->uri_index,
00460 apr_pstrdup(pool, uri), APR_HASH_KEY_STRING,
00461 (void *)ns);
00462 }
00463 }
00464
00465 *pdb = db;
00466 return NULL;
00467 }
00468
00469 static void dav_propdb_close(dav_db *db)
00470 {
00471
00472 if (db->ns_table_dirty) {
00473 dav_propdb_metadata m;
00474 apr_datum_t key;
00475 apr_datum_t value;
00476 dav_error *err;
00477
00478 key.dptr = DAV_GDBM_NS_KEY;
00479 key.dsize = DAV_GDBM_NS_KEY_LEN;
00480
00481 value.dptr = db->ns_table.buf;
00482 value.dsize = db->ns_table.cur_len;
00483
00484
00485 m.major = DAV_DBVSN_MAJOR;
00486 m.minor = db->version;
00487 m.ns_count = htons(db->ns_count);
00488
00489 memcpy(db->ns_table.buf, &m, sizeof(m));
00490
00491 err = dav_dbm_store(db, key, value);
00492
00493 }
00494
00495 dav_dbm_close(db);
00496 }
00497
00498 static dav_error * dav_propdb_define_namespaces(dav_db *db, dav_xmlns_info *xi)
00499 {
00500 int ns;
00501 const char *uri = db->ns_table.buf + sizeof(dav_propdb_metadata);
00502
00503
00504 for (ns = 0; ns < db->ns_count; ++ns, uri += strlen(uri) + 1) {
00505
00506
00507
00508
00509
00510 if (*uri == '\0')
00511 continue;
00512
00513
00514
00515 dav_xmlns_add(xi,
00516 apr_psprintf(xi->pool, "ns%d", ns),
00517 apr_pstrdup(xi->pool, uri));
00518 }
00519
00520 return NULL;
00521 }
00522
00523 static dav_error * dav_propdb_output_value(dav_db *db,
00524 const dav_prop_name *name,
00525 dav_xmlns_info *xi,
00526 apr_text_header *phdr,
00527 int *found)
00528 {
00529 apr_datum_t key = dav_build_key(db, name);
00530 apr_datum_t value;
00531 dav_error *err;
00532
00533 if ((err = dav_dbm_fetch(db, key, &value)) != NULL)
00534 return err;
00535 if (value.dptr == NULL) {
00536 *found = 0;
00537 return NULL;
00538 }
00539 *found = 1;
00540
00541 dav_append_prop(db->pool, key.dptr, value.dptr, phdr);
00542
00543 dav_dbm_freedatum(db, value);
00544
00545 return NULL;
00546 }
00547
00548 static dav_error * dav_propdb_map_namespaces(
00549 dav_db *db,
00550 const apr_array_header_t *namespaces,
00551 dav_namespace_map **mapping)
00552 {
00553 dav_namespace_map *m = apr_palloc(db->pool, sizeof(*m));
00554 int i;
00555 int *pmap;
00556 const char **puri;
00557
00558
00559
00560
00561
00562
00563
00564 m->ns_map = pmap = apr_palloc(db->pool, namespaces->nelts * sizeof(*pmap));
00565 for (i = namespaces->nelts, puri = (const char **)namespaces->elts;
00566 i-- > 0;
00567 ++puri, ++pmap) {
00568
00569 const char *uri = *puri;
00570 apr_size_t uri_len = strlen(uri);
00571 int ns_id = (int)apr_hash_get(db->uri_index, uri, uri_len);
00572
00573 if (ns_id == 0) {
00574 dav_check_bufsize(db->pool, &db->ns_table, uri_len + 1);
00575 memcpy(db->ns_table.buf + db->ns_table.cur_len, uri, uri_len + 1);
00576 db->ns_table.cur_len += uri_len + 1;
00577
00578
00579
00580 apr_hash_set(db->uri_index, apr_pstrdup(db->pool, uri), uri_len,
00581 (void *)(db->ns_count + 1));
00582
00583 db->ns_table_dirty = 1;
00584
00585 *pmap = db->ns_count++;
00586 }
00587 else {
00588 *pmap = ns_id - 1;
00589 }
00590 }
00591
00592 *mapping = m;
00593 return NULL;
00594 }
00595
00596 static dav_error * dav_propdb_store(dav_db *db, const dav_prop_name *name,
00597 const apr_xml_elem *elem,
00598 dav_namespace_map *mapping)
00599 {
00600 apr_datum_t key = dav_build_key(db, name);
00601 apr_datum_t value;
00602
00603
00604
00605
00606
00607
00608
00609
00610 apr_xml_quote_elem(db->pool, (apr_xml_elem *)elem);
00611
00612
00613 apr_xml_to_text(db->pool, elem, APR_XML_X2T_LANG_INNER, NULL,
00614 mapping->ns_map,
00615 (const char **)&value.dptr, &value.dsize);
00616
00617 return dav_dbm_store(db, key, value);
00618 }
00619
00620 static dav_error * dav_propdb_remove(dav_db *db, const dav_prop_name *name)
00621 {
00622 apr_datum_t key = dav_build_key(db, name);
00623 return dav_dbm_delete(db, key);
00624 }
00625
00626 static int dav_propdb_exists(dav_db *db, const dav_prop_name *name)
00627 {
00628 apr_datum_t key = dav_build_key(db, name);
00629 return dav_dbm_exists(db, key);
00630 }
00631
00632 static const char *dav_get_ns_table_uri(dav_db *db, int ns_id)
00633 {
00634 const char *p = db->ns_table.buf + sizeof(dav_propdb_metadata);
00635
00636 while (ns_id--)
00637 p += strlen(p) + 1;
00638
00639 return p;
00640 }
00641
00642 static void dav_set_name(dav_db *db, dav_prop_name *pname)
00643 {
00644 const char *s = db->iter.dptr;
00645
00646 if (s == NULL) {
00647 pname->ns = pname->name = NULL;
00648 }
00649 else if (*s == ':') {
00650 pname->ns = "";
00651 pname->name = s + 1;
00652 }
00653 else {
00654 int id = atoi(s);
00655
00656 pname->ns = dav_get_ns_table_uri(db, id);
00657 if (s[1] == ':') {
00658 pname->name = s + 2;
00659 }
00660 else {
00661 pname->name = ap_strchr_c(s + 2, ':') + 1;
00662 }
00663 }
00664 }
00665
00666 static dav_error * dav_propdb_next_name(dav_db *db, dav_prop_name *pname)
00667 {
00668 dav_error *err;
00669
00670
00671
00672 if (db->iter.dptr != NULL)
00673 dav_dbm_freedatum(db, db->iter);
00674
00675 if ((err = dav_dbm_nextkey(db, &db->iter)) != NULL)
00676 return err;
00677
00678
00679 if (db->iter.dptr != NULL && *db->iter.dptr == 'M')
00680 return dav_propdb_next_name(db, pname);
00681
00682 dav_set_name(db, pname);
00683 return NULL;
00684 }
00685
00686 static dav_error * dav_propdb_first_name(dav_db *db, dav_prop_name *pname)
00687 {
00688 dav_error *err;
00689
00690 if ((err = dav_dbm_firstkey(db, &db->iter)) != NULL)
00691 return err;
00692
00693
00694 if (db->iter.dptr != NULL && *db->iter.dptr == 'M')
00695 return dav_propdb_next_name(db, pname);
00696
00697 dav_set_name(db, pname);
00698 return NULL;
00699 }
00700
00701 static dav_error * dav_propdb_get_rollback(dav_db *db,
00702 const dav_prop_name *name,
00703 dav_deadprop_rollback **prollback)
00704 {
00705 dav_deadprop_rollback *rb = apr_pcalloc(db->pool, sizeof(*rb));
00706 apr_datum_t key;
00707 apr_datum_t value;
00708 dav_error *err;
00709
00710 key = dav_build_key(db, name);
00711 rb->key.dptr = apr_pstrdup(db->pool, key.dptr);
00712 rb->key.dsize = key.dsize;
00713
00714 if ((err = dav_dbm_fetch(db, key, &value)) != NULL)
00715 return err;
00716 if (value.dptr != NULL) {
00717 rb->value.dptr = apr_pmemdup(db->pool, value.dptr, value.dsize);
00718 rb->value.dsize = value.dsize;
00719 }
00720
00721 *prollback = rb;
00722 return NULL;
00723 }
00724
00725 static dav_error * dav_propdb_apply_rollback(dav_db *db,
00726 dav_deadprop_rollback *rollback)
00727 {
00728 if (rollback->value.dptr == NULL) {
00729
00730 (void) dav_dbm_delete(db, rollback->key);
00731 return NULL;
00732 }
00733
00734 return dav_dbm_store(db, rollback->key, rollback->value);
00735 }
00736
00737 const dav_hooks_db dav_hooks_db_dbm =
00738 {
00739 dav_propdb_open,
00740 dav_propdb_close,
00741 dav_propdb_define_namespaces,
00742 dav_propdb_output_value,
00743 dav_propdb_map_namespaces,
00744 dav_propdb_store,
00745 dav_propdb_remove,
00746 dav_propdb_exists,
00747 dav_propdb_first_name,
00748 dav_propdb_next_name,
00749 dav_propdb_get_rollback,
00750 dav_propdb_apply_rollback,
00751
00752 NULL
00753 };