]> git.sommitrealweird.co.uk Git - onak.git/blob - keydb_db4.c
autoconf onak-mail.pl config
[onak.git] / keydb_db4.c
1 /*
2  * keydb_db4.c - Routines to store and fetch keys in a DB3 database.
3  *
4  * Jonathan McDowell <noodles@earth.li>
5  *
6  * Copyright 2002-2004 Project Purple
7  */
8
9 #include <sys/types.h>
10 #include <sys/uio.h>
11 #include <ctype.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18
19 #include <db.h>
20
21 #include "charfuncs.h"
22 #include "keydb.h"
23 #include "keyid.h"
24 #include "decodekey.h"
25 #include "keystructs.h"
26 #include "mem.h"
27 #include "log.h"
28 #include "onak-conf.h"
29 #include "parsekey.h"
30 #include "wordlist.h"
31
32 /**
33  *      dbenv - our database environment.
34  */
35 static DB_ENV *dbenv = NULL;
36
37 /**
38  *      numdb - The number of database files we have.
39  */
40 static int numdbs = 16;
41
42 /**
43  *      dbconn - our connections to the key database files.
44  */
45 static DB **dbconns = NULL;
46
47 /**
48  *      worddb - our connection to the word database.
49  */
50 static DB *worddb = NULL;
51
52 /**
53  *      id32db - our connection to the 32bit ID database.
54  */
55 static DB *id32db = NULL;
56
57 /**
58  *      txn - our current transaction id.
59  */
60 static DB_TXN *txn = NULL;
61
62 DB *keydb(uint64_t keyid)
63 {
64         uint64_t keytrun;
65
66         keytrun = keyid >> 8;
67
68         return(dbconns[keytrun % numdbs]);
69 }
70
71 /**
72  *      initdb - Initialize the key database.
73  *
74  *      This function should be called before any of the other functions in
75  *      this file are called in order to allow the DB to be initialized ready
76  *      for access.
77  */
78 void initdb(bool readonly)
79 {
80         char       buf[1024];
81         FILE      *numdb = NULL;
82         int        ret = 0;
83         int        i = 0;
84         u_int32_t  flags = 0;
85
86         snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", config.db_dir);
87         numdb = fopen(buf, "r");
88         if (numdb != NULL) {
89                 if (fgets(buf, sizeof(buf), numdb) != NULL) {
90                         numdbs = atoi(buf);
91                 }
92                 fclose(numdb);
93         } else if (!readonly) {
94                 logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s",
95                                 strerror(errno));
96                 numdb = fopen(buf, "w");
97                 if (numdb != NULL) {
98                         fprintf(numdb, "%d", numdbs);
99                         fclose(numdb);
100                 } else {
101                         logthing(LOGTHING_ERROR,
102                                 "Couldn't write num_keydb: %s",
103                                 strerror(errno));
104                 }
105         }
106
107         dbconns = malloc(sizeof (DB *) * numdbs);
108         if (dbconns == NULL) {
109                 logthing(LOGTHING_CRITICAL,
110                                 "Couldn't allocate memory for dbconns");
111                 exit(1);
112         }
113
114         ret = db_env_create(&dbenv, 0);
115         if (ret != 0) {
116                 logthing(LOGTHING_CRITICAL,
117                         "db_env_create: %s", db_strerror(ret));
118                 exit(1);
119         }
120
121         /*
122          * Enable deadlock detection so that we don't block indefinitely on
123          * anything. What we really want is simple 2 state locks, but I'm not
124          * sure how to make the standard DB functions do that yet.
125          */
126         ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT);
127         if (ret != 0) {
128                 logthing(LOGTHING_CRITICAL,
129                         "db_env_create: %s", db_strerror(ret));
130                 exit(1);
131         }
132
133         ret = dbenv->open(dbenv, config.db_dir,
134                         DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
135                         DB_INIT_TXN |
136                         DB_CREATE,
137                         0);
138         if (ret != 0) {
139                 logthing(LOGTHING_CRITICAL,
140                                 "Error opening db environment: %s (%s)",
141                                 config.db_dir,
142                                 db_strerror(ret));
143                 exit(1);
144         }
145
146         starttrans();
147
148         for (i = 0; i < numdbs; i++) {
149                 ret = db_create(&dbconns[i], dbenv, 0);
150                 if (ret != 0) {
151                         logthing(LOGTHING_CRITICAL,
152                                 "db_create: %s", db_strerror(ret));
153                         exit(1);
154                 }
155
156                 snprintf(buf, 1023, "keydb.%d.db", i);
157                 flags = DB_CREATE;
158                 if (readonly) {
159                         flags = DB_RDONLY;
160                 }
161                 ret = dbconns[i]->open(dbconns[i],
162                                 txn,
163                                 buf,
164                                 "keydb",
165                                 DB_HASH,
166                                 flags,
167                                 0664);
168                 if (ret != 0) {
169                         logthing(LOGTHING_CRITICAL,
170                                 "Error opening key database: %s (%s)",
171                                 buf,
172                                 db_strerror(ret));
173                         exit(1);
174                 }
175         }
176
177         ret = db_create(&worddb, dbenv, 0);
178         if (ret != 0) {
179                 logthing(LOGTHING_CRITICAL, "db_create: %s", db_strerror(ret));
180                 exit(1);
181         }
182         ret = worddb->set_flags(worddb, DB_DUP);
183
184         ret = worddb->open(worddb, txn, "worddb", "worddb", DB_BTREE,
185                         flags,
186                         0664);
187         if (ret != 0) {
188                 logthing(LOGTHING_CRITICAL,
189                                 "Error opening word database: %s (%s)",
190                                 "worddb",
191                                 db_strerror(ret));
192                 exit(1);
193         }
194
195         ret = db_create(&id32db, dbenv, 0);
196         if (ret != 0) {
197                 logthing(LOGTHING_CRITICAL, "db_create: %s", db_strerror(ret));
198                 exit(1);
199         }
200         ret = id32db->set_flags(id32db, DB_DUP);
201
202         ret = id32db->open(id32db, txn, "id32db", "id32db", DB_HASH,
203                         flags,
204                         0664);
205         if (ret != 0) {
206                 logthing(LOGTHING_CRITICAL,
207                                 "Error opening id32 database: %s (%s)",
208                                 "id32db",
209                                 db_strerror(ret));
210                 exit(1);
211         }
212         endtrans();
213         
214         return;
215 }
216
217 /**
218  *      cleanupdb - De-initialize the key database.
219  *
220  *      This function should be called upon program exit to allow the DB to
221  *      cleanup after itself.
222  */
223 void cleanupdb(void)
224 {
225         int i = 0;
226
227         dbenv->txn_checkpoint(dbenv, 0, 0, 0);
228         id32db->close(id32db, 0);
229         id32db = NULL;
230         worddb->close(worddb, 0);
231         worddb = NULL;
232         for (i = 0; i < numdbs; i++) {
233                 dbconns[i]->close(dbconns[i], 0);
234                 dbconns[i] = NULL;
235         }
236         dbenv->close(dbenv, 0);
237         dbenv = NULL;
238 }
239
240 /**
241  *      starttrans - Start a transaction.
242  *
243  *      Start a transaction. Intended to be used if we're about to perform many
244  *      operations on the database to help speed it all up, or if we want
245  *      something to only succeed if all relevant operations are successful.
246  */
247 bool starttrans(void)
248 {
249         int ret;
250
251         log_assert(dbenv != NULL);
252         log_assert(txn == NULL);
253
254         ret = dbenv->txn_begin(dbenv,
255                 NULL, /* No parent transaction */
256                 &txn,
257                 0);
258         if (ret != 0) {
259                 logthing(LOGTHING_CRITICAL,
260                                 "Error starting transaction: %s",
261                                 db_strerror(ret));
262                 exit(1);
263         }
264
265         return true;
266 }
267
268 /**
269  *      endtrans - End a transaction.
270  *
271  *      Ends a transaction.
272  */
273 void endtrans(void)
274 {
275         int ret;
276
277         log_assert(dbenv != NULL);
278         log_assert(txn != NULL);
279
280         ret = txn->commit(txn,
281                 0);
282         if (ret != 0) {
283                 logthing(LOGTHING_CRITICAL,
284                                 "Error ending transaction: %s",
285                                 db_strerror(ret));
286                 exit(1);
287         }
288         txn = NULL;
289
290         return;
291 }
292
293 /**
294  *      fetch_key - Given a keyid fetch the key from storage.
295  *      @keyid: The keyid to fetch.
296  *      @publickey: A pointer to a structure to return the key in.
297  *      @intrans: If we're already in a transaction.
298  *
299  *      We use the hex representation of the keyid as the filename to fetch the
300  *      key from. The key is stored in the file as a binary OpenPGP stream of
301  *      packets, so we can just use read_openpgp_stream() to read the packets
302  *      in and then parse_keys() to parse the packets into a publickey
303  *      structure.
304  */
305 int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
306                 bool intrans)
307 {
308         struct openpgp_packet_list *packets = NULL;
309         DBT key, data;
310         int ret = 0;
311         int numkeys = 0;
312         struct buffer_ctx fetchbuf;
313
314         if (keyid < 0x100000000LL) {
315                 keyid = getfullkeyid(keyid);
316         }
317
318         memset(&key, 0, sizeof(key));
319         memset(&data, 0, sizeof(data));
320
321         data.size = 0;
322         data.data = NULL;
323
324         key.size = sizeof(keyid);
325         key.data = &keyid;
326
327         if (!intrans) {
328                 starttrans();
329         }
330
331         ret = keydb(keyid)->get(keydb(keyid),
332                         txn,
333                         &key,
334                         &data,
335                         0); /* flags*/
336         
337         if (ret == 0) {
338                 fetchbuf.buffer = data.data;
339                 fetchbuf.offset = 0;
340                 fetchbuf.size = data.size;
341                 read_openpgp_stream(buffer_fetchchar, &fetchbuf,
342                                 &packets, 0);
343                 parse_keys(packets, publickey);
344                 free_packet_list(packets);
345                 packets = NULL;
346                 numkeys++;
347         } else if (ret != DB_NOTFOUND) {
348                 logthing(LOGTHING_ERROR,
349                                 "Problem retrieving key: %s",
350                                 db_strerror(ret));
351         }
352
353         if (!intrans) {
354                 endtrans();
355         }
356
357         return (numkeys);
358 }
359
360 int worddb_cmp(const void *d1, const void *d2)
361 {
362         return memcmp(d1, d2, 12);
363 }
364
365 /**
366  *      fetch_key_text - Trys to find the keys that contain the supplied text.
367  *      @search: The text to search for.
368  *      @publickey: A pointer to a structure to return the key in.
369  *
370  *      This function searches for the supplied text and returns the keys that
371  *      contain it.
372  */
373 int fetch_key_text(const char *search, struct openpgp_publickey **publickey)
374 {
375         DBC *cursor = NULL;
376         DBT key, data;
377         int ret;
378         uint64_t keyid;
379         int i;
380         int numkeys;
381         char *searchtext = NULL;
382         struct ll *wordlist = NULL;
383         struct ll *curword = NULL;
384         struct ll *keylist = NULL;
385         struct ll *newkeylist = NULL;
386
387         numkeys = 0;
388         searchtext = strdup(search);
389         wordlist = makewordlist(wordlist, searchtext);
390
391         starttrans();
392
393         ret = worddb->cursor(worddb,
394                         txn,
395                         &cursor,
396                         0);   /* flags */
397
398         for (curword = wordlist; curword != NULL; curword = curword->next) {
399                 memset(&key, 0, sizeof(key));
400                 memset(&data, 0, sizeof(data));
401                 key.data = curword->object;
402                 key.size = strlen(curword->object);
403                 data.flags = DB_DBT_MALLOC;
404                 ret = cursor->c_get(cursor,
405                                 &key,
406                                 &data,
407                                 DB_SET);
408                 while (ret == 0 && strncmp(key.data, curword->object,
409                                         key.size) == 0 &&
410                                 ((char *) curword->object)[key.size] == 0) {
411                         keyid = 0;
412                         for (i = 4; i < 12; i++) {
413                                 keyid <<= 8;
414                                 keyid += ((unsigned char *)
415                                                 data.data)[i];
416                         }
417
418                         if (keylist == NULL ||
419                                         llfind(keylist, data.data,
420                                                 worddb_cmp) != NULL) {
421                                 newkeylist = lladd(newkeylist, data.data);
422                                 data.data = NULL;
423                         } else {
424                                 free(data.data);
425                                 data.data = NULL;
426                         }
427                         ret = cursor->c_get(cursor,
428                                         &key,
429                                         &data,
430                                         DB_NEXT);
431                 }
432                 llfree(keylist, free);
433                 keylist = newkeylist;
434                 newkeylist = NULL;
435                 if (data.data != NULL) {
436                         free(data.data);
437                         data.data = NULL;
438                 }
439         }
440         llfree(wordlist, NULL);
441         wordlist = NULL;
442         
443         for (newkeylist = keylist;
444                         newkeylist != NULL && numkeys < config.maxkeys;
445                         newkeylist = newkeylist->next) {
446
447                         keyid = 0;
448                         for (i = 4; i < 12; i++) {
449                                 keyid <<= 8;
450                                 keyid += ((unsigned char *)
451                                                 newkeylist->object)[i];
452                         }
453
454                         numkeys += fetch_key(keyid,
455                                         publickey,
456                                         true);
457         }
458         llfree(keylist, free);
459         keylist = NULL;
460         free(searchtext);
461         searchtext = NULL;
462
463         ret = cursor->c_close(cursor);
464         cursor = NULL;
465
466         endtrans();
467         
468         return (numkeys);
469 }
470
471 /**
472  *      store_key - Takes a key and stores it.
473  *      @publickey: A pointer to the public key to store.
474  *      @intrans: If we're already in a transaction.
475  *      @update: If true the key exists and should be updated.
476  *
477  *      Again we just use the hex representation of the keyid as the filename
478  *      to store the key to. We flatten the public key to a list of OpenPGP
479  *      packets and then use write_openpgp_stream() to write the stream out to
480  *      the file. If update is true then we delete the old key first, otherwise
481  *      we trust that it doesn't exist.
482  */
483 int store_key(struct openpgp_publickey *publickey, bool intrans, bool update)
484 {
485         struct     openpgp_packet_list *packets = NULL;
486         struct     openpgp_packet_list *list_end = NULL;
487         struct     openpgp_publickey *next = NULL;
488         int        ret = 0;
489         int        i = 0;
490         struct     buffer_ctx storebuf;
491         DBT        key;
492         DBT        data;
493         uint64_t   keyid = 0;
494         uint32_t   shortkeyid = 0;
495         uint64_t  *subkeyids = NULL;
496         char     **uids = NULL;
497         char      *primary = NULL;
498         unsigned char worddb_data[12];
499         struct ll *wordlist = NULL;
500         struct ll *curword  = NULL;
501         bool       deadlock = false;
502
503         keyid = get_keyid(publickey);
504
505         if (!intrans) {
506                 starttrans();
507         }
508
509         /*
510          * Delete the key if we already have it.
511          *
512          * TODO: Can we optimize this perhaps? Possibly when other data is
513          * involved as well? I suspect this is easiest and doesn't make a lot
514          * of difference though - the largest chunk of data is the keydata and
515          * it definitely needs updated.
516          */
517         if (update) {
518                 deadlock = (delete_key(keyid, true) == -1);
519         }
520
521         /*
522          * Convert the key to a flat set of binary data.
523          */
524         if (!deadlock) {
525                 next = publickey->next;
526                 publickey->next = NULL;
527                 flatten_publickey(publickey, &packets, &list_end);
528                 publickey->next = next;
529
530                 storebuf.offset = 0; 
531                 storebuf.size = 8192;
532                 storebuf.buffer = malloc(8192);
533         
534                 write_openpgp_stream(buffer_putchar, &storebuf, packets);
535
536                 /*
537                  * Now we have the key data store it in the DB; the keyid is
538                  * the key.
539                  */
540                 memset(&key, 0, sizeof(key));
541                 memset(&data, 0, sizeof(data));
542                 key.data = &keyid;
543                 key.size = sizeof(keyid);
544                 data.size = storebuf.offset;
545                 data.data = storebuf.buffer;
546
547                 ret = keydb(keyid)->put(keydb(keyid),
548                                 txn,
549                                 &key,
550                                 &data,
551                                 0); /* flags*/
552                 if (ret != 0) {
553                         logthing(LOGTHING_ERROR,
554                                         "Problem storing key: %s",
555                                         db_strerror(ret));
556                         if (ret == DB_LOCK_DEADLOCK) {
557                                 deadlock = true;
558                         }
559                 }
560
561                 free(storebuf.buffer);
562                 storebuf.buffer = NULL;
563                 storebuf.size = 0;
564                 storebuf.offset = 0; 
565         
566                 free_packet_list(packets);
567                 packets = NULL;
568         }
569
570         /*
571          * Walk through our uids storing the words into the db with the keyid.
572          */
573         if (!deadlock) {
574                 uids = keyuids(publickey, &primary);
575         }
576         if (uids != NULL) {
577                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
578                         wordlist = makewordlist(wordlist, uids[i]);
579                 }
580
581                 for (curword = wordlist; curword != NULL && !deadlock;
582                                 curword = curword->next) {
583                         memset(&key, 0, sizeof(key));
584                         memset(&data, 0, sizeof(data));
585                         key.data = curword->object;
586                         key.size = strlen(key.data);
587                         data.data = worddb_data;
588                         data.size = sizeof(worddb_data);
589
590                         /*
591                          * Our data is the key creation time followed by the
592                          * key id.
593                          */
594                         worddb_data[ 0] = publickey->publickey->data[1];
595                         worddb_data[ 1] = publickey->publickey->data[2];
596                         worddb_data[ 2] = publickey->publickey->data[3];
597                         worddb_data[ 3] = publickey->publickey->data[4];
598                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
599                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
600                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
601                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
602                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
603                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
604                         worddb_data[10] = (keyid >>  8) & 0xFF;
605                         worddb_data[11] = keyid & 0xFF; 
606                         ret = worddb->put(worddb,
607                                 txn,
608                                 &key,
609                                 &data,
610                                 0);
611                         if (ret != 0) {
612                                 logthing(LOGTHING_ERROR,
613                                         "Problem storing word: %s",
614                                         db_strerror(ret));
615                                 if (ret == DB_LOCK_DEADLOCK) {
616                                         deadlock = true;
617                                 }
618                         }
619                 }
620
621                 /*
622                  * Free our UID and word lists.
623                  */
624                 llfree(wordlist, NULL);
625                 for (i = 0; uids[i] != NULL; i++) {
626                         free(uids[i]);
627                         uids[i] = NULL;
628                 }
629                 free(uids);
630                 uids = NULL;
631         }
632
633         if (!intrans) {
634                 endtrans();
635         }
636
637         /*
638          * Write the truncated 32 bit keyid so we can lookup the full id for
639          * queries.
640          */
641         if (!deadlock) {
642                 shortkeyid = keyid & 0xFFFFFFFF;
643
644                 memset(&key, 0, sizeof(key));
645                 memset(&data, 0, sizeof(data));
646                 key.data = &shortkeyid;
647                 key.size = sizeof(shortkeyid);
648                 data.data = &keyid;
649                 data.size = sizeof(keyid);
650
651                 ret = id32db->put(id32db,
652                         txn,
653                         &key,
654                         &data,
655                         0);
656                 if (ret != 0) {
657                         logthing(LOGTHING_ERROR,
658                                 "Problem storing short keyid: %s",
659                                 db_strerror(ret));
660                         if (ret == DB_LOCK_DEADLOCK) {
661                                 deadlock = true;
662                         }
663                 }
664         }
665
666         if (!deadlock) {
667                 subkeyids = keysubkeys(publickey);
668                 i = 0;
669                 while (subkeyids != NULL && subkeyids[i] != 0) {
670                         shortkeyid = subkeyids[i++] & 0xFFFFFFFF;
671
672                         memset(&key, 0, sizeof(key));
673                         memset(&data, 0, sizeof(data));
674                         key.data = &shortkeyid;
675                         key.size = sizeof(shortkeyid);
676                         data.data = &keyid;
677                         data.size = sizeof(keyid);
678
679                         ret = id32db->put(id32db,
680                                 txn,
681                                 &key,
682                                 &data,
683                                 0);
684                         if (ret != 0) {
685                                 logthing(LOGTHING_ERROR,
686                                         "Problem storing short keyid: %s",
687                                         db_strerror(ret));
688                                 if (ret == DB_LOCK_DEADLOCK) {
689                                         deadlock = true;
690                                 }
691                         }
692                 }
693                 if (subkeyids != NULL) {
694                         free(subkeyids);
695                         subkeyids = NULL;
696                 }
697         }
698
699         return deadlock ? -1 : 0 ;
700 }
701
702 /**
703  *      delete_key - Given a keyid delete the key from storage.
704  *      @keyid: The keyid to delete.
705  *      @intrans: If we're already in a transaction.
706  *
707  *      This function deletes a public key from whatever storage mechanism we
708  *      are using. Returns 0 if the key existed.
709  */
710 int delete_key(uint64_t keyid, bool intrans)
711 {
712         struct openpgp_publickey *publickey = NULL;
713         DBT key, data;
714         DBC *cursor = NULL;
715         uint32_t   shortkeyid = 0;
716         uint64_t  *subkeyids = NULL;
717         int ret = 0;
718         int i;
719         char **uids = NULL;
720         char *primary = NULL;
721         unsigned char worddb_data[12];
722         struct ll *wordlist = NULL;
723         struct ll *curword  = NULL;
724         bool deadlock = false;
725
726         if (!intrans) {
727                 starttrans();
728         }
729
730         fetch_key(keyid, &publickey, true);
731
732         /*
733          * Walk through the uids removing the words from the worddb.
734          */
735         if (publickey != NULL) {
736                 uids = keyuids(publickey, &primary);
737         }
738         if (uids != NULL) {
739                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
740                         wordlist = makewordlist(wordlist, uids[i]);
741                 }
742                                 
743                 ret = worddb->cursor(worddb,
744                         txn,
745                         &cursor,
746                         0);   /* flags */
747
748                 for (curword = wordlist; curword != NULL && !deadlock;
749                                 curword = curword->next) {
750                         memset(&key, 0, sizeof(key));
751                         memset(&data, 0, sizeof(data));
752                         key.data = curword->object;
753                         key.size = strlen(key.data);
754                         data.data = worddb_data;
755                         data.size = sizeof(worddb_data);
756
757                         /*
758                          * Our data is the key creation time followed by the
759                          * key id.
760                          */
761                         worddb_data[ 0] = publickey->publickey->data[1];
762                         worddb_data[ 1] = publickey->publickey->data[2];
763                         worddb_data[ 2] = publickey->publickey->data[3];
764                         worddb_data[ 3] = publickey->publickey->data[4];
765                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
766                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
767                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
768                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
769                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
770                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
771                         worddb_data[10] = (keyid >>  8) & 0xFF;
772                         worddb_data[11] = keyid & 0xFF; 
773
774                         ret = cursor->c_get(cursor,
775                                 &key,
776                                 &data,
777                                 DB_GET_BOTH);
778
779                         if (ret == 0) {
780                                 ret = cursor->c_del(cursor, 0);
781                                 if (ret != 0) {
782                                         logthing(LOGTHING_ERROR,
783                                                 "Problem deleting word: %s",
784                                                 db_strerror(ret));
785                                 }
786                         }
787
788                         if (ret != 0) {
789                                 logthing(LOGTHING_ERROR,
790                                         "Problem deleting word: %s",
791                                         db_strerror(ret));
792                                 if (ret == DB_LOCK_DEADLOCK) {
793                                         deadlock = true;
794                                 }
795                         }
796                 }
797                 ret = cursor->c_close(cursor);
798                 cursor = NULL;
799
800                 /*
801                  * Free our UID and word lists.
802                  */
803                 llfree(wordlist, NULL);
804                 for (i = 0; uids[i] != NULL; i++) {
805                         free(uids[i]);
806                         uids[i] = NULL;
807                 }
808                 free(uids);
809                 uids = NULL;
810                 free_publickey(publickey);
811                 publickey = NULL;
812         }
813
814         if (!deadlock) {
815                 ret = id32db->cursor(id32db,
816                         txn,
817                         &cursor,
818                         0);   /* flags */
819
820                 shortkeyid = keyid & 0xFFFFFFFF;
821
822                 memset(&key, 0, sizeof(key));
823                 memset(&data, 0, sizeof(data));
824                 key.data = &shortkeyid;
825                 key.size = sizeof(shortkeyid);
826                 data.data = &keyid;
827                 data.size = sizeof(keyid);
828
829                 ret = cursor->c_get(cursor,
830                         &key,
831                         &data,
832                         DB_GET_BOTH);
833
834                 if (ret == 0) {
835                         ret = cursor->c_del(cursor, 0);
836                         if (ret != 0) {
837                                 logthing(LOGTHING_ERROR,
838                                         "Problem deleting short keyid: %s",
839                                         db_strerror(ret));
840                         }
841                 }
842
843                 if (ret != 0) {
844                         logthing(LOGTHING_ERROR,
845                                 "Problem deleting short keyid: %s",
846                                 db_strerror(ret));
847                         if (ret == DB_LOCK_DEADLOCK) {
848                                 deadlock = true;
849                         }
850                 }
851
852                 subkeyids = keysubkeys(publickey);
853                 i = 0;
854                 while (subkeyids != NULL && subkeyids[i] != 0) {
855                         shortkeyid = subkeyids[i++] & 0xFFFFFFFF;
856
857                         memset(&key, 0, sizeof(key));
858                         memset(&data, 0, sizeof(data));
859                         key.data = &shortkeyid;
860                         key.size = sizeof(shortkeyid);
861                         data.data = &keyid;
862                         data.size = sizeof(keyid);
863
864                         ret = cursor->c_get(cursor,
865                                 &key,
866                                 &data,
867                                 DB_GET_BOTH);
868
869                         if (ret == 0) {
870                                 ret = cursor->c_del(cursor, 0);
871                                 if (ret != 0) {
872                                         logthing(LOGTHING_ERROR,
873                                                 "Problem deleting short"
874                                                 " keyid: %s",
875                                                 db_strerror(ret));
876                                 }
877                         }
878
879                         if (ret != 0) {
880                                 logthing(LOGTHING_ERROR,
881                                         "Problem deleting short keyid: %s",
882                                         db_strerror(ret));
883                                 if (ret == DB_LOCK_DEADLOCK) {
884                                         deadlock = true;
885                                 }
886                         }
887                 }
888                 if (subkeyids != NULL) {
889                         free(subkeyids);
890                         subkeyids = NULL;
891                 }
892
893                 ret = cursor->c_close(cursor);
894                 cursor = NULL;
895         }
896
897         if (!deadlock) {
898                 key.data = &keyid;
899                 key.size = sizeof(keyid);
900
901                 keydb(keyid)->del(keydb(keyid),
902                                 txn,
903                                 &key,
904                                 0); /* flags */
905         }
906
907         if (!intrans) {
908                 endtrans();
909         }
910
911         return deadlock ? (-1) : (ret == DB_NOTFOUND);
912 }
913
914 /**
915  *      dumpdb - dump the key database
916  *      @filenamebase: The base filename to use for the dump.
917  *
918  *      Dumps the database into one or more files, which contain pure OpenPGP
919  *      that can be reimported into onak or gpg. filenamebase provides a base
920  *      file name for the dump; several files may be created, all of which will
921  *      begin with this string and then have a unique number and a .pgp
922  *      extension.
923  */
924 int dumpdb(char *filenamebase)
925 {
926         DBT   key, data;
927         DBC  *cursor = NULL;
928         int   ret = 0;
929         int   fd = -1;
930         int   i = 0;
931         char  filename[1024];
932
933         filename[1023] = 0;
934         for (i = 0; i < numdbs; i++) {
935                 ret = dbconns[i]->cursor(dbconns[i],
936                         NULL,
937                         &cursor,
938                         0);   /* flags */
939
940                 snprintf(filename, 1023, "%s.%d.pgp", filenamebase, i);
941                 fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0640);
942                 if (fd == -1) {
943                         logthing(LOGTHING_ERROR,
944                                 "Error opening keydump file (%s): %s",
945                                 filename,
946                                 strerror(errno));
947                 } else {
948                         memset(&key, 0, sizeof(key));
949                         memset(&data, 0, sizeof(data));
950                         ret = cursor->c_get(cursor, &key, &data, DB_NEXT);
951                         while (ret == 0) {
952                                 write(fd, data.data, data.size);
953                                 memset(&key, 0, sizeof(key));
954                                 memset(&data, 0, sizeof(data));
955                                 ret = cursor->c_get(cursor, &key, &data,
956                                                 DB_NEXT);
957                         }
958                         if (ret != DB_NOTFOUND) {
959                                 logthing(LOGTHING_ERROR,
960                                         "Problem reading key: %s",
961                                         db_strerror(ret));
962                         }
963                         close(fd);
964                 }
965
966                 ret = cursor->c_close(cursor);
967                 cursor = NULL;
968         }
969         
970         return 0;
971 }
972
973 /**
974  *      getfullkeyid - Maps a 32bit key id to a 64bit one.
975  *      @keyid: The 32bit keyid.
976  *
977  *      This function maps a 32bit key id to the full 64bit one. It returns the
978  *      full keyid. If the key isn't found a keyid of 0 is returned.
979  */
980 uint64_t getfullkeyid(uint64_t keyid)
981 {
982         DBT       key, data;
983         DBC      *cursor = NULL;
984         uint32_t  shortkeyid = 0;
985         int       ret = 0;
986
987         if (keyid < 0x100000000LL) {
988                 ret = id32db->cursor(id32db,
989                                 txn,
990                                 &cursor,
991                                 0);   /* flags */
992
993                 shortkeyid = keyid & 0xFFFFFFFF;
994
995                 memset(&key, 0, sizeof(key));
996                 memset(&data, 0, sizeof(data));
997                 key.data = &shortkeyid;
998                 key.size = sizeof(shortkeyid);
999                 data.flags = DB_DBT_MALLOC;
1000
1001                 ret = cursor->c_get(cursor,
1002                         &key,
1003                         &data,
1004                         DB_SET);
1005
1006                 if (ret == 0) {
1007                         keyid = *(uint64_t *) data.data;
1008
1009                         if (data.data != NULL) {
1010                                 free(data.data);
1011                                 data.data = NULL;
1012                         }
1013                 }
1014
1015                 ret = cursor->c_close(cursor);
1016                 cursor = NULL;
1017         }
1018         
1019         return keyid;
1020 }
1021
1022 /*
1023  * Include the basic keydb routines.
1024  */
1025 #define NEED_GETKEYSIGS 1
1026 #define NEED_KEYID2UID 1
1027 #include "keydb.c"