]> git.sommitrealweird.co.uk Git - onak.git/blob - keydb_db4.c
6fbf9da5392798159cac6e13682ea087f154ed16
[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         int        maxlocks;
281
282         snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir,
283                         DB4_UPGRADE_FILE);
284         ret = stat(buf, &statbuf);
285         while ((ret == 0) || (errno != ENOENT)) {
286                 if (ret != 0) {
287                         logthing(LOGTHING_CRITICAL, "Couldn't stat upgrade "
288                                 "lock file: %s (%d)", strerror(errno), ret);
289                         exit(1);
290                 }
291                 logthing(LOGTHING_DEBUG, "DB4 upgrade in progress; waiting.");
292                 sleep(5);
293                 ret = stat(buf, &statbuf);
294         }
295         ret = 0;
296
297         snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", config.db_dir);
298         numdb = fopen(buf, "r");
299         if (numdb != NULL) {
300                 if (fgets(buf, sizeof(buf), numdb) != NULL) {
301                         numdbs = atoi(buf);
302                 }
303                 fclose(numdb);
304         } else if (!readonly) {
305                 logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s",
306                                 strerror(errno));
307                 numdb = fopen(buf, "w");
308                 if (numdb != NULL) {
309                         fprintf(numdb, "%d", numdbs);
310                         fclose(numdb);
311                 } else {
312                         logthing(LOGTHING_ERROR,
313                                 "Couldn't write num_keydb: %s",
314                                 strerror(errno));
315                 }
316         }
317
318         dbconns = calloc(numdbs, sizeof (DB *));
319         if (dbconns == NULL) {
320                 logthing(LOGTHING_CRITICAL,
321                                 "Couldn't allocate memory for dbconns");
322                 ret = 1;
323         }
324
325         if (ret == 0) {
326                 ret = db_env_create(&dbenv, 0);
327                 if (ret != 0) {
328                         logthing(LOGTHING_CRITICAL,
329                                 "db_env_create: %s", db_strerror(ret));
330                 }
331         }
332
333         /*
334          * Up the number of locks we're allowed at once. We base this on
335          * the maximum number of keys we're going to return.
336          */
337         maxlocks = config.maxkeys * 16;
338         if (maxlocks < 1000) {
339                 maxlocks = 1000;
340         }
341         dbenv->set_lk_max_locks(dbenv, maxlocks);
342         dbenv->set_lk_max_objects(dbenv, maxlocks);
343
344         /*
345          * Enable deadlock detection so that we don't block indefinitely on
346          * anything. What we really want is simple 2 state locks, but I'm not
347          * sure how to make the standard DB functions do that yet.
348          */
349         if (ret == 0) {
350                 dbenv->set_errcall(dbenv, &db4_errfunc);
351                 ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT);
352                 if (ret != 0) {
353                         logthing(LOGTHING_CRITICAL,
354                                 "db_env_create: %s", db_strerror(ret));
355                 }
356         }
357
358         if (ret == 0) {
359                 ret = dbenv->open(dbenv, config.db_dir,
360                                 DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
361                                 DB_INIT_TXN |
362                                 DB_CREATE,
363                                 0);
364 #ifdef DB_VERSION_MISMATCH
365                 if (ret == DB_VERSION_MISMATCH) {
366                         dbenv->close(dbenv, 0);
367                         dbenv = NULL;
368                         ret = db4_upgradedb(numdbs);
369                         if (ret == 0) {
370                                 ret = db_env_create(&dbenv, 0);
371                         }
372                         if (ret == 0) {
373                                 dbenv->set_errcall(dbenv, &db4_errfunc);
374                                 dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT);
375                                 ret = dbenv->open(dbenv, config.db_dir,
376                                         DB_INIT_LOG | DB_INIT_MPOOL |
377                                         DB_INIT_LOCK | DB_INIT_TXN |
378                                         DB_CREATE | DB_RECOVER,
379                                         0);
380
381                                 if (ret == 0) {
382                                         dbenv->txn_checkpoint(dbenv,
383                                                         0,
384                                                         0,
385                                                         DB_FORCE);
386                                 }
387                         }
388                 }
389 #endif
390                 if (ret != 0) {
391                         logthing(LOGTHING_CRITICAL,
392                                         "Error opening db environment: %s (%s)",
393                                         config.db_dir,
394                                         db_strerror(ret));
395                         dbenv->close(dbenv, 0);
396                         dbenv = NULL;
397                 }
398         }
399
400         if (ret == 0) {
401                 db4_starttrans();
402
403                 for (i = 0; !ret && i < numdbs; i++) {
404                         ret = db_create(&dbconns[i], dbenv, 0);
405                         if (ret != 0) {
406                                 logthing(LOGTHING_CRITICAL,
407                                         "db_create: %s", db_strerror(ret));
408                         }
409
410                         if (ret == 0) {
411                                 snprintf(buf, 1023, "keydb.%d.db", i);
412                                 flags = DB_CREATE;
413                                 if (readonly) {
414                                         flags = DB_RDONLY;
415                                 }
416                                 ret = dbconns[i]->open(dbconns[i],
417                                                 txn,
418                                                 buf,
419                                                 "keydb",
420                                                 DB_HASH,
421                                                 flags,
422                                                 0664);
423                                 if (ret != 0) {
424                                         logthing(LOGTHING_CRITICAL,
425                                                 "Error opening key database:"
426                                                 " %s (%s)",
427                                                 buf,
428                                                 db_strerror(ret));
429                                 }
430                         }
431                 }
432
433         }
434
435         if (ret == 0) {
436                 ret = db_create(&worddb, dbenv, 0);
437                 if (ret != 0) {
438                         logthing(LOGTHING_CRITICAL, "db_create: %s",
439                                         db_strerror(ret));
440                 }
441         }
442
443         if (ret == 0) {
444                 ret = worddb->set_flags(worddb, DB_DUP);
445         }
446
447         if (ret == 0) {
448                 ret = worddb->open(worddb, txn, "worddb", "worddb", DB_BTREE,
449                                 flags,
450                                 0664);
451                 if (ret != 0) {
452                         logthing(LOGTHING_CRITICAL,
453                                         "Error opening word database: %s (%s)",
454                                         "worddb",
455                                         db_strerror(ret));
456                 }
457         }
458
459         if (ret == 0) {
460                 ret = db_create(&id32db, dbenv, 0);
461                 if (ret != 0) {
462                         logthing(LOGTHING_CRITICAL, "db_create: %s",
463                                         db_strerror(ret));
464                 }
465         }
466
467         if (ret == 0) {
468                 ret = id32db->set_flags(id32db, DB_DUP);
469         }
470
471         if (ret == 0) {
472                 ret = id32db->open(id32db, txn, "id32db", "id32db", DB_HASH,
473                                 flags,
474                                 0664);
475                 if (ret != 0) {
476                         logthing(LOGTHING_CRITICAL,
477                                         "Error opening id32 database: %s (%s)",
478                                         "id32db",
479                                         db_strerror(ret));
480                 }
481         }
482
483         if (txn != NULL) {
484                 db4_endtrans();
485         }
486
487         if (ret != 0) {
488                 db4_cleanupdb();
489                 logthing(LOGTHING_CRITICAL,
490                                 "Error opening database; exiting");
491                 exit(EXIT_FAILURE);
492         }
493         
494         return;
495 }
496
497 /**
498  *      getfullkeyid - Maps a 32bit key id to a 64bit one.
499  *      @keyid: The 32bit keyid.
500  *
501  *      This function maps a 32bit key id to the full 64bit one. It returns the
502  *      full keyid. If the key isn't found a keyid of 0 is returned.
503  */
504 static uint64_t db4_getfullkeyid(uint64_t keyid)
505 {
506         DBT       key, data;
507         DBC      *cursor = NULL;
508         uint32_t  shortkeyid = 0;
509         int       ret = 0;
510
511         if (keyid < 0x100000000LL) {
512                 ret = id32db->cursor(id32db,
513                                 txn,
514                                 &cursor,
515                                 0);   /* flags */
516
517                 shortkeyid = keyid & 0xFFFFFFFF;
518
519                 memset(&key, 0, sizeof(key));
520                 memset(&data, 0, sizeof(data));
521                 key.data = &shortkeyid;
522                 key.size = sizeof(shortkeyid);
523                 data.flags = DB_DBT_MALLOC;
524
525                 ret = cursor->c_get(cursor,
526                         &key,
527                         &data,
528                         DB_SET);
529
530                 if (ret == 0) {
531                         keyid = *(uint64_t *) data.data;
532
533                         if (data.data != NULL) {
534                                 free(data.data);
535                                 data.data = NULL;
536                         }
537                 }
538
539                 ret = cursor->c_close(cursor);
540                 cursor = NULL;
541         }
542         
543         return keyid;
544 }
545
546 /**
547  *      fetch_key - Given a keyid fetch the key from storage.
548  *      @keyid: The keyid to fetch.
549  *      @publickey: A pointer to a structure to return the key in.
550  *      @intrans: If we're already in a transaction.
551  *
552  *      We use the hex representation of the keyid as the filename to fetch the
553  *      key from. The key is stored in the file as a binary OpenPGP stream of
554  *      packets, so we can just use read_openpgp_stream() to read the packets
555  *      in and then parse_keys() to parse the packets into a publickey
556  *      structure.
557  */
558 static int db4_fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
559                 bool intrans)
560 {
561         struct openpgp_packet_list *packets = NULL;
562         DBT key, data;
563         int ret = 0;
564         int numkeys = 0;
565         struct buffer_ctx fetchbuf;
566
567         if (keyid < 0x100000000LL) {
568                 keyid = db4_getfullkeyid(keyid);
569         }
570
571         memset(&key, 0, sizeof(key));
572         memset(&data, 0, sizeof(data));
573
574         data.size = 0;
575         data.data = NULL;
576
577         key.size = sizeof(keyid);
578         key.data = &keyid;
579
580         if (!intrans) {
581                 db4_starttrans();
582         }
583
584         ret = keydb(keyid)->get(keydb(keyid),
585                         txn,
586                         &key,
587                         &data,
588                         0); /* flags*/
589         
590         if (ret == 0) {
591                 fetchbuf.buffer = data.data;
592                 fetchbuf.offset = 0;
593                 fetchbuf.size = data.size;
594                 read_openpgp_stream(buffer_fetchchar, &fetchbuf,
595                                 &packets, 0);
596                 parse_keys(packets, publickey);
597                 free_packet_list(packets);
598                 packets = NULL;
599                 numkeys++;
600         } else if (ret != DB_NOTFOUND) {
601                 logthing(LOGTHING_ERROR,
602                                 "Problem retrieving key: %s",
603                                 db_strerror(ret));
604         }
605
606         if (!intrans) {
607                 db4_endtrans();
608         }
609
610         return (numkeys);
611 }
612
613 int worddb_cmp(const void *d1, const void *d2)
614 {
615         return memcmp(d1, d2, 12);
616 }
617
618 /**
619  *      fetch_key_text - Trys to find the keys that contain the supplied text.
620  *      @search: The text to search for.
621  *      @publickey: A pointer to a structure to return the key in.
622  *
623  *      This function searches for the supplied text and returns the keys that
624  *      contain it.
625  */
626 static int db4_fetch_key_text(const char *search,
627                 struct openpgp_publickey **publickey)
628 {
629         DBC *cursor = NULL;
630         DBT key, data;
631         int ret;
632         uint64_t keyid;
633         int i;
634         int numkeys;
635         char *searchtext = NULL;
636         struct ll *wordlist = NULL;
637         struct ll *curword = NULL;
638         struct keyarray keylist = { NULL, 0, 0 };
639         struct keyarray newkeylist = { NULL, 0, 0 };
640         int firstpass = 1;
641
642         numkeys = 0;
643         searchtext = strdup(search);
644         wordlist = makewordlist(wordlist, searchtext);
645
646         for (curword = wordlist; curword != NULL; curword = curword->next) {
647                 db4_starttrans();
648
649                 ret = worddb->cursor(worddb,
650                                 txn,
651                                 &cursor,
652                                 0);   /* flags */
653
654                 memset(&key, 0, sizeof(key));
655                 memset(&data, 0, sizeof(data));
656                 key.data = curword->object;
657                 key.size = strlen(curword->object);
658                 data.flags = DB_DBT_MALLOC;
659                 ret = cursor->c_get(cursor,
660                                 &key,
661                                 &data,
662                                 DB_SET);
663                 while (ret == 0 && strncmp(key.data, curword->object,
664                                         key.size) == 0 &&
665                                 ((char *) curword->object)[key.size] == 0) {
666                         keyid = 0;
667                         for (i = 4; i < 12; i++) {
668                                 keyid <<= 8;
669                                 keyid += ((unsigned char *)
670                                                 data.data)[i];
671                         }
672
673                         /*
674                          * Only add the keys containing this word if this is
675                          * our first pass (ie we have no existing key list),
676                          * or the key contained a previous word.
677                          */
678                         if (firstpass || array_find(&keylist, keyid)) {
679                                 array_add(&newkeylist, keyid);
680                         }
681
682                         free(data.data);
683                         data.data = NULL;
684
685                         ret = cursor->c_get(cursor,
686                                         &key,
687                                         &data,
688                                         DB_NEXT);
689                 }
690                 array_free(&keylist);
691                 keylist = newkeylist;
692                 newkeylist.keys = NULL;
693                 newkeylist.count = newkeylist.size = 0;
694                 if (data.data != NULL) {
695                         free(data.data);
696                         data.data = NULL;
697                 }
698                 ret = cursor->c_close(cursor);
699                 cursor = NULL;
700                 firstpass = 0;
701                 db4_endtrans();
702         }
703         llfree(wordlist, NULL);
704         wordlist = NULL;
705
706         if (keylist.count > config.maxkeys) {
707                 keylist.count = config.maxkeys;
708         }
709         
710         db4_starttrans();
711         for (i = 0; i < keylist.count; i++) {
712                 numkeys += db4_fetch_key(keylist.keys[i],
713                         publickey,
714                         true);
715         }
716         array_free(&keylist);
717         free(searchtext);
718         searchtext = NULL;
719
720         db4_endtrans();
721         
722         return (numkeys);
723 }
724
725 /**
726  *      delete_key - Given a keyid delete the key from storage.
727  *      @keyid: The keyid to delete.
728  *      @intrans: If we're already in a transaction.
729  *
730  *      This function deletes a public key from whatever storage mechanism we
731  *      are using. Returns 0 if the key existed.
732  */
733 static int db4_delete_key(uint64_t keyid, bool intrans)
734 {
735         struct openpgp_publickey *publickey = NULL;
736         DBT key, data;
737         DBC *cursor = NULL;
738         uint32_t   shortkeyid = 0;
739         uint64_t  *subkeyids = NULL;
740         int ret = 0;
741         int i;
742         char **uids = NULL;
743         char *primary = NULL;
744         unsigned char worddb_data[12];
745         struct ll *wordlist = NULL;
746         struct ll *curword  = NULL;
747         bool deadlock = false;
748
749         if (!intrans) {
750                 db4_starttrans();
751         }
752
753         db4_fetch_key(keyid, &publickey, true);
754
755         /*
756          * Walk through the uids removing the words from the worddb.
757          */
758         if (publickey != NULL) {
759                 uids = keyuids(publickey, &primary);
760         }
761         if (uids != NULL) {
762                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
763                         wordlist = makewordlist(wordlist, uids[i]);
764                 }
765                                 
766                 ret = worddb->cursor(worddb,
767                         txn,
768                         &cursor,
769                         0);   /* flags */
770
771                 for (curword = wordlist; curword != NULL && !deadlock;
772                                 curword = curword->next) {
773                         memset(&key, 0, sizeof(key));
774                         memset(&data, 0, sizeof(data));
775                         key.data = curword->object;
776                         key.size = strlen(key.data);
777                         data.data = worddb_data;
778                         data.size = sizeof(worddb_data);
779
780                         /*
781                          * Our data is the key creation time followed by the
782                          * key id.
783                          */
784                         worddb_data[ 0] = publickey->publickey->data[1];
785                         worddb_data[ 1] = publickey->publickey->data[2];
786                         worddb_data[ 2] = publickey->publickey->data[3];
787                         worddb_data[ 3] = publickey->publickey->data[4];
788                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
789                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
790                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
791                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
792                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
793                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
794                         worddb_data[10] = (keyid >>  8) & 0xFF;
795                         worddb_data[11] = keyid & 0xFF; 
796
797                         ret = cursor->c_get(cursor,
798                                 &key,
799                                 &data,
800                                 DB_GET_BOTH);
801
802                         if (ret == 0) {
803                                 ret = cursor->c_del(cursor, 0);
804                                 if (ret != 0) {
805                                         logthing(LOGTHING_ERROR,
806                                                 "Problem deleting word: %s",
807                                                 db_strerror(ret));
808                                 }
809                         }
810
811                         if (ret != 0) {
812                                 logthing(LOGTHING_ERROR,
813                                         "Problem deleting word: %s",
814                                         db_strerror(ret));
815                                 if (ret == DB_LOCK_DEADLOCK) {
816                                         deadlock = true;
817                                 }
818                         }
819                 }
820                 ret = cursor->c_close(cursor);
821                 cursor = NULL;
822
823                 /*
824                  * Free our UID and word lists.
825                  */
826                 llfree(wordlist, NULL);
827                 for (i = 0; uids[i] != NULL; i++) {
828                         free(uids[i]);
829                         uids[i] = NULL;
830                 }
831                 free(uids);
832                 uids = NULL;
833                 free_publickey(publickey);
834                 publickey = NULL;
835         }
836
837         if (!deadlock) {
838                 ret = id32db->cursor(id32db,
839                         txn,
840                         &cursor,
841                         0);   /* flags */
842
843                 shortkeyid = keyid & 0xFFFFFFFF;
844
845                 memset(&key, 0, sizeof(key));
846                 memset(&data, 0, sizeof(data));
847                 key.data = &shortkeyid;
848                 key.size = sizeof(shortkeyid);
849                 data.data = &keyid;
850                 data.size = sizeof(keyid);
851
852                 ret = cursor->c_get(cursor,
853                         &key,
854                         &data,
855                         DB_GET_BOTH);
856
857                 if (ret == 0) {
858                         ret = cursor->c_del(cursor, 0);
859                         if (ret != 0) {
860                                 logthing(LOGTHING_ERROR,
861                                         "Problem deleting short keyid: %s",
862                                         db_strerror(ret));
863                         }
864                 }
865
866                 if (ret != 0) {
867                         logthing(LOGTHING_ERROR,
868                                 "Problem deleting short keyid: %s",
869                                 db_strerror(ret));
870                         if (ret == DB_LOCK_DEADLOCK) {
871                                 deadlock = true;
872                         }
873                 }
874
875                 subkeyids = keysubkeys(publickey);
876                 i = 0;
877                 while (subkeyids != NULL && subkeyids[i] != 0) {
878                         shortkeyid = subkeyids[i++] & 0xFFFFFFFF;
879
880                         memset(&key, 0, sizeof(key));
881                         memset(&data, 0, sizeof(data));
882                         key.data = &shortkeyid;
883                         key.size = sizeof(shortkeyid);
884                         data.data = &keyid;
885                         data.size = sizeof(keyid);
886
887                         ret = cursor->c_get(cursor,
888                                 &key,
889                                 &data,
890                                 DB_GET_BOTH);
891
892                         if (ret == 0) {
893                                 ret = cursor->c_del(cursor, 0);
894                                 if (ret != 0) {
895                                         logthing(LOGTHING_ERROR,
896                                                 "Problem deleting short"
897                                                 " keyid: %s",
898                                                 db_strerror(ret));
899                                 }
900                         }
901
902                         if (ret != 0) {
903                                 logthing(LOGTHING_ERROR,
904                                         "Problem deleting short keyid: %s",
905                                         db_strerror(ret));
906                                 if (ret == DB_LOCK_DEADLOCK) {
907                                         deadlock = true;
908                                 }
909                         }
910                 }
911                 if (subkeyids != NULL) {
912                         free(subkeyids);
913                         subkeyids = NULL;
914                 }
915
916                 ret = cursor->c_close(cursor);
917                 cursor = NULL;
918         }
919
920         if (!deadlock) {
921                 key.data = &keyid;
922                 key.size = sizeof(keyid);
923
924                 keydb(keyid)->del(keydb(keyid),
925                                 txn,
926                                 &key,
927                                 0); /* flags */
928         }
929
930         if (!intrans) {
931                 db4_endtrans();
932         }
933
934         return deadlock ? (-1) : (ret == DB_NOTFOUND);
935 }
936
937 /**
938  *      store_key - Takes a key and stores it.
939  *      @publickey: A pointer to the public key to store.
940  *      @intrans: If we're already in a transaction.
941  *      @update: If true the key exists and should be updated.
942  *
943  *      Again we just use the hex representation of the keyid as the filename
944  *      to store the key to. We flatten the public key to a list of OpenPGP
945  *      packets and then use write_openpgp_stream() to write the stream out to
946  *      the file. If update is true then we delete the old key first, otherwise
947  *      we trust that it doesn't exist.
948  */
949 static int db4_store_key(struct openpgp_publickey *publickey, bool intrans,
950                 bool update)
951 {
952         struct     openpgp_packet_list *packets = NULL;
953         struct     openpgp_packet_list *list_end = NULL;
954         struct     openpgp_publickey *next = NULL;
955         int        ret = 0;
956         int        i = 0;
957         struct     buffer_ctx storebuf;
958         DBT        key;
959         DBT        data;
960         uint64_t   keyid = 0;
961         uint32_t   shortkeyid = 0;
962         uint64_t  *subkeyids = NULL;
963         char     **uids = NULL;
964         char      *primary = NULL;
965         unsigned char worddb_data[12];
966         struct ll *wordlist = NULL;
967         struct ll *curword  = NULL;
968         bool       deadlock = false;
969
970         keyid = get_keyid(publickey);
971
972         if (!intrans) {
973                 db4_starttrans();
974         }
975
976         /*
977          * Delete the key if we already have it.
978          *
979          * TODO: Can we optimize this perhaps? Possibly when other data is
980          * involved as well? I suspect this is easiest and doesn't make a lot
981          * of difference though - the largest chunk of data is the keydata and
982          * it definitely needs updated.
983          */
984         if (update) {
985                 deadlock = (db4_delete_key(keyid, true) == -1);
986         }
987
988         /*
989          * Convert the key to a flat set of binary data.
990          */
991         if (!deadlock) {
992                 next = publickey->next;
993                 publickey->next = NULL;
994                 flatten_publickey(publickey, &packets, &list_end);
995                 publickey->next = next;
996
997                 storebuf.offset = 0; 
998                 storebuf.size = 8192;
999                 storebuf.buffer = malloc(8192);
1000         
1001                 write_openpgp_stream(buffer_putchar, &storebuf, packets);
1002
1003                 /*
1004                  * Now we have the key data store it in the DB; the keyid is
1005                  * the key.
1006                  */
1007                 memset(&key, 0, sizeof(key));
1008                 memset(&data, 0, sizeof(data));
1009                 key.data = &keyid;
1010                 key.size = sizeof(keyid);
1011                 data.size = storebuf.offset;
1012                 data.data = storebuf.buffer;
1013
1014                 ret = keydb(keyid)->put(keydb(keyid),
1015                                 txn,
1016                                 &key,
1017                                 &data,
1018                                 0); /* flags*/
1019                 if (ret != 0) {
1020                         logthing(LOGTHING_ERROR,
1021                                         "Problem storing key: %s",
1022                                         db_strerror(ret));
1023                         if (ret == DB_LOCK_DEADLOCK) {
1024                                 deadlock = true;
1025                         }
1026                 }
1027
1028                 free(storebuf.buffer);
1029                 storebuf.buffer = NULL;
1030                 storebuf.size = 0;
1031                 storebuf.offset = 0; 
1032         
1033                 free_packet_list(packets);
1034                 packets = NULL;
1035         }
1036
1037         /*
1038          * Walk through our uids storing the words into the db with the keyid.
1039          */
1040         if (!deadlock) {
1041                 uids = keyuids(publickey, &primary);
1042         }
1043         if (uids != NULL) {
1044                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
1045                         wordlist = makewordlist(wordlist, uids[i]);
1046                 }
1047
1048                 for (curword = wordlist; curword != NULL && !deadlock;
1049                                 curword = curword->next) {
1050                         memset(&key, 0, sizeof(key));
1051                         memset(&data, 0, sizeof(data));
1052                         key.data = curword->object;
1053                         key.size = strlen(key.data);
1054                         data.data = worddb_data;
1055                         data.size = sizeof(worddb_data);
1056
1057                         /*
1058                          * Our data is the key creation time followed by the
1059                          * key id.
1060                          */
1061                         worddb_data[ 0] = publickey->publickey->data[1];
1062                         worddb_data[ 1] = publickey->publickey->data[2];
1063                         worddb_data[ 2] = publickey->publickey->data[3];
1064                         worddb_data[ 3] = publickey->publickey->data[4];
1065                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
1066                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
1067                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
1068                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
1069                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
1070                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
1071                         worddb_data[10] = (keyid >>  8) & 0xFF;
1072                         worddb_data[11] = keyid & 0xFF; 
1073                         ret = worddb->put(worddb,
1074                                 txn,
1075                                 &key,
1076                                 &data,
1077                                 0);
1078                         if (ret != 0) {
1079                                 logthing(LOGTHING_ERROR,
1080                                         "Problem storing word: %s",
1081                                         db_strerror(ret));
1082                                 if (ret == DB_LOCK_DEADLOCK) {
1083                                         deadlock = true;
1084                                 }
1085                         }
1086                 }
1087
1088                 /*
1089                  * Free our UID and word lists.
1090                  */
1091                 llfree(wordlist, NULL);
1092                 for (i = 0; uids[i] != NULL; i++) {
1093                         free(uids[i]);
1094                         uids[i] = NULL;
1095                 }
1096                 free(uids);
1097                 uids = NULL;
1098         }
1099
1100         /*
1101          * Write the truncated 32 bit keyid so we can lookup the full id for
1102          * queries.
1103          */
1104         if (!deadlock) {
1105                 shortkeyid = keyid & 0xFFFFFFFF;
1106
1107                 memset(&key, 0, sizeof(key));
1108                 memset(&data, 0, sizeof(data));
1109                 key.data = &shortkeyid;
1110                 key.size = sizeof(shortkeyid);
1111                 data.data = &keyid;
1112                 data.size = sizeof(keyid);
1113
1114                 ret = id32db->put(id32db,
1115                         txn,
1116                         &key,
1117                         &data,
1118                         0);
1119                 if (ret != 0) {
1120                         logthing(LOGTHING_ERROR,
1121                                 "Problem storing short keyid: %s",
1122                                 db_strerror(ret));
1123                         if (ret == DB_LOCK_DEADLOCK) {
1124                                 deadlock = true;
1125                         }
1126                 }
1127         }
1128
1129         if (!deadlock) {
1130                 subkeyids = keysubkeys(publickey);
1131                 i = 0;
1132                 while (subkeyids != NULL && subkeyids[i] != 0) {
1133                         shortkeyid = subkeyids[i++] & 0xFFFFFFFF;
1134
1135                         memset(&key, 0, sizeof(key));
1136                         memset(&data, 0, sizeof(data));
1137                         key.data = &shortkeyid;
1138                         key.size = sizeof(shortkeyid);
1139                         data.data = &keyid;
1140                         data.size = sizeof(keyid);
1141
1142                         ret = id32db->put(id32db,
1143                                 txn,
1144                                 &key,
1145                                 &data,
1146                                 0);
1147                         if (ret != 0) {
1148                                 logthing(LOGTHING_ERROR,
1149                                         "Problem storing short keyid: %s",
1150                                         db_strerror(ret));
1151                                 if (ret == DB_LOCK_DEADLOCK) {
1152                                         deadlock = true;
1153                                 }
1154                         }
1155                 }
1156                 if (subkeyids != NULL) {
1157                         free(subkeyids);
1158                         subkeyids = NULL;
1159                 }
1160         }
1161
1162         if (!intrans) {
1163                 db4_endtrans();
1164         }
1165
1166         return deadlock ? -1 : 0 ;
1167 }
1168
1169 /**
1170  *      iterate_keys - call a function once for each key in the db.
1171  *      @iterfunc: The function to call.
1172  *      @ctx: A context pointer
1173  *
1174  *      Calls iterfunc once for each key in the database. ctx is passed
1175  *      unaltered to iterfunc. This function is intended to aid database dumps
1176  *      and statistic calculations.
1177  *
1178  *      Returns the number of keys we iterated over.
1179  */
1180 static int db4_iterate_keys(void (*iterfunc)(void *ctx,
1181                 struct openpgp_publickey *key), void *ctx)
1182 {
1183         DBT                         dbkey, data;
1184         DBC                        *cursor = NULL;
1185         int                         ret = 0;
1186         int                         i = 0;
1187         int                         numkeys = 0;
1188         struct buffer_ctx           fetchbuf;
1189         struct openpgp_packet_list *packets = NULL;
1190         struct openpgp_publickey   *key = NULL;
1191
1192         for (i = 0; i < numdbs; i++) {
1193                 ret = dbconns[i]->cursor(dbconns[i],
1194                         NULL,
1195                         &cursor,
1196                         0);   /* flags */
1197
1198                 memset(&dbkey, 0, sizeof(dbkey));
1199                 memset(&data, 0, sizeof(data));
1200                 ret = cursor->c_get(cursor, &dbkey, &data, DB_NEXT);
1201                 while (ret == 0) {
1202                         fetchbuf.buffer = data.data;
1203                         fetchbuf.offset = 0;
1204                         fetchbuf.size = data.size;
1205                         read_openpgp_stream(buffer_fetchchar, &fetchbuf,
1206                                 &packets, 0);
1207                         parse_keys(packets, &key);
1208
1209                         iterfunc(ctx, key);
1210                         
1211                         free_publickey(key);
1212                         key = NULL;
1213                         free_packet_list(packets);
1214                         packets = NULL;
1215                         
1216                         memset(&dbkey, 0, sizeof(dbkey));
1217                         memset(&data, 0, sizeof(data));
1218                         ret = cursor->c_get(cursor, &dbkey, &data,
1219                                         DB_NEXT);
1220                         numkeys++;
1221                 }
1222                 if (ret != DB_NOTFOUND) {
1223                         logthing(LOGTHING_ERROR,
1224                                 "Problem reading key: %s",
1225                                 db_strerror(ret));
1226                 }
1227
1228                 ret = cursor->c_close(cursor);
1229                 cursor = NULL;
1230         }
1231         
1232         return numkeys;
1233 }
1234
1235 /*
1236  * Include the basic keydb routines.
1237  */
1238 #define NEED_GETKEYSIGS 1
1239 #define NEED_KEYID2UID 1
1240 #define NEED_UPDATEKEYS 1
1241 #include "keydb.c"
1242
1243 struct dbfuncs keydb_db4_funcs = {
1244         .initdb                 = db4_initdb,
1245         .cleanupdb              = db4_cleanupdb,
1246         .starttrans             = db4_starttrans,
1247         .endtrans               = db4_endtrans,
1248         .fetch_key              = db4_fetch_key,
1249         .fetch_key_text         = db4_fetch_key_text,
1250         .store_key              = db4_store_key,
1251         .update_keys            = generic_update_keys,
1252         .delete_key             = db4_delete_key,
1253         .getkeysigs             = generic_getkeysigs,
1254         .cached_getkeysigs      = generic_cached_getkeysigs,
1255         .keyid2uid              = generic_keyid2uid,
1256         .getfullkeyid           = db4_getfullkeyid,
1257         .iterate_keys           = db4_iterate_keys,
1258 };