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