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