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