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