15e9cd1aeSAssar Westerlund /* 25e9cd1aeSAssar Westerlund * Copyright (c) 1997 - 2001 Kungliga Tekniska H�gskolan 35e9cd1aeSAssar Westerlund * (Royal Institute of Technology, Stockholm, Sweden). 45e9cd1aeSAssar Westerlund * All rights reserved. 55e9cd1aeSAssar Westerlund * 65e9cd1aeSAssar Westerlund * Redistribution and use in source and binary forms, with or without 75e9cd1aeSAssar Westerlund * modification, are permitted provided that the following conditions 85e9cd1aeSAssar Westerlund * are met: 95e9cd1aeSAssar Westerlund * 105e9cd1aeSAssar Westerlund * 1. Redistributions of source code must retain the above copyright 115e9cd1aeSAssar Westerlund * notice, this list of conditions and the following disclaimer. 125e9cd1aeSAssar Westerlund * 135e9cd1aeSAssar Westerlund * 2. Redistributions in binary form must reproduce the above copyright 145e9cd1aeSAssar Westerlund * notice, this list of conditions and the following disclaimer in the 155e9cd1aeSAssar Westerlund * documentation and/or other materials provided with the distribution. 165e9cd1aeSAssar Westerlund * 175e9cd1aeSAssar Westerlund * 3. Neither the name of the Institute nor the names of its contributors 185e9cd1aeSAssar Westerlund * may be used to endorse or promote products derived from this software 195e9cd1aeSAssar Westerlund * without specific prior written permission. 205e9cd1aeSAssar Westerlund * 215e9cd1aeSAssar Westerlund * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 225e9cd1aeSAssar Westerlund * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 235e9cd1aeSAssar Westerlund * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 245e9cd1aeSAssar Westerlund * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 255e9cd1aeSAssar Westerlund * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 265e9cd1aeSAssar Westerlund * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 275e9cd1aeSAssar Westerlund * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 285e9cd1aeSAssar Westerlund * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 295e9cd1aeSAssar Westerlund * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 305e9cd1aeSAssar Westerlund * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 315e9cd1aeSAssar Westerlund * SUCH DAMAGE. 325e9cd1aeSAssar Westerlund */ 335e9cd1aeSAssar Westerlund 345e9cd1aeSAssar Westerlund #include "hdb_locl.h" 355e9cd1aeSAssar Westerlund 361c43270aSJacques Vidrine RCSID("$Id: db3.c,v 1.8.6.1 2003/08/29 16:59:39 lha Exp $"); 375e9cd1aeSAssar Westerlund 384137ff4cSJacques Vidrine #if HAVE_DB3 394137ff4cSJacques Vidrine 401c43270aSJacques Vidrine #ifdef HAVE_DB4_DB_H 411c43270aSJacques Vidrine #include <db4/db.h> 421c43270aSJacques Vidrine #elif defined(HAVE_DB3_DB_H) 431c43270aSJacques Vidrine #include <db3/db.h> 441c43270aSJacques Vidrine #else 454137ff4cSJacques Vidrine #include <db.h> 461c43270aSJacques Vidrine #endif 474137ff4cSJacques Vidrine 485e9cd1aeSAssar Westerlund static krb5_error_code 495e9cd1aeSAssar Westerlund DB_close(krb5_context context, HDB *db) 505e9cd1aeSAssar Westerlund { 515e9cd1aeSAssar Westerlund DB *d = (DB*)db->db; 525e9cd1aeSAssar Westerlund DBC *dbcp = (DBC*)db->dbc; 535e9cd1aeSAssar Westerlund 545e9cd1aeSAssar Westerlund dbcp->c_close(dbcp); 555e9cd1aeSAssar Westerlund db->dbc = 0; 565e9cd1aeSAssar Westerlund d->close(d, 0); 575e9cd1aeSAssar Westerlund return 0; 585e9cd1aeSAssar Westerlund } 595e9cd1aeSAssar Westerlund 605e9cd1aeSAssar Westerlund static krb5_error_code 615e9cd1aeSAssar Westerlund DB_destroy(krb5_context context, HDB *db) 625e9cd1aeSAssar Westerlund { 635e9cd1aeSAssar Westerlund krb5_error_code ret; 645e9cd1aeSAssar Westerlund 655e9cd1aeSAssar Westerlund ret = hdb_clear_master_key (context, db); 665e9cd1aeSAssar Westerlund free(db->name); 675e9cd1aeSAssar Westerlund free(db); 685e9cd1aeSAssar Westerlund return ret; 695e9cd1aeSAssar Westerlund } 705e9cd1aeSAssar Westerlund 715e9cd1aeSAssar Westerlund static krb5_error_code 725e9cd1aeSAssar Westerlund DB_lock(krb5_context context, HDB *db, int operation) 735e9cd1aeSAssar Westerlund { 745e9cd1aeSAssar Westerlund DB *d = (DB*)db->db; 755e9cd1aeSAssar Westerlund int fd; 765e9cd1aeSAssar Westerlund if ((*d->fd)(d, &fd)) 775e9cd1aeSAssar Westerlund return HDB_ERR_CANT_LOCK_DB; 785e9cd1aeSAssar Westerlund return hdb_lock(fd, operation); 795e9cd1aeSAssar Westerlund } 805e9cd1aeSAssar Westerlund 815e9cd1aeSAssar Westerlund static krb5_error_code 825e9cd1aeSAssar Westerlund DB_unlock(krb5_context context, HDB *db) 835e9cd1aeSAssar Westerlund { 845e9cd1aeSAssar Westerlund DB *d = (DB*)db->db; 855e9cd1aeSAssar Westerlund int fd; 865e9cd1aeSAssar Westerlund if ((*d->fd)(d, &fd)) 875e9cd1aeSAssar Westerlund return HDB_ERR_CANT_LOCK_DB; 885e9cd1aeSAssar Westerlund return hdb_unlock(fd); 895e9cd1aeSAssar Westerlund } 905e9cd1aeSAssar Westerlund 915e9cd1aeSAssar Westerlund 925e9cd1aeSAssar Westerlund static krb5_error_code 935e9cd1aeSAssar Westerlund DB_seq(krb5_context context, HDB *db, 945e9cd1aeSAssar Westerlund unsigned flags, hdb_entry *entry, int flag) 955e9cd1aeSAssar Westerlund { 965e9cd1aeSAssar Westerlund DBT key, value; 975e9cd1aeSAssar Westerlund DBC *dbcp = db->dbc; 985e9cd1aeSAssar Westerlund krb5_data key_data, data; 995e9cd1aeSAssar Westerlund int code; 1005e9cd1aeSAssar Westerlund 1015e9cd1aeSAssar Westerlund memset(&key, 0, sizeof(DBT)); 1025e9cd1aeSAssar Westerlund memset(&value, 0, sizeof(DBT)); 1035e9cd1aeSAssar Westerlund if (db->lock(context, db, HDB_RLOCK)) 1045e9cd1aeSAssar Westerlund return HDB_ERR_DB_INUSE; 1055e9cd1aeSAssar Westerlund code = dbcp->c_get(dbcp, &key, &value, flag); 1065e9cd1aeSAssar Westerlund db->unlock(context, db); /* XXX check value */ 1075e9cd1aeSAssar Westerlund if (code == DB_NOTFOUND) 1085e9cd1aeSAssar Westerlund return HDB_ERR_NOENTRY; 1095e9cd1aeSAssar Westerlund if (code) 1105e9cd1aeSAssar Westerlund return code; 1115e9cd1aeSAssar Westerlund 1125e9cd1aeSAssar Westerlund key_data.data = key.data; 1135e9cd1aeSAssar Westerlund key_data.length = key.size; 1145e9cd1aeSAssar Westerlund data.data = value.data; 1155e9cd1aeSAssar Westerlund data.length = value.size; 1165e9cd1aeSAssar Westerlund if (hdb_value2entry(context, &data, entry)) 1175e9cd1aeSAssar Westerlund return DB_seq(context, db, flags, entry, DB_NEXT); 1185e9cd1aeSAssar Westerlund if (db->master_key_set && (flags & HDB_F_DECRYPT)) { 1195e9cd1aeSAssar Westerlund code = hdb_unseal_keys (context, db, entry); 1205e9cd1aeSAssar Westerlund if (code) 1215e9cd1aeSAssar Westerlund hdb_free_entry (context, entry); 1225e9cd1aeSAssar Westerlund } 1235e9cd1aeSAssar Westerlund if (entry->principal == NULL) { 1245e9cd1aeSAssar Westerlund entry->principal = malloc(sizeof(*entry->principal)); 1255e9cd1aeSAssar Westerlund if (entry->principal == NULL) { 1265e9cd1aeSAssar Westerlund hdb_free_entry (context, entry); 1274137ff4cSJacques Vidrine krb5_set_error_string(context, "malloc: out of memory"); 1284137ff4cSJacques Vidrine return ENOMEM; 1295e9cd1aeSAssar Westerlund } else { 1305e9cd1aeSAssar Westerlund hdb_key2principal(context, &key_data, entry->principal); 1315e9cd1aeSAssar Westerlund } 1325e9cd1aeSAssar Westerlund } 1335e9cd1aeSAssar Westerlund return 0; 1345e9cd1aeSAssar Westerlund } 1355e9cd1aeSAssar Westerlund 1365e9cd1aeSAssar Westerlund 1375e9cd1aeSAssar Westerlund static krb5_error_code 1385e9cd1aeSAssar Westerlund DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry) 1395e9cd1aeSAssar Westerlund { 1405e9cd1aeSAssar Westerlund return DB_seq(context, db, flags, entry, DB_FIRST); 1415e9cd1aeSAssar Westerlund } 1425e9cd1aeSAssar Westerlund 1435e9cd1aeSAssar Westerlund 1445e9cd1aeSAssar Westerlund static krb5_error_code 1455e9cd1aeSAssar Westerlund DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry) 1465e9cd1aeSAssar Westerlund { 1475e9cd1aeSAssar Westerlund return DB_seq(context, db, flags, entry, DB_NEXT); 1485e9cd1aeSAssar Westerlund } 1495e9cd1aeSAssar Westerlund 1505e9cd1aeSAssar Westerlund static krb5_error_code 1515e9cd1aeSAssar Westerlund DB_rename(krb5_context context, HDB *db, const char *new_name) 1525e9cd1aeSAssar Westerlund { 1535e9cd1aeSAssar Westerlund int ret; 1545e9cd1aeSAssar Westerlund char *old, *new; 1555e9cd1aeSAssar Westerlund 1565e9cd1aeSAssar Westerlund asprintf(&old, "%s.db", db->name); 1575e9cd1aeSAssar Westerlund asprintf(&new, "%s.db", new_name); 1585e9cd1aeSAssar Westerlund ret = rename(old, new); 1595e9cd1aeSAssar Westerlund free(old); 1605e9cd1aeSAssar Westerlund free(new); 1615e9cd1aeSAssar Westerlund if(ret) 1625e9cd1aeSAssar Westerlund return errno; 1635e9cd1aeSAssar Westerlund 1645e9cd1aeSAssar Westerlund free(db->name); 1655e9cd1aeSAssar Westerlund db->name = strdup(new_name); 1665e9cd1aeSAssar Westerlund return 0; 1675e9cd1aeSAssar Westerlund } 1685e9cd1aeSAssar Westerlund 1695e9cd1aeSAssar Westerlund static krb5_error_code 1705e9cd1aeSAssar Westerlund DB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply) 1715e9cd1aeSAssar Westerlund { 1725e9cd1aeSAssar Westerlund DB *d = (DB*)db->db; 1735e9cd1aeSAssar Westerlund DBT k, v; 1745e9cd1aeSAssar Westerlund int code; 1755e9cd1aeSAssar Westerlund 1765e9cd1aeSAssar Westerlund memset(&k, 0, sizeof(DBT)); 1775e9cd1aeSAssar Westerlund memset(&v, 0, sizeof(DBT)); 1785e9cd1aeSAssar Westerlund k.data = key.data; 1795e9cd1aeSAssar Westerlund k.size = key.length; 1805e9cd1aeSAssar Westerlund k.flags = 0; 1815e9cd1aeSAssar Westerlund if ((code = db->lock(context, db, HDB_RLOCK))) 1825e9cd1aeSAssar Westerlund return code; 1835e9cd1aeSAssar Westerlund code = d->get(d, NULL, &k, &v, 0); 1845e9cd1aeSAssar Westerlund db->unlock(context, db); 1855e9cd1aeSAssar Westerlund if(code == DB_NOTFOUND) 1865e9cd1aeSAssar Westerlund return HDB_ERR_NOENTRY; 1875e9cd1aeSAssar Westerlund if(code) 1885e9cd1aeSAssar Westerlund return code; 1895e9cd1aeSAssar Westerlund 1905e9cd1aeSAssar Westerlund krb5_data_copy(reply, v.data, v.size); 1915e9cd1aeSAssar Westerlund return 0; 1925e9cd1aeSAssar Westerlund } 1935e9cd1aeSAssar Westerlund 1945e9cd1aeSAssar Westerlund static krb5_error_code 1955e9cd1aeSAssar Westerlund DB__put(krb5_context context, HDB *db, int replace, 1965e9cd1aeSAssar Westerlund krb5_data key, krb5_data value) 1975e9cd1aeSAssar Westerlund { 1985e9cd1aeSAssar Westerlund DB *d = (DB*)db->db; 1995e9cd1aeSAssar Westerlund DBT k, v; 2005e9cd1aeSAssar Westerlund int code; 2015e9cd1aeSAssar Westerlund 2025e9cd1aeSAssar Westerlund memset(&k, 0, sizeof(DBT)); 2035e9cd1aeSAssar Westerlund memset(&v, 0, sizeof(DBT)); 2045e9cd1aeSAssar Westerlund k.data = key.data; 2055e9cd1aeSAssar Westerlund k.size = key.length; 2065e9cd1aeSAssar Westerlund k.flags = 0; 2075e9cd1aeSAssar Westerlund v.data = value.data; 2085e9cd1aeSAssar Westerlund v.size = value.length; 2095e9cd1aeSAssar Westerlund v.flags = 0; 2105e9cd1aeSAssar Westerlund if ((code = db->lock(context, db, HDB_WLOCK))) 2115e9cd1aeSAssar Westerlund return code; 2125e9cd1aeSAssar Westerlund code = d->put(d, NULL, &k, &v, replace ? 0 : DB_NOOVERWRITE); 2135e9cd1aeSAssar Westerlund db->unlock(context, db); 2145e9cd1aeSAssar Westerlund if(code == DB_KEYEXIST) 2155e9cd1aeSAssar Westerlund return HDB_ERR_EXISTS; 2165e9cd1aeSAssar Westerlund if(code) 2175e9cd1aeSAssar Westerlund return errno; 2185e9cd1aeSAssar Westerlund return 0; 2195e9cd1aeSAssar Westerlund } 2205e9cd1aeSAssar Westerlund 2215e9cd1aeSAssar Westerlund static krb5_error_code 2225e9cd1aeSAssar Westerlund DB__del(krb5_context context, HDB *db, krb5_data key) 2235e9cd1aeSAssar Westerlund { 2245e9cd1aeSAssar Westerlund DB *d = (DB*)db->db; 2255e9cd1aeSAssar Westerlund DBT k; 2265e9cd1aeSAssar Westerlund krb5_error_code code; 2275e9cd1aeSAssar Westerlund memset(&k, 0, sizeof(DBT)); 2285e9cd1aeSAssar Westerlund k.data = key.data; 2295e9cd1aeSAssar Westerlund k.size = key.length; 2305e9cd1aeSAssar Westerlund k.flags = 0; 2315e9cd1aeSAssar Westerlund code = db->lock(context, db, HDB_WLOCK); 2325e9cd1aeSAssar Westerlund if(code) 2335e9cd1aeSAssar Westerlund return code; 2345e9cd1aeSAssar Westerlund code = d->del(d, NULL, &k, 0); 2355e9cd1aeSAssar Westerlund db->unlock(context, db); 2365e9cd1aeSAssar Westerlund if(code == DB_NOTFOUND) 2375e9cd1aeSAssar Westerlund return HDB_ERR_NOENTRY; 2385e9cd1aeSAssar Westerlund if(code) 2395e9cd1aeSAssar Westerlund return code; 2405e9cd1aeSAssar Westerlund return 0; 2415e9cd1aeSAssar Westerlund } 2425e9cd1aeSAssar Westerlund 2435e9cd1aeSAssar Westerlund static krb5_error_code 2445e9cd1aeSAssar Westerlund DB_open(krb5_context context, HDB *db, int flags, mode_t mode) 2455e9cd1aeSAssar Westerlund { 2465e9cd1aeSAssar Westerlund char *fn; 2475e9cd1aeSAssar Westerlund krb5_error_code ret; 2485e9cd1aeSAssar Westerlund DB *d; 2495e9cd1aeSAssar Westerlund int myflags = 0; 2505e9cd1aeSAssar Westerlund 2515e9cd1aeSAssar Westerlund if (flags & O_CREAT) 2525e9cd1aeSAssar Westerlund myflags |= DB_CREATE; 2535e9cd1aeSAssar Westerlund 2545e9cd1aeSAssar Westerlund if (flags & O_EXCL) 2555e9cd1aeSAssar Westerlund myflags |= DB_EXCL; 2565e9cd1aeSAssar Westerlund 2575e9cd1aeSAssar Westerlund if (flags & O_RDONLY) 2585e9cd1aeSAssar Westerlund myflags |= DB_RDONLY; 2595e9cd1aeSAssar Westerlund 2605e9cd1aeSAssar Westerlund if (flags & O_TRUNC) 2615e9cd1aeSAssar Westerlund myflags |= DB_TRUNCATE; 2625e9cd1aeSAssar Westerlund 2635e9cd1aeSAssar Westerlund asprintf(&fn, "%s.db", db->name); 2644137ff4cSJacques Vidrine if (fn == NULL) { 2654137ff4cSJacques Vidrine krb5_set_error_string(context, "malloc: out of memory"); 2665e9cd1aeSAssar Westerlund return ENOMEM; 2674137ff4cSJacques Vidrine } 2685e9cd1aeSAssar Westerlund db_create(&d, NULL, 0); 2695e9cd1aeSAssar Westerlund db->db = d; 2701c43270aSJacques Vidrine #if (DB_VERSION_MAJOR > 3) && (DB_VERSION_MINOR > 0) 2711c43270aSJacques Vidrine if ((ret = d->open(db->db, NULL, fn, NULL, DB_BTREE, myflags, mode))) { 2721c43270aSJacques Vidrine #else 2735e9cd1aeSAssar Westerlund if ((ret = d->open(db->db, fn, NULL, DB_BTREE, myflags, mode))) { 2741c43270aSJacques Vidrine #endif 2755e9cd1aeSAssar Westerlund if(ret == ENOENT) 2765e9cd1aeSAssar Westerlund /* try to open without .db extension */ 2771c43270aSJacques Vidrine #if (DB_VERSION_MAJOR > 3) && (DB_VERSION_MINOR > 0) 2781c43270aSJacques Vidrine if (d->open(db->db, NULL, db->name, NULL, DB_BTREE, myflags, mode)) { 2791c43270aSJacques Vidrine #else 2805e9cd1aeSAssar Westerlund if (d->open(db->db, db->name, NULL, DB_BTREE, myflags, mode)) { 2811c43270aSJacques Vidrine #endif 2825e9cd1aeSAssar Westerlund free(fn); 2834137ff4cSJacques Vidrine krb5_set_error_string(context, "opening %s: %s", 2844137ff4cSJacques Vidrine db->name, strerror(ret)); 2855e9cd1aeSAssar Westerlund return ret; 2865e9cd1aeSAssar Westerlund } 2875e9cd1aeSAssar Westerlund } 2885e9cd1aeSAssar Westerlund free(fn); 2895e9cd1aeSAssar Westerlund 2905e9cd1aeSAssar Westerlund ret = d->cursor(d, NULL, (DBC **)&db->dbc, 0); 2914137ff4cSJacques Vidrine if (ret) { 2924137ff4cSJacques Vidrine krb5_set_error_string(context, "d->cursor: %s", strerror(ret)); 2935e9cd1aeSAssar Westerlund return ret; 2944137ff4cSJacques Vidrine } 2955e9cd1aeSAssar Westerlund 2965e9cd1aeSAssar Westerlund if((flags & O_ACCMODE) == O_RDONLY) 2975e9cd1aeSAssar Westerlund ret = hdb_check_db_format(context, db); 2985e9cd1aeSAssar Westerlund else 2995e9cd1aeSAssar Westerlund ret = hdb_init_db(context, db); 3005e9cd1aeSAssar Westerlund if(ret == HDB_ERR_NOENTRY) 3015e9cd1aeSAssar Westerlund return 0; 3025e9cd1aeSAssar Westerlund return ret; 3035e9cd1aeSAssar Westerlund } 3045e9cd1aeSAssar Westerlund 3055e9cd1aeSAssar Westerlund krb5_error_code 3065e9cd1aeSAssar Westerlund hdb_db_create(krb5_context context, HDB **db, 3075e9cd1aeSAssar Westerlund const char *filename) 3085e9cd1aeSAssar Westerlund { 3095e9cd1aeSAssar Westerlund *db = malloc(sizeof(**db)); 3104137ff4cSJacques Vidrine if (*db == NULL) { 3114137ff4cSJacques Vidrine krb5_set_error_string(context, "malloc: out of memory"); 3125e9cd1aeSAssar Westerlund return ENOMEM; 3134137ff4cSJacques Vidrine } 3145e9cd1aeSAssar Westerlund 3155e9cd1aeSAssar Westerlund (*db)->db = NULL; 3165e9cd1aeSAssar Westerlund (*db)->name = strdup(filename); 3174137ff4cSJacques Vidrine if ((*db)->name == NULL) { 3184137ff4cSJacques Vidrine krb5_set_error_string(context, "malloc: out of memory"); 3194137ff4cSJacques Vidrine free(*db); 3204137ff4cSJacques Vidrine *db = NULL; 3214137ff4cSJacques Vidrine return ENOMEM; 3224137ff4cSJacques Vidrine } 3235e9cd1aeSAssar Westerlund (*db)->master_key_set = 0; 3245e9cd1aeSAssar Westerlund (*db)->openp = 0; 3255e9cd1aeSAssar Westerlund (*db)->open = DB_open; 3265e9cd1aeSAssar Westerlund (*db)->close = DB_close; 3275e9cd1aeSAssar Westerlund (*db)->fetch = _hdb_fetch; 3285e9cd1aeSAssar Westerlund (*db)->store = _hdb_store; 3295e9cd1aeSAssar Westerlund (*db)->remove = _hdb_remove; 3305e9cd1aeSAssar Westerlund (*db)->firstkey = DB_firstkey; 3315e9cd1aeSAssar Westerlund (*db)->nextkey= DB_nextkey; 3325e9cd1aeSAssar Westerlund (*db)->lock = DB_lock; 3335e9cd1aeSAssar Westerlund (*db)->unlock = DB_unlock; 3345e9cd1aeSAssar Westerlund (*db)->rename = DB_rename; 3355e9cd1aeSAssar Westerlund (*db)->_get = DB__get; 3365e9cd1aeSAssar Westerlund (*db)->_put = DB__put; 3375e9cd1aeSAssar Westerlund (*db)->_del = DB__del; 3385e9cd1aeSAssar Westerlund (*db)->destroy = DB_destroy; 3395e9cd1aeSAssar Westerlund return 0; 3405e9cd1aeSAssar Westerlund } 3414137ff4cSJacques Vidrine #endif /* HAVE_DB3 */ 342