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