]> the.earth.li Git - onak.git/blob - keydb_db4.c
Add support for displaying EC/ECDSA key types
[onak.git] / keydb_db4.c
1 /*
2  * keydb_db4.c - Routines to store and fetch keys in a DB4 database.
3  *
4  * Copyright 2002-2008 Jonathan McDowell <noodles@earth.li>
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, write to the Free Software Foundation, Inc., 51
17  * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/uio.h>
23 #include <ctype.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include <db.h>
32
33 #include "charfuncs.h"
34 #include "keyarray.h"
35 #include "keydb.h"
36 #include "keyid.h"
37 #include "decodekey.h"
38 #include "keystructs.h"
39 #include "mem.h"
40 #include "log.h"
41 #include "onak-conf.h"
42 #include "parsekey.h"
43 #include "wordlist.h"
44
45 #define DB4_UPGRADE_FILE "db_upgrade.lck"
46
47 /**
48  *      dbenv - our database environment.
49  */
50 static DB_ENV *dbenv = NULL;
51
52 /**
53  *      numdb - The number of database files we have.
54  */
55 static int numdbs = 16;
56
57 /**
58  *      dbconn - our connections to the key database files.
59  */
60 static DB **dbconns = NULL;
61
62 /**
63  *      worddb - our connection to the word database.
64  */
65 static DB *worddb = NULL;
66
67 /**
68  *      id32db - our connection to the 32bit ID database.
69  */
70 static DB *id32db = NULL;
71
72 /**
73  *      skshashdb - our connection to the SKS hash database.
74  */
75 static DB *skshashdb = NULL;
76
77 /**
78  *      subkeydb - our connection to the subkey ID lookup database.
79  */
80 static DB *subkeydb = NULL;
81
82 /**
83  *      txn - our current transaction id.
84  */
85 static DB_TXN *txn = NULL;
86
87 DB *keydb(uint64_t keyid)
88 {
89         uint64_t keytrun;
90
91         keytrun = keyid >> 8;
92
93         return(dbconns[keytrun % numdbs]);
94 }
95
96 /**
97  *      db4_errfunc - Direct DB errors to logfile
98  *
99  *      Basic function to take errors from the DB library and output them to
100  *      the logfile rather than stderr.
101  */
102 #if (DB_VERSION_MAJOR == 4) && (DB_VERSION_MINOR < 3)
103 static void db4_errfunc(const char *errpfx, const char *errmsg)
104 #else
105 static void db4_errfunc(const DB_ENV *edbenv, const char *errpfx,
106                 const char *errmsg)
107 #endif
108 {
109         if (errpfx) {
110                 logthing(LOGTHING_DEBUG, "db4 error: %s:%s", errpfx, errmsg);
111         } else {
112                 logthing(LOGTHING_DEBUG, "db4 error: %s", errmsg);
113         }
114
115         return;
116 }
117
118 /**
119  *      starttrans - Start a transaction.
120  *
121  *      Start a transaction. Intended to be used if we're about to perform many
122  *      operations on the database to help speed it all up, or if we want
123  *      something to only succeed if all relevant operations are successful.
124  */
125 static bool db4_starttrans(void)
126 {
127         int ret;
128
129         log_assert(dbenv != NULL);
130         log_assert(txn == NULL);
131
132         ret = dbenv->txn_begin(dbenv,
133                 NULL, /* No parent transaction */
134                 &txn,
135                 0);
136         if (ret != 0) {
137                 logthing(LOGTHING_CRITICAL,
138                                 "Error starting transaction: %s",
139                                 db_strerror(ret));
140                 exit(1);
141         }
142
143         return true;
144 }
145
146 /**
147  *      endtrans - End a transaction.
148  *
149  *      Ends a transaction.
150  */
151 static void db4_endtrans(void)
152 {
153         int ret;
154
155         log_assert(dbenv != NULL);
156         log_assert(txn != NULL);
157
158         ret = txn->commit(txn,
159                 0);
160         if (ret != 0) {
161                 logthing(LOGTHING_CRITICAL,
162                                 "Error ending transaction: %s",
163                                 db_strerror(ret));
164                 exit(1);
165         }
166         txn = NULL;
167
168         return;
169 }
170
171 /**
172  *      cleanupdb - De-initialize the key database.
173  *
174  *      This function should be called upon program exit to allow the DB to
175  *      cleanup after itself.
176  */
177 static void db4_cleanupdb(void)
178 {
179         int i = 0;
180
181         if (dbenv != NULL) {
182                 dbenv->txn_checkpoint(dbenv, 0, 0, 0);
183                 if (subkeydb != NULL) {
184                         subkeydb->close(subkeydb, 0);
185                         subkeydb = NULL;
186                 }
187                 if (skshashdb != NULL) {
188                         skshashdb->close(skshashdb, 0);
189                         skshashdb = NULL;
190                 }
191                 if (id32db != NULL) {
192                         id32db->close(id32db, 0);
193                         id32db = NULL;
194                 }
195                 if (worddb != NULL) {
196                         worddb->close(worddb, 0);
197                         worddb = NULL;
198                 }
199                 for (i = 0; i < numdbs; i++) {
200                         if (dbconns[i] != NULL) {
201                                 dbconns[i]->close(dbconns[i], 0);
202                                 dbconns[i] = NULL;
203                         }
204                 }
205                 free(dbconns);
206                 dbconns = NULL;
207                 dbenv->close(dbenv, 0);
208                 dbenv = NULL;
209         }
210 }
211
212 /**
213  *      db4_upgradedb - Upgrade a DB4 database
214  *
215  *      Called if we discover we need to upgrade our DB4 database; ie if
216  *      we're running with a newer version of db4 than the database was
217  *      created with.
218  */
219 static int db4_upgradedb(int numdb)
220 {
221         DB *curdb = NULL;
222         int ret;
223         int i;
224         char buf[1024];
225         int lockfile_fd;
226         struct stat statbuf;
227
228         snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir,
229                         DB4_UPGRADE_FILE);
230         lockfile_fd = open(buf, O_RDWR | O_CREAT | O_EXCL, 0600);
231         if (lockfile_fd < 0) {
232                 if (errno == EEXIST) {
233                         while (stat(buf, &statbuf) == 0) ;
234                         return 0;
235                 } else {
236                         logthing(LOGTHING_CRITICAL, "Couldn't open database "
237                                 "update lock file: %s", strerror(errno));
238                         return -1;
239                 }
240         }
241         snprintf(buf, sizeof(buf) - 1, "%d", getpid());
242         write(lockfile_fd, buf, strlen(buf));
243         close(lockfile_fd);
244
245         logthing(LOGTHING_NOTICE, "Upgrading DB4 database");
246         ret = db_env_create(&dbenv, 0);
247         dbenv->set_errcall(dbenv, &db4_errfunc);
248         dbenv->remove(dbenv, config.db_dir, 0);
249         dbenv = NULL;
250         for (i = 0; i < numdb; i++) {
251                 ret = db_create(&curdb, NULL, 0);
252                 if (ret == 0) {
253                         snprintf(buf, sizeof(buf) - 1, "%s/keydb.%d.db",
254                                 config.db_dir, i);
255                         logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
256                         ret = curdb->upgrade(curdb, buf, 0);
257                         curdb->close(curdb, 0);
258                 } else {
259                         logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
260                                 buf,
261                                 db_strerror(ret));
262                 }
263         }
264
265         ret = db_create(&curdb, NULL, 0);
266         if (ret == 0) {
267                 snprintf(buf, sizeof(buf) - 1, "%s/worddb", config.db_dir);
268                 logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
269                 ret = curdb->upgrade(curdb, buf, 0);
270                 curdb->close(curdb, 0);
271         } else {
272                 logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
273                         buf,
274                         db_strerror(ret));
275         }
276
277         ret = db_create(&curdb, NULL, 0);
278         if (ret == 0) {
279                 snprintf(buf, sizeof(buf) - 1, "%s/id32db", config.db_dir);
280                 logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
281                 ret = curdb->upgrade(curdb, buf, 0);
282                 curdb->close(curdb, 0);
283         } else {
284                 logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
285                         buf,
286                         db_strerror(ret));
287         }
288
289         ret = db_create(&curdb, NULL, 0);
290         if (ret == 0) {
291                 snprintf(buf, sizeof(buf) - 1, "%s/skshashdb", config.db_dir);
292                 logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
293                 ret = curdb->upgrade(curdb, buf, 0);
294                 curdb->close(curdb, 0);
295         } else {
296                 logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
297                         buf,
298                         db_strerror(ret));
299         }
300
301         ret = db_create(&curdb, NULL, 0);
302         if (ret == 0) {
303                 snprintf(buf, sizeof(buf) - 1, "%s/subkeydb", config.db_dir);
304                 logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
305                 ret = curdb->upgrade(curdb, buf, 0);
306                 curdb->close(curdb, 0);
307         } else {
308                 logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
309                         buf,
310                         db_strerror(ret));
311         }
312
313         snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir,
314                         DB4_UPGRADE_FILE);
315         unlink(buf);
316
317         return ret;
318 }
319
320 /**
321  *      initdb - Initialize the key database.
322  *
323  *      This function should be called before any of the other functions in
324  *      this file are called in order to allow the DB to be initialized ready
325  *      for access.
326  */
327 static void db4_initdb(bool readonly)
328 {
329         char       buf[1024];
330         FILE      *numdb = NULL;
331         int        ret = 0;
332         int        i = 0;
333         uint32_t   flags = 0;
334         struct stat statbuf;
335         int        maxlocks;
336
337         snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir,
338                         DB4_UPGRADE_FILE);
339         ret = stat(buf, &statbuf);
340         while ((ret == 0) || (errno != ENOENT)) {
341                 if (ret != 0) {
342                         logthing(LOGTHING_CRITICAL, "Couldn't stat upgrade "
343                                 "lock file: %s (%d)", strerror(errno), ret);
344                         exit(1);
345                 }
346                 logthing(LOGTHING_DEBUG, "DB4 upgrade in progress; waiting.");
347                 sleep(5);
348                 ret = stat(buf, &statbuf);
349         }
350         ret = 0;
351
352         snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", config.db_dir);
353         numdb = fopen(buf, "r");
354         if (numdb != NULL) {
355                 if (fgets(buf, sizeof(buf), numdb) != NULL) {
356                         numdbs = atoi(buf);
357                 }
358                 fclose(numdb);
359         } else if (!readonly) {
360                 logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s",
361                                 strerror(errno));
362                 numdb = fopen(buf, "w");
363                 if (numdb != NULL) {
364                         fprintf(numdb, "%d", numdbs);
365                         fclose(numdb);
366                 } else {
367                         logthing(LOGTHING_ERROR,
368                                 "Couldn't write num_keydb: %s",
369                                 strerror(errno));
370                 }
371         }
372
373         dbconns = calloc(numdbs, sizeof (DB *));
374         if (dbconns == NULL) {
375                 logthing(LOGTHING_CRITICAL,
376                                 "Couldn't allocate memory for dbconns");
377                 ret = 1;
378         }
379
380         if (ret == 0) {
381                 ret = db_env_create(&dbenv, 0);
382                 if (ret != 0) {
383                         logthing(LOGTHING_CRITICAL,
384                                 "db_env_create: %s", db_strerror(ret));
385                 }
386         }
387
388         /*
389          * Up the number of locks we're allowed at once. We base this on
390          * the maximum number of keys we're going to return.
391          */
392         maxlocks = config.maxkeys * 16;
393         if (maxlocks < 1000) {
394                 maxlocks = 1000;
395         }
396         dbenv->set_lk_max_locks(dbenv, maxlocks);
397         dbenv->set_lk_max_objects(dbenv, maxlocks);
398
399         /*
400          * Enable deadlock detection so that we don't block indefinitely on
401          * anything. What we really want is simple 2 state locks, but I'm not
402          * sure how to make the standard DB functions do that yet.
403          */
404         if (ret == 0) {
405                 dbenv->set_errcall(dbenv, &db4_errfunc);
406                 ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT);
407                 if (ret != 0) {
408                         logthing(LOGTHING_CRITICAL,
409                                 "db_env_create: %s", db_strerror(ret));
410                 }
411         }
412
413         if (ret == 0) {
414                 ret = dbenv->open(dbenv, config.db_dir,
415                                 DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
416                                 DB_INIT_TXN |
417                                 DB_CREATE,
418                                 0);
419 #ifdef DB_VERSION_MISMATCH
420                 if (ret == DB_VERSION_MISMATCH) {
421                         dbenv->close(dbenv, 0);
422                         dbenv = NULL;
423                         ret = db4_upgradedb(numdbs);
424                         if (ret == 0) {
425                                 ret = db_env_create(&dbenv, 0);
426                         }
427                         if (ret == 0) {
428                                 dbenv->set_errcall(dbenv, &db4_errfunc);
429                                 dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT);
430                                 ret = dbenv->open(dbenv, config.db_dir,
431                                         DB_INIT_LOG | DB_INIT_MPOOL |
432                                         DB_INIT_LOCK | DB_INIT_TXN |
433                                         DB_CREATE | DB_RECOVER,
434                                         0);
435
436                                 if (ret == 0) {
437                                         dbenv->txn_checkpoint(dbenv,
438                                                         0,
439                                                         0,
440                                                         DB_FORCE);
441                                 }
442                         }
443                 }
444 #endif
445                 if (ret != 0) {
446                         logthing(LOGTHING_CRITICAL,
447                                         "Error opening db environment: %s (%s)",
448                                         config.db_dir,
449                                         db_strerror(ret));
450                         dbenv->close(dbenv, 0);
451                         dbenv = NULL;
452                 }
453         }
454
455         if (ret == 0) {
456                 db4_starttrans();
457
458                 for (i = 0; !ret && i < numdbs; i++) {
459                         ret = db_create(&dbconns[i], dbenv, 0);
460                         if (ret != 0) {
461                                 logthing(LOGTHING_CRITICAL,
462                                         "db_create: %s", db_strerror(ret));
463                         }
464
465                         if (ret == 0) {
466                                 snprintf(buf, 1023, "keydb.%d.db", i);
467                                 flags = DB_CREATE;
468                                 if (readonly) {
469                                         flags = DB_RDONLY;
470                                 }
471                                 ret = dbconns[i]->open(dbconns[i],
472                                                 txn,
473                                                 buf,
474                                                 "keydb",
475                                                 DB_HASH,
476                                                 flags,
477                                                 0664);
478                                 if (ret != 0) {
479                                         logthing(LOGTHING_CRITICAL,
480                                                 "Error opening key database:"
481                                                 " %s (%s)",
482                                                 buf,
483                                                 db_strerror(ret));
484                                 }
485                         }
486                 }
487
488         }
489
490         if (ret == 0) {
491                 ret = db_create(&worddb, dbenv, 0);
492                 if (ret != 0) {
493                         logthing(LOGTHING_CRITICAL, "db_create: %s",
494                                         db_strerror(ret));
495                 }
496         }
497
498         if (ret == 0) {
499                 ret = worddb->set_flags(worddb, DB_DUP);
500         }
501
502         if (ret == 0) {
503                 ret = worddb->open(worddb, txn, "worddb", "worddb", DB_BTREE,
504                                 flags,
505                                 0664);
506                 if (ret != 0) {
507                         logthing(LOGTHING_CRITICAL,
508                                         "Error opening word database: %s (%s)",
509                                         "worddb",
510                                         db_strerror(ret));
511                 }
512         }
513
514         if (ret == 0) {
515                 ret = db_create(&id32db, dbenv, 0);
516                 if (ret != 0) {
517                         logthing(LOGTHING_CRITICAL, "db_create: %s",
518                                         db_strerror(ret));
519                 }
520         }
521
522         if (ret == 0) {
523                 ret = id32db->set_flags(id32db, DB_DUP);
524         }
525
526         if (ret == 0) {
527                 ret = id32db->open(id32db, txn, "id32db", "id32db", DB_HASH,
528                                 flags,
529                                 0664);
530                 if (ret != 0) {
531                         logthing(LOGTHING_CRITICAL,
532                                         "Error opening id32 database: %s (%s)",
533                                         "id32db",
534                                         db_strerror(ret));
535                 }
536         }
537
538         if (ret == 0) {
539                 ret = db_create(&skshashdb, dbenv, 0);
540                 if (ret != 0) {
541                         logthing(LOGTHING_CRITICAL, "db_create: %s",
542                                         db_strerror(ret));
543                 }
544         }
545
546         if (ret == 0) {
547                 ret = skshashdb->open(skshashdb, txn, "skshashdb",
548                                 "skshashdb", DB_HASH,
549                                 flags,
550                                 0664);
551                 if (ret != 0) {
552                         logthing(LOGTHING_CRITICAL,
553                                 "Error opening skshash database: %s (%s)",
554                                 "skshashdb",
555                                 db_strerror(ret));
556                 }
557         }
558
559         if (ret == 0) {
560                 ret = db_create(&subkeydb, dbenv, 0);
561                 if (ret != 0) {
562                         logthing(LOGTHING_CRITICAL, "db_create: %s",
563                                         db_strerror(ret));
564                 }
565         }
566
567         if (ret == 0) {
568                 ret = subkeydb->open(subkeydb, txn, "subkeydb", "subkeydb",
569                                 DB_HASH,
570                                 flags,
571                                 0664);
572                 if (ret != 0) {
573                         logthing(LOGTHING_CRITICAL,
574                                 "Error opening subkey database: %s (%s)",
575                                 "subkeydb",
576                                 db_strerror(ret));
577                 }
578         }
579
580         if (txn != NULL) {
581                 db4_endtrans();
582         }
583
584         if (ret != 0) {
585                 db4_cleanupdb();
586                 logthing(LOGTHING_CRITICAL,
587                                 "Error opening database; exiting");
588                 exit(EXIT_FAILURE);
589         }
590         
591         return;
592 }
593
594 /**
595  *      getfullkeyid - Maps a 32bit key id to a 64bit one.
596  *      @keyid: The 32bit keyid.
597  *
598  *      This function maps a 32bit key id to the full 64bit one. It returns the
599  *      full keyid. If the key isn't found a keyid of 0 is returned.
600  */
601 static uint64_t db4_getfullkeyid(uint64_t keyid)
602 {
603         DBT       key, data;
604         DBC      *cursor = NULL;
605         uint32_t  shortkeyid = 0;
606         int       ret = 0;
607
608         if (keyid < 0x100000000LL) {
609                 ret = id32db->cursor(id32db,
610                                 txn,
611                                 &cursor,
612                                 0);   /* flags */
613
614                 shortkeyid = keyid & 0xFFFFFFFF;
615
616                 memset(&key, 0, sizeof(key));
617                 memset(&data, 0, sizeof(data));
618                 key.data = &shortkeyid;
619                 key.size = sizeof(shortkeyid);
620                 data.flags = DB_DBT_MALLOC;
621
622                 ret = cursor->c_get(cursor,
623                         &key,
624                         &data,
625                         DB_SET);
626
627                 if (ret == 0) {
628                         keyid = *(uint64_t *) data.data;
629
630                         if (data.data != NULL) {
631                                 free(data.data);
632                                 data.data = NULL;
633                         }
634                 }
635
636                 ret = cursor->c_close(cursor);
637                 cursor = NULL;
638         }
639         
640         return keyid;
641 }
642
643 /**
644  *      fetch_key - Given a keyid fetch the key from storage.
645  *      @keyid: The keyid to fetch.
646  *      @publickey: A pointer to a structure to return the key in.
647  *      @intrans: If we're already in a transaction.
648  *
649  *      We use the hex representation of the keyid as the filename to fetch the
650  *      key from. The key is stored in the file as a binary OpenPGP stream of
651  *      packets, so we can just use read_openpgp_stream() to read the packets
652  *      in and then parse_keys() to parse the packets into a publickey
653  *      structure.
654  */
655 static int db4_fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
656                 bool intrans)
657 {
658         struct openpgp_packet_list *packets = NULL;
659         DBT key, data;
660         int ret = 0;
661         int numkeys = 0;
662         struct buffer_ctx fetchbuf;
663
664         if (keyid < 0x100000000LL) {
665                 keyid = db4_getfullkeyid(keyid);
666         }
667
668         memset(&key, 0, sizeof(key));
669         memset(&data, 0, sizeof(data));
670
671         data.size = 0;
672         data.data = NULL;
673
674         key.size = sizeof(keyid);
675         key.data = &keyid;
676
677         if (!intrans) {
678                 db4_starttrans();
679         }
680
681         ret = keydb(keyid)->get(keydb(keyid),
682                         txn,
683                         &key,
684                         &data,
685                         0); /* flags*/
686
687         if (ret == DB_NOTFOUND) {
688                 /* If we didn't find the key ID see if it's a subkey ID */
689                 memset(&key, 0, sizeof(key));
690                 memset(&data, 0, sizeof(data));
691                 data.size = 0;
692                 data.data = NULL;
693                 key.size = sizeof(keyid);
694                 key.data = &keyid;
695
696                 ret = subkeydb->get(subkeydb,
697                         txn,
698                         &key,
699                         &data,
700                         0); /* flags*/
701
702                 if (ret == 0) {
703                         /* We got a subkey match; retrieve the actual key */
704                         keyid = *(uint64_t *) data.data;
705
706                         memset(&key, 0, sizeof(key));
707                         memset(&data, 0, sizeof(data));
708                         data.size = 0;
709                         data.data = NULL;
710                         key.size = sizeof(keyid);
711                         key.data = &keyid;
712
713                         ret = keydb(keyid)->get(keydb(keyid),
714                                 txn,
715                                 &key,
716                                 &data,
717                                 0); /* flags*/
718                 }
719         }
720
721         if (ret == 0) {
722                 fetchbuf.buffer = data.data;
723                 fetchbuf.offset = 0;
724                 fetchbuf.size = data.size;
725                 read_openpgp_stream(buffer_fetchchar, &fetchbuf,
726                                 &packets, 0);
727                 parse_keys(packets, publickey);
728                 free_packet_list(packets);
729                 packets = NULL;
730                 numkeys++;
731         } else if (ret != DB_NOTFOUND) {
732                 logthing(LOGTHING_ERROR,
733                                 "Problem retrieving key: %s",
734                                 db_strerror(ret));
735         }
736
737         if (!intrans) {
738                 db4_endtrans();
739         }
740
741         return (numkeys);
742 }
743
744 int worddb_cmp(const void *d1, const void *d2)
745 {
746         return memcmp(d1, d2, 12);
747 }
748
749 /**
750  *      fetch_key_text - Trys to find the keys that contain the supplied text.
751  *      @search: The text to search for.
752  *      @publickey: A pointer to a structure to return the key in.
753  *
754  *      This function searches for the supplied text and returns the keys that
755  *      contain it.
756  */
757 static int db4_fetch_key_text(const char *search,
758                 struct openpgp_publickey **publickey)
759 {
760         DBC *cursor = NULL;
761         DBT key, data;
762         int ret;
763         uint64_t keyid;
764         int i;
765         int numkeys;
766         char *searchtext = NULL;
767         struct ll *wordlist = NULL;
768         struct ll *curword = NULL;
769         struct keyarray keylist = { NULL, 0, 0 };
770         struct keyarray newkeylist = { NULL, 0, 0 };
771         int firstpass = 1;
772
773         numkeys = 0;
774         searchtext = strdup(search);
775         wordlist = makewordlist(wordlist, searchtext);
776
777         for (curword = wordlist; curword != NULL; curword = curword->next) {
778                 db4_starttrans();
779
780                 ret = worddb->cursor(worddb,
781                                 txn,
782                                 &cursor,
783                                 0);   /* flags */
784
785                 memset(&key, 0, sizeof(key));
786                 memset(&data, 0, sizeof(data));
787                 key.data = curword->object;
788                 key.size = strlen(curword->object);
789                 data.flags = DB_DBT_MALLOC;
790                 ret = cursor->c_get(cursor,
791                                 &key,
792                                 &data,
793                                 DB_SET);
794                 while (ret == 0 && strncmp(key.data, curword->object,
795                                         key.size) == 0 &&
796                                 ((char *) curword->object)[key.size] == 0) {
797                         keyid = 0;
798                         for (i = 4; i < 12; i++) {
799                                 keyid <<= 8;
800                                 keyid += ((unsigned char *)
801                                                 data.data)[i];
802                         }
803
804                         /*
805                          * Only add the keys containing this word if this is
806                          * our first pass (ie we have no existing key list),
807                          * or the key contained a previous word.
808                          */
809                         if (firstpass || array_find(&keylist, keyid)) {
810                                 array_add(&newkeylist, keyid);
811                         }
812
813                         free(data.data);
814                         data.data = NULL;
815
816                         ret = cursor->c_get(cursor,
817                                         &key,
818                                         &data,
819                                         DB_NEXT);
820                 }
821                 array_free(&keylist);
822                 keylist = newkeylist;
823                 newkeylist.keys = NULL;
824                 newkeylist.count = newkeylist.size = 0;
825                 if (data.data != NULL) {
826                         free(data.data);
827                         data.data = NULL;
828                 }
829                 ret = cursor->c_close(cursor);
830                 cursor = NULL;
831                 firstpass = 0;
832                 db4_endtrans();
833         }
834         llfree(wordlist, NULL);
835         wordlist = NULL;
836
837         if (keylist.count > config.maxkeys) {
838                 keylist.count = config.maxkeys;
839         }
840         
841         db4_starttrans();
842         for (i = 0; i < keylist.count; i++) {
843                 numkeys += db4_fetch_key(keylist.keys[i],
844                         publickey,
845                         true);
846         }
847         array_free(&keylist);
848         free(searchtext);
849         searchtext = NULL;
850
851         db4_endtrans();
852         
853         return (numkeys);
854 }
855
856 static int db4_fetch_key_skshash(const struct skshash *hash,
857                 struct openpgp_publickey **publickey)
858 {
859         DBT       key, data;
860         DBC      *cursor = NULL;
861         uint64_t  keyid = 0;
862         int       ret = 0;
863
864         ret = skshashdb->cursor(skshashdb,
865                         txn,
866                         &cursor,
867                         0);   /* flags */
868
869         memset(&key, 0, sizeof(key));
870         memset(&data, 0, sizeof(data));
871         key.data = (void *) hash->hash;
872         key.size = sizeof(hash->hash);
873         data.flags = DB_DBT_MALLOC;
874
875         ret = cursor->c_get(cursor,
876                 &key,
877                 &data,
878                 DB_SET);
879
880         if (ret == 0) {
881                 keyid = *(uint64_t *) data.data;
882
883                 if (data.data != NULL) {
884                         free(data.data);
885                         data.data = NULL;
886                 }
887         }
888
889         ret = cursor->c_close(cursor);
890         cursor = NULL;
891
892         return db4_fetch_key(keyid, publickey, false);
893 }
894
895 /**
896  *      delete_key - Given a keyid delete the key from storage.
897  *      @keyid: The keyid to delete.
898  *      @intrans: If we're already in a transaction.
899  *
900  *      This function deletes a public key from whatever storage mechanism we
901  *      are using. Returns 0 if the key existed.
902  */
903 static int db4_delete_key(uint64_t keyid, bool intrans)
904 {
905         struct openpgp_publickey *publickey = NULL;
906         DBT key, data;
907         DBC *cursor = NULL;
908         uint32_t   shortkeyid = 0;
909         uint64_t  *subkeyids = NULL;
910         int ret = 0;
911         int i;
912         char **uids = NULL;
913         char *primary = NULL;
914         unsigned char worddb_data[12];
915         struct ll *wordlist = NULL;
916         struct ll *curword  = NULL;
917         bool deadlock = false;
918         struct skshash hash;
919
920         if (!intrans) {
921                 db4_starttrans();
922         }
923
924         db4_fetch_key(keyid, &publickey, true);
925
926         /*
927          * Walk through the uids removing the words from the worddb.
928          */
929         if (publickey != NULL) {
930                 uids = keyuids(publickey, &primary);
931         }
932         if (uids != NULL) {
933                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
934                         wordlist = makewordlist(wordlist, uids[i]);
935                 }
936                                 
937                 ret = worddb->cursor(worddb,
938                         txn,
939                         &cursor,
940                         0);   /* flags */
941
942                 for (curword = wordlist; curword != NULL && !deadlock;
943                                 curword = curword->next) {
944                         memset(&key, 0, sizeof(key));
945                         memset(&data, 0, sizeof(data));
946                         key.data = curword->object;
947                         key.size = strlen(key.data);
948                         data.data = worddb_data;
949                         data.size = sizeof(worddb_data);
950
951                         /*
952                          * Our data is the key creation time followed by the
953                          * key id.
954                          */
955                         worddb_data[ 0] = publickey->publickey->data[1];
956                         worddb_data[ 1] = publickey->publickey->data[2];
957                         worddb_data[ 2] = publickey->publickey->data[3];
958                         worddb_data[ 3] = publickey->publickey->data[4];
959                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
960                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
961                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
962                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
963                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
964                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
965                         worddb_data[10] = (keyid >>  8) & 0xFF;
966                         worddb_data[11] = keyid & 0xFF;
967
968                         ret = cursor->c_get(cursor,
969                                 &key,
970                                 &data,
971                                 DB_GET_BOTH);
972
973                         if (ret == 0) {
974                                 ret = cursor->c_del(cursor, 0);
975                         }
976
977                         if (ret != 0) {
978                                 logthing(LOGTHING_ERROR,
979                                         "Problem deleting word: %s "
980                                         "(0x%016" PRIX64 ")",
981                                         db_strerror(ret),
982                                         keyid);
983                                 if (ret == DB_LOCK_DEADLOCK) {
984                                         deadlock = true;
985                                 }
986                         }
987                 }
988                 ret = cursor->c_close(cursor);
989                 cursor = NULL;
990
991                 ret = skshashdb->cursor(skshashdb,
992                         txn,
993                         &cursor,
994                         0);   /* flags */
995                 get_skshash(publickey, &hash);
996
997                 memset(&key, 0, sizeof(key));
998                 memset(&data, 0, sizeof(data));
999                 key.data = hash.hash;
1000                 key.size = sizeof(hash.hash);
1001                 data.data = &keyid;
1002                 data.size = sizeof(keyid);
1003
1004                 ret = cursor->c_get(cursor,
1005                         &key,
1006                         &data,
1007                         DB_GET_BOTH);
1008
1009                 if (ret == 0) {
1010                         ret = cursor->c_del(cursor, 0);
1011                 }
1012
1013                 if (ret != 0) {
1014                         logthing(LOGTHING_ERROR,
1015                                 "Problem deleting skshash: %s "
1016                                 "(0x%016" PRIX64 ")",
1017                                 db_strerror(ret),
1018                                 keyid);
1019                         if (ret == DB_LOCK_DEADLOCK) {
1020                                 deadlock = true;
1021                         }
1022                 }
1023
1024                 ret = cursor->c_close(cursor);
1025                 cursor = NULL;
1026
1027                 /*
1028                  * Free our UID and word lists.
1029                  */
1030                 llfree(wordlist, NULL);
1031                 for (i = 0; uids[i] != NULL; i++) {
1032                         free(uids[i]);
1033                         uids[i] = NULL;
1034                 }
1035                 free(uids);
1036                 uids = NULL;
1037                 free_publickey(publickey);
1038                 publickey = NULL;
1039         }
1040
1041         if (!deadlock) {
1042                 ret = id32db->cursor(id32db,
1043                         txn,
1044                         &cursor,
1045                         0);   /* flags */
1046
1047                 shortkeyid = keyid & 0xFFFFFFFF;
1048
1049                 memset(&key, 0, sizeof(key));
1050                 memset(&data, 0, sizeof(data));
1051                 key.data = &shortkeyid;
1052                 key.size = sizeof(shortkeyid);
1053                 data.data = &keyid;
1054                 data.size = sizeof(keyid);
1055
1056                 ret = cursor->c_get(cursor,
1057                         &key,
1058                         &data,
1059                         DB_GET_BOTH);
1060
1061                 if (ret == 0) {
1062                         ret = cursor->c_del(cursor, 0);
1063                 }
1064
1065                 if (ret != 0) {
1066                         logthing(LOGTHING_ERROR,
1067                                 "Problem deleting short keyid: %s "
1068                                 "(0x%016" PRIX64 ")",
1069                                 db_strerror(ret),
1070                                 keyid);
1071                         if (ret == DB_LOCK_DEADLOCK) {
1072                                 deadlock = true;
1073                         }
1074                 }
1075
1076                 subkeyids = keysubkeys(publickey);
1077                 i = 0;
1078                 while (subkeyids != NULL && subkeyids[i] != 0) {
1079                         memset(&key, 0, sizeof(key));
1080                         key.data = &subkeyids[i];
1081                         key.size = sizeof(subkeyids[i]);
1082                         subkeydb->del(subkeydb, txn, &key, 0);
1083                         if (ret != 0) {
1084                                 logthing(LOGTHING_ERROR,
1085                                         "Problem deleting subkey id: %s "
1086                                         "(0x%016" PRIX64 ")",
1087                                         db_strerror(ret),
1088                                         keyid);
1089                                 if (ret == DB_LOCK_DEADLOCK) {
1090                                         deadlock = true;
1091                                 }
1092                         }
1093
1094                         shortkeyid = subkeyids[i++] & 0xFFFFFFFF;
1095
1096                         memset(&key, 0, sizeof(key));
1097                         memset(&data, 0, sizeof(data));
1098                         key.data = &shortkeyid;
1099                         key.size = sizeof(shortkeyid);
1100                         data.data = &keyid;
1101                         data.size = sizeof(keyid);
1102
1103                         ret = cursor->c_get(cursor,
1104                                 &key,
1105                                 &data,
1106                                 DB_GET_BOTH);
1107
1108                         if (ret == 0) {
1109                                 ret = cursor->c_del(cursor, 0);
1110                         }
1111
1112                         if (ret != 0) {
1113                                 logthing(LOGTHING_ERROR,
1114                                         "Problem deleting short keyid: %s "
1115                                         "(0x%016" PRIX64 ")",
1116                                         db_strerror(ret),
1117                                         keyid);
1118                                 if (ret == DB_LOCK_DEADLOCK) {
1119                                         deadlock = true;
1120                                 }
1121                         }
1122                 }
1123                 if (subkeyids != NULL) {
1124                         free(subkeyids);
1125                         subkeyids = NULL;
1126                 }
1127                 ret = cursor->c_close(cursor);
1128                 cursor = NULL;
1129
1130         }
1131
1132         if (!deadlock) {
1133                 key.data = &keyid;
1134                 key.size = sizeof(keyid);
1135
1136                 keydb(keyid)->del(keydb(keyid),
1137                                 txn,
1138                                 &key,
1139                                 0); /* flags */
1140         }
1141
1142         if (!intrans) {
1143                 db4_endtrans();
1144         }
1145
1146         return deadlock ? (-1) : (ret == DB_NOTFOUND);
1147 }
1148
1149 /**
1150  *      store_key - Takes a key and stores it.
1151  *      @publickey: A pointer to the public key to store.
1152  *      @intrans: If we're already in a transaction.
1153  *      @update: If true the key exists and should be updated.
1154  *
1155  *      Again we just use the hex representation of the keyid as the filename
1156  *      to store the key to. We flatten the public key to a list of OpenPGP
1157  *      packets and then use write_openpgp_stream() to write the stream out to
1158  *      the file. If update is true then we delete the old key first, otherwise
1159  *      we trust that it doesn't exist.
1160  */
1161 static int db4_store_key(struct openpgp_publickey *publickey, bool intrans,
1162                 bool update)
1163 {
1164         struct     openpgp_packet_list *packets = NULL;
1165         struct     openpgp_packet_list *list_end = NULL;
1166         struct     openpgp_publickey *next = NULL;
1167         int        ret = 0;
1168         int        i = 0;
1169         struct     buffer_ctx storebuf;
1170         DBT        key;
1171         DBT        data;
1172         uint64_t   keyid = 0;
1173         uint32_t   shortkeyid = 0;
1174         uint64_t  *subkeyids = NULL;
1175         char     **uids = NULL;
1176         char      *primary = NULL;
1177         unsigned char worddb_data[12];
1178         struct ll *wordlist = NULL;
1179         struct ll *curword  = NULL;
1180         bool       deadlock = false;
1181         struct skshash hash;
1182
1183         if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
1184                 logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
1185                 return 0;
1186         }
1187
1188         if (!intrans) {
1189                 db4_starttrans();
1190         }
1191
1192         /*
1193          * Delete the key if we already have it.
1194          *
1195          * TODO: Can we optimize this perhaps? Possibly when other data is
1196          * involved as well? I suspect this is easiest and doesn't make a lot
1197          * of difference though - the largest chunk of data is the keydata and
1198          * it definitely needs updated.
1199          */
1200         if (update) {
1201                 deadlock = (db4_delete_key(keyid, true) == -1);
1202         }
1203
1204         /*
1205          * Convert the key to a flat set of binary data.
1206          */
1207         if (!deadlock) {
1208                 next = publickey->next;
1209                 publickey->next = NULL;
1210                 flatten_publickey(publickey, &packets, &list_end);
1211                 publickey->next = next;
1212
1213                 storebuf.offset = 0; 
1214                 storebuf.size = 8192;
1215                 storebuf.buffer = malloc(8192);
1216         
1217                 write_openpgp_stream(buffer_putchar, &storebuf, packets);
1218
1219                 /*
1220                  * Now we have the key data store it in the DB; the keyid is
1221                  * the key.
1222                  */
1223                 memset(&key, 0, sizeof(key));
1224                 memset(&data, 0, sizeof(data));
1225                 key.data = &keyid;
1226                 key.size = sizeof(keyid);
1227                 data.size = storebuf.offset;
1228                 data.data = storebuf.buffer;
1229
1230                 ret = keydb(keyid)->put(keydb(keyid),
1231                                 txn,
1232                                 &key,
1233                                 &data,
1234                                 0); /* flags*/
1235                 if (ret != 0) {
1236                         logthing(LOGTHING_ERROR,
1237                                         "Problem storing key: %s",
1238                                         db_strerror(ret));
1239                         if (ret == DB_LOCK_DEADLOCK) {
1240                                 deadlock = true;
1241                         }
1242                 }
1243
1244                 free(storebuf.buffer);
1245                 storebuf.buffer = NULL;
1246                 storebuf.size = 0;
1247                 storebuf.offset = 0; 
1248         
1249                 free_packet_list(packets);
1250                 packets = NULL;
1251         }
1252
1253         /*
1254          * Walk through our uids storing the words into the db with the keyid.
1255          */
1256         if (!deadlock) {
1257                 uids = keyuids(publickey, &primary);
1258         }
1259         if (uids != NULL) {
1260                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
1261                         wordlist = makewordlist(wordlist, uids[i]);
1262                 }
1263
1264                 for (curword = wordlist; curword != NULL && !deadlock;
1265                                 curword = curword->next) {
1266                         memset(&key, 0, sizeof(key));
1267                         memset(&data, 0, sizeof(data));
1268                         key.data = curword->object;
1269                         key.size = strlen(key.data);
1270                         data.data = worddb_data;
1271                         data.size = sizeof(worddb_data);
1272
1273                         /*
1274                          * Our data is the key creation time followed by the
1275                          * key id.
1276                          */
1277                         worddb_data[ 0] = publickey->publickey->data[1];
1278                         worddb_data[ 1] = publickey->publickey->data[2];
1279                         worddb_data[ 2] = publickey->publickey->data[3];
1280                         worddb_data[ 3] = publickey->publickey->data[4];
1281                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
1282                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
1283                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
1284                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
1285                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
1286                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
1287                         worddb_data[10] = (keyid >>  8) & 0xFF;
1288                         worddb_data[11] = keyid & 0xFF; 
1289                         ret = worddb->put(worddb,
1290                                 txn,
1291                                 &key,
1292                                 &data,
1293                                 0);
1294                         if (ret != 0) {
1295                                 logthing(LOGTHING_ERROR,
1296                                         "Problem storing word: %s",
1297                                         db_strerror(ret));
1298                                 if (ret == DB_LOCK_DEADLOCK) {
1299                                         deadlock = true;
1300                                 }
1301                         }
1302                 }
1303
1304                 /*
1305                  * Free our UID and word lists.
1306                  */
1307                 llfree(wordlist, NULL);
1308                 for (i = 0; uids[i] != NULL; i++) {
1309                         free(uids[i]);
1310                         uids[i] = NULL;
1311                 }
1312                 free(uids);
1313                 uids = NULL;
1314         }
1315
1316         /*
1317          * Write the truncated 32 bit keyid so we can lookup the full id for
1318          * queries.
1319          */
1320         if (!deadlock) {
1321                 shortkeyid = keyid & 0xFFFFFFFF;
1322
1323                 memset(&key, 0, sizeof(key));
1324                 memset(&data, 0, sizeof(data));
1325                 key.data = &shortkeyid;
1326                 key.size = sizeof(shortkeyid);
1327                 data.data = &keyid;
1328                 data.size = sizeof(keyid);
1329
1330                 ret = id32db->put(id32db,
1331                         txn,
1332                         &key,
1333                         &data,
1334                         0);
1335                 if (ret != 0) {
1336                         logthing(LOGTHING_ERROR,
1337                                 "Problem storing short keyid: %s",
1338                                 db_strerror(ret));
1339                         if (ret == DB_LOCK_DEADLOCK) {
1340                                 deadlock = true;
1341                         }
1342                 }
1343         }
1344
1345         if (!deadlock) {
1346                 subkeyids = keysubkeys(publickey);
1347                 i = 0;
1348                 while (subkeyids != NULL && subkeyids[i] != 0) {
1349                         /* Store the subkey ID -> main key ID mapping */
1350                         memset(&key, 0, sizeof(key));
1351                         memset(&data, 0, sizeof(data));
1352                         key.data = &subkeyids[i];
1353                         key.size = sizeof(subkeyids[i]);
1354                         data.data = &keyid;
1355                         data.size = sizeof(keyid);
1356
1357                         ret = subkeydb->put(subkeydb,
1358                                 txn,
1359                                 &key,
1360                                 &data,
1361                                 0);
1362                         if (ret != 0) {
1363                                 logthing(LOGTHING_ERROR,
1364                                         "Problem storing subkey keyid: %s",
1365                                         db_strerror(ret));
1366                                 if (ret == DB_LOCK_DEADLOCK) {
1367                                         deadlock = true;
1368                                 }
1369                         }
1370
1371                         /* Store the short subkey ID -> main key ID mapping */
1372                         shortkeyid = subkeyids[i++] & 0xFFFFFFFF;
1373
1374                         memset(&key, 0, sizeof(key));
1375                         memset(&data, 0, sizeof(data));
1376                         key.data = &shortkeyid;
1377                         key.size = sizeof(shortkeyid);
1378                         data.data = &keyid;
1379                         data.size = sizeof(keyid);
1380
1381                         ret = id32db->put(id32db,
1382                                 txn,
1383                                 &key,
1384                                 &data,
1385                                 0);
1386                         if (ret != 0) {
1387                                 logthing(LOGTHING_ERROR,
1388                                         "Problem storing short keyid: %s",
1389                                         db_strerror(ret));
1390                                 if (ret == DB_LOCK_DEADLOCK) {
1391                                         deadlock = true;
1392                                 }
1393                         }
1394                 }
1395                 if (subkeyids != NULL) {
1396                         free(subkeyids);
1397                         subkeyids = NULL;
1398                 }
1399         }
1400
1401         if (!deadlock) {
1402                 get_skshash(publickey, &hash);
1403                 memset(&key, 0, sizeof(key));
1404                 memset(&data, 0, sizeof(data));
1405                 key.data = hash.hash;
1406                 key.size = sizeof(hash.hash);
1407                 data.data = &keyid;
1408                 data.size = sizeof(keyid);
1409
1410                 ret = skshashdb->put(skshashdb,
1411                         txn,
1412                         &key,
1413                         &data,
1414                         0);
1415                 if (ret != 0) {
1416                         logthing(LOGTHING_ERROR,
1417                                 "Problem storing SKS hash: %s",
1418                                 db_strerror(ret));
1419                         if (ret == DB_LOCK_DEADLOCK) {
1420                                 deadlock = true;
1421                         }
1422                 }
1423         }
1424
1425         if (!intrans) {
1426                 db4_endtrans();
1427         }
1428
1429         return deadlock ? -1 : 0 ;
1430 }
1431
1432 /**
1433  *      iterate_keys - call a function once for each key in the db.
1434  *      @iterfunc: The function to call.
1435  *      @ctx: A context pointer
1436  *
1437  *      Calls iterfunc once for each key in the database. ctx is passed
1438  *      unaltered to iterfunc. This function is intended to aid database dumps
1439  *      and statistic calculations.
1440  *
1441  *      Returns the number of keys we iterated over.
1442  */
1443 static int db4_iterate_keys(void (*iterfunc)(void *ctx,
1444                 struct openpgp_publickey *key), void *ctx)
1445 {
1446         DBT                         dbkey, data;
1447         DBC                        *cursor = NULL;
1448         int                         ret = 0;
1449         int                         i = 0;
1450         int                         numkeys = 0;
1451         struct buffer_ctx           fetchbuf;
1452         struct openpgp_packet_list *packets = NULL;
1453         struct openpgp_publickey   *key = NULL;
1454
1455         for (i = 0; i < numdbs; i++) {
1456                 ret = dbconns[i]->cursor(dbconns[i],
1457                         NULL,
1458                         &cursor,
1459                         0);   /* flags */
1460
1461                 memset(&dbkey, 0, sizeof(dbkey));
1462                 memset(&data, 0, sizeof(data));
1463                 ret = cursor->c_get(cursor, &dbkey, &data, DB_NEXT);
1464                 while (ret == 0) {
1465                         fetchbuf.buffer = data.data;
1466                         fetchbuf.offset = 0;
1467                         fetchbuf.size = data.size;
1468                         read_openpgp_stream(buffer_fetchchar, &fetchbuf,
1469                                 &packets, 0);
1470                         parse_keys(packets, &key);
1471
1472                         iterfunc(ctx, key);
1473                         
1474                         free_publickey(key);
1475                         key = NULL;
1476                         free_packet_list(packets);
1477                         packets = NULL;
1478                         
1479                         memset(&dbkey, 0, sizeof(dbkey));
1480                         memset(&data, 0, sizeof(data));
1481                         ret = cursor->c_get(cursor, &dbkey, &data,
1482                                         DB_NEXT);
1483                         numkeys++;
1484                 }
1485                 if (ret != DB_NOTFOUND) {
1486                         logthing(LOGTHING_ERROR,
1487                                 "Problem reading key: %s",
1488                                 db_strerror(ret));
1489                 }
1490
1491                 ret = cursor->c_close(cursor);
1492                 cursor = NULL;
1493         }
1494         
1495         return numkeys;
1496 }
1497
1498 /*
1499  * Include the basic keydb routines.
1500  */
1501 #define NEED_GETKEYSIGS 1
1502 #define NEED_KEYID2UID 1
1503 #define NEED_UPDATEKEYS 1
1504 #include "keydb.c"
1505
1506 struct dbfuncs keydb_db4_funcs = {
1507         .initdb                 = db4_initdb,
1508         .cleanupdb              = db4_cleanupdb,
1509         .starttrans             = db4_starttrans,
1510         .endtrans               = db4_endtrans,
1511         .fetch_key              = db4_fetch_key,
1512         .fetch_key_text         = db4_fetch_key_text,
1513         .fetch_key_skshash      = db4_fetch_key_skshash,
1514         .store_key              = db4_store_key,
1515         .update_keys            = generic_update_keys,
1516         .delete_key             = db4_delete_key,
1517         .getkeysigs             = generic_getkeysigs,
1518         .cached_getkeysigs      = generic_cached_getkeysigs,
1519         .keyid2uid              = generic_keyid2uid,
1520         .getfullkeyid           = db4_getfullkeyid,
1521         .iterate_keys           = db4_iterate_keys,
1522 };