b3441b7edaa21db571f594f9a010de58b50ffca9
[onak.git] / keydb_db3.c
1 /*
2  * keydb_db3.c - Routines to store and fetch keys in a DB3 database.
3  *
4  * Jonathan McDowell <noodles@earth.li>
5  *
6  * Copyright 2002 Project Purple
7  *
8  * $Id: keydb_db3.c,v 1.22 2003/10/10 16:57:27 noodles Exp $
9  */
10
11 #include <assert.h>
12 #include <sys/types.h>
13 #include <sys/uio.h>
14 #include <ctype.h>
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21
22 #include <db.h>
23
24 #include "charfuncs.h"
25 #include "keydb.h"
26 #include "keyid.h"
27 #include "decodekey.h"
28 #include "keystructs.h"
29 #include "mem.h"
30 #include "log.h"
31 #include "onak-conf.h"
32 #include "parsekey.h"
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  *      txn - our current transaction id.
56  */
57 static DB_TXN *txn = NULL;
58
59 DB *keydb(uint64_t keyid)
60 {
61         uint64_t keytrun;
62
63         keytrun = keyid >> 8;
64
65         return(dbconns[keytrun % numdbs]);
66 }
67
68 /**
69  *      makewordlist - Takes a string and splits it into a set of unique words.
70  *      @wordlist: The current word list.
71  *      @words: The string to split and add.
72  *
73  *      We take words and split it on non alpha numeric characters. These get
74  *      added to the word list if they're not already present. If the wordlist
75  *      is NULL then we start a new list, otherwise it's search for already
76  *      added words. Note that words is modified in the process of scanning.
77  *
78  *      Returns the new word list.
79  */
80 struct ll *makewordlist(struct ll *wordlist, char *word)
81 {
82         char *start = NULL;
83         char *end = NULL;
84
85         /*
86          * Walk through the words string, spliting on non alphanumerics and
87          * then checking if the word already exists in the list. If not then
88          * we add it.
89          */
90         end = word;
91         while (end != NULL && *end != 0) {
92                 start = end;
93                 while (*start != 0 && !isalnum(*start)) {
94                         start++;
95                 }
96                 end = start;
97                 while (*end != 0 && isalnum(*end)) {
98                         *end = tolower(*end);
99                         end++;
100                 }
101                 if (end - start > 1) {
102                         if (*end != 0) {
103                                 *end = 0;
104                                 end++;
105                         }
106                         
107                         if (llfind(wordlist, start,
108                                         strcmp) == NULL) {
109                                 wordlist = lladd(wordlist,
110                                                 start);
111                         }
112                 }
113         }
114
115         return wordlist;
116 }
117
118 /**
119  *      initdb - Initialize the key database.
120  *
121  *      This function should be called before any of the other functions in
122  *      this file are called in order to allow the DB to be initialized ready
123  *      for access.
124  */
125 void initdb(void)
126 {
127         char  buf[1024];
128         FILE *numdb = NULL;
129         int   ret = 0;
130         int   i = 0;
131
132         snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", config.db_dir);
133         numdb = fopen(buf, "r");
134         if (numdb != NULL) {
135                 if (fgets(buf, sizeof(buf), numdb) != NULL) {
136                         numdbs = atoi(buf);
137                 }
138                 fclose(numdb);
139         } else {
140                 logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s",
141                                 strerror(errno));
142                 numdb = fopen(buf, "w");
143                 if (numdb != NULL) {
144                         fprintf(numdb, "%d", numdbs);
145                         fclose(numdb);
146                 } else {
147                         logthing(LOGTHING_ERROR,
148                                 "Couldn't write num_keydb: %s",
149                                 strerror(errno));
150                 }
151         }
152
153         dbconns = malloc(sizeof (DB *) * numdbs);
154         if (dbconns == NULL) {
155                 logthing(LOGTHING_CRITICAL,
156                                 "Couldn't allocate memory for dbconns");
157                 exit(1);
158         }
159
160         ret = db_env_create(&dbenv, 0);
161         if (ret != 0) {
162                 logthing(LOGTHING_CRITICAL,
163                         "db_env_create: %s", db_strerror(ret));
164                 exit(1);
165         }
166
167         /*
168          * Enable deadlock detection so that we don't block indefinitely on
169          * anything. What we really want is simple 2 state locks, but I'm not
170          * sure how to make the standard DB functions do that yet.
171          */
172         ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT);
173         if (ret != 0) {
174                 logthing(LOGTHING_CRITICAL,
175                         "db_env_create: %s", db_strerror(ret));
176                 exit(1);
177         }
178
179         ret = dbenv->open(dbenv, config.db_dir,
180                         DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
181                         DB_INIT_TXN |
182                         DB_CREATE,
183                         0);
184         if (ret != 0) {
185                 logthing(LOGTHING_CRITICAL,
186                                 "Error opening db environment: %s (%s)",
187                                 config.db_dir,
188                                 db_strerror(ret));
189                 exit(1);
190         }
191
192         for (i = 0; i < numdbs; i++) {
193                 ret = db_create(&dbconns[i], dbenv, 0);
194                 if (ret != 0) {
195                         logthing(LOGTHING_CRITICAL,
196                                 "db_create: %s", db_strerror(ret));
197                         exit(1);
198                 }
199
200                 snprintf(buf, 1023, "keydb.%d.db", i);
201                 ret = dbconns[i]->open(dbconns[i], buf,
202                         NULL,
203                         DB_HASH,
204                         DB_CREATE,
205                         0664);
206                 if (ret != 0) {
207                         logthing(LOGTHING_CRITICAL,
208                                 "Error opening key database: %s (%s)",
209                                 buf,
210                                 db_strerror(ret));
211                         exit(1);
212                 }
213         }
214
215         ret = db_create(&worddb, dbenv, 0);
216         if (ret != 0) {
217                 logthing(LOGTHING_CRITICAL, "db_create: %s", db_strerror(ret));
218                 exit(1);
219         }
220         ret = worddb->set_flags(worddb, DB_DUP);
221
222         ret = worddb->open(worddb, "worddb", NULL, DB_BTREE,
223                         DB_CREATE,
224                         0664);
225         if (ret != 0) {
226                 logthing(LOGTHING_CRITICAL,
227                                 "Error opening word database: %s (%s)",
228                                 "worddb",
229                                 db_strerror(ret));
230                 exit(1);
231         }
232         
233         return;
234 }
235
236 /**
237  *      cleanupdb - De-initialize the key database.
238  *
239  *      This function should be called upon program exit to allow the DB to
240  *      cleanup after itself.
241  */
242 void cleanupdb(void)
243 {
244         int i = 0;
245
246         txn_checkpoint(dbenv, 0, 0, 0);
247         worddb->close(worddb, 0);
248         worddb = NULL;
249         for (i = 0; i < numdbs; i++) {
250                 dbconns[i]->close(dbconns[i], 0);
251                 dbconns[i] = NULL;
252         }
253         dbenv->close(dbenv, 0);
254         dbenv = NULL;
255 }
256
257 /**
258  *      starttrans - Start a transaction.
259  *
260  *      Start a transaction. Intended to be used if we're about to perform many
261  *      operations on the database to help speed it all up, or if we want
262  *      something to only succeed if all relevant operations are successful.
263  */
264 bool starttrans(void)
265 {
266         int ret;
267
268         assert(dbenv != NULL);
269         assert(txn == NULL);
270
271         ret = txn_begin(dbenv,
272                 NULL, /* No parent transaction */
273                 &txn,
274                 0);
275         if (ret != 0) {
276                 logthing(LOGTHING_CRITICAL,
277                                 "Error starting transaction: %s",
278                                 db_strerror(ret));
279                 exit(1);
280         }
281
282         return true;
283 }
284
285 /**
286  *      endtrans - End a transaction.
287  *
288  *      Ends a transaction.
289  */
290 void endtrans(void)
291 {
292         int ret;
293
294         assert(dbenv != NULL);
295         assert(txn != NULL);
296
297         ret = txn_commit(txn,
298                 0);
299         if (ret != 0) {
300                 logthing(LOGTHING_CRITICAL,
301                                 "Error ending transaction: %s",
302                                 db_strerror(ret));
303                 exit(1);
304         }
305         txn = NULL;
306
307         return;
308 }
309
310 /**
311  *      fetch_key - Given a keyid fetch the key from storage.
312  *      @keyid: The keyid to fetch.
313  *      @publickey: A pointer to a structure to return the key in.
314  *      @intrans: If we're already in a transaction.
315  *
316  *      We use the hex representation of the keyid as the filename to fetch the
317  *      key from. The key is stored in the file as a binary OpenPGP stream of
318  *      packets, so we can just use read_openpgp_stream() to read the packets
319  *      in and then parse_keys() to parse the packets into a publickey
320  *      structure.
321  */
322 int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
323                 bool intrans)
324 {
325         struct openpgp_packet_list *packets = NULL;
326         DBT key, data;
327         int ret = 0;
328         int numkeys = 0;
329         struct buffer_ctx fetchbuf;
330
331         memset(&key, 0, sizeof(key));
332         memset(&data, 0, sizeof(data));
333
334         data.size = 0;
335         data.data = NULL;
336
337         key.size = sizeof(keyid);
338         key.data = &keyid;
339         keyid &= 0xFFFFFFFF;
340
341         if (!intrans) {
342                 starttrans();
343         }
344
345         ret = keydb(keyid)->get(keydb(keyid),
346                         txn,
347                         &key,
348                         &data,
349                         0); /* flags*/
350         
351         if (ret == 0) {
352                 fetchbuf.buffer = data.data;
353                 fetchbuf.offset = 0;
354                 fetchbuf.size = data.size;
355                 read_openpgp_stream(buffer_fetchchar, &fetchbuf,
356                                 &packets, 0);
357                 parse_keys(packets, publickey);
358                 free_packet_list(packets);
359                 packets = NULL;
360                 numkeys++;
361         } else if (ret != DB_NOTFOUND) {
362                 logthing(LOGTHING_ERROR,
363                                 "Problem retrieving key: %s",
364                                 db_strerror(ret));
365         }
366
367         if (!intrans) {
368                 endtrans();
369         }
370
371         return (numkeys);
372 }
373
374 int worddb_cmp(const char *d1, const char *d2)
375 {
376         return memcmp(d1, d2, 12);
377 }
378
379 /**
380  *      fetch_key_text - Trys to find the keys that contain the supplied text.
381  *      @search: The text to search for.
382  *      @publickey: A pointer to a structure to return the key in.
383  *
384  *      This function searches for the supplied text and returns the keys that
385  *      contain it.
386  */
387 int fetch_key_text(const char *search, struct openpgp_publickey **publickey)
388 {
389         DBC *cursor = NULL;
390         DBT key, data;
391         int ret;
392         uint64_t keyid;
393         int i;
394         int numkeys;
395         char *searchtext = NULL;
396         struct ll *wordlist = NULL;
397         struct ll *curword = NULL;
398         struct ll *keylist = NULL;
399         struct ll *newkeylist = NULL;
400
401         numkeys = 0;
402         searchtext = strdup(search);
403         wordlist = makewordlist(wordlist, searchtext);
404
405         starttrans();
406
407         ret = worddb->cursor(worddb,
408                         txn,
409                         &cursor,
410                         0);   /* flags */
411
412         for (curword = wordlist; curword != NULL; curword = curword->next) {
413                 memset(&key, 0, sizeof(key));
414                 memset(&data, 0, sizeof(data));
415                 key.data = curword->object;
416                 key.size = strlen(curword->object);
417                 data.flags = DB_DBT_MALLOC;
418                 ret = cursor->c_get(cursor,
419                                 &key,
420                                 &data,
421                                 DB_SET);
422                 while (ret == 0 && strncmp(key.data, curword->object,
423                                         key.size) == 0 &&
424                                 ((char *) curword->object)[key.size] == 0) {
425                         keyid = 0;
426                         for (i = 4; i < 12; i++) {
427                                 keyid <<= 8;
428                                 keyid += ((unsigned char *)
429                                                 data.data)[i];
430                         }
431
432                         if (keylist == NULL ||
433                                         llfind(keylist, data.data,
434                                                 worddb_cmp) != NULL) {
435                                 newkeylist = lladd(newkeylist, data.data);
436                                 data.data = NULL;
437                         } else {
438                                 free(data.data);
439                                 data.data = NULL;
440                         }
441                         ret = cursor->c_get(cursor,
442                                         &key,
443                                         &data,
444                                         DB_NEXT);
445                 }
446                 llfree(keylist, free);
447                 keylist = newkeylist;
448                 newkeylist = NULL;
449                 if (data.data != NULL) {
450                         free(data.data);
451                         data.data = NULL;
452                 }
453         }
454         llfree(wordlist, NULL);
455         wordlist = NULL;
456         
457         for (newkeylist = keylist;
458                         newkeylist != NULL && numkeys < config.maxkeys;
459                         newkeylist = newkeylist->next) {
460
461                         keyid = 0;
462                         for (i = 4; i < 12; i++) {
463                                 keyid <<= 8;
464                                 keyid += ((unsigned char *)
465                                                 newkeylist->object)[i];
466                         }
467
468                         numkeys += fetch_key(keyid,
469                                         publickey,
470                                         true);
471         }
472         llfree(keylist, free);
473         keylist = NULL;
474         free(searchtext);
475         searchtext = NULL;
476
477         ret = cursor->c_close(cursor);
478         cursor = NULL;
479
480         endtrans();
481         
482         return (numkeys);
483 }
484
485 /**
486  *      store_key - Takes a key and stores it.
487  *      @publickey: A pointer to the public key to store.
488  *      @intrans: If we're already in a transaction.
489  *      @update: If true the key exists and should be updated.
490  *
491  *      Again we just use the hex representation of the keyid as the filename
492  *      to store the key to. We flatten the public key to a list of OpenPGP
493  *      packets and then use write_openpgp_stream() to write the stream out to
494  *      the file. If update is true then we delete the old key first, otherwise
495  *      we trust that it doesn't exist.
496  */
497 int store_key(struct openpgp_publickey *publickey, bool intrans, bool update)
498 {
499         struct     openpgp_packet_list *packets = NULL;
500         struct     openpgp_packet_list *list_end = NULL;
501         struct     openpgp_publickey *next = NULL;
502         int        ret = 0;
503         int        i = 0;
504         struct     buffer_ctx storebuf;
505         DBT        key;
506         DBT        data;
507         uint64_t   keyid = 0;
508         char     **uids = NULL;
509         char      *primary = NULL;
510         unsigned char worddb_data[12];
511         struct ll *wordlist = NULL;
512         struct ll *curword  = NULL;
513         bool       deadlock = false;
514
515         keyid = get_keyid(publickey);
516
517         if (!intrans) {
518                 starttrans();
519         }
520
521         /*
522          * Delete the key if we already have it.
523          *
524          * TODO: Can we optimize this perhaps? Possibly when other data is
525          * involved as well? I suspect this is easiest and doesn't make a lot
526          * of difference though - the largest chunk of data is the keydata and
527          * it definitely needs updated.
528          */
529         if (update) {
530                 deadlock = (delete_key(keyid, true) == -1);
531         }
532
533         /*
534          * Convert the key to a flat set of binary data.
535          */
536         if (!deadlock) {
537                 next = publickey->next;
538                 publickey->next = NULL;
539                 flatten_publickey(publickey, &packets, &list_end);
540                 publickey->next = next;
541
542                 storebuf.offset = 0; 
543                 storebuf.size = 8192;
544                 storebuf.buffer = malloc(8192);
545         
546                 write_openpgp_stream(buffer_putchar, &storebuf, packets);
547
548                 /*
549                  * Now we have the key data store it in the DB; the keyid is
550                  * the key.
551                  */
552                 memset(&key, 0, sizeof(key));
553                 memset(&data, 0, sizeof(data));
554                 key.data = &keyid;
555                 key.size = sizeof(keyid);
556                 keyid &= 0xFFFFFFFF;
557                 data.size = storebuf.offset;
558                 data.data = storebuf.buffer;
559
560                 ret = keydb(keyid)->put(keydb(keyid),
561                                 txn,
562                                 &key,
563                                 &data,
564                                 0); /* flags*/
565                 if (ret != 0) {
566                         logthing(LOGTHING_ERROR,
567                                         "Problem storing key: %s",
568                                         db_strerror(ret));
569                         if (ret == DB_LOCK_DEADLOCK) {
570                                 deadlock = true;
571                         }
572                 }
573
574                 free(storebuf.buffer);
575                 storebuf.buffer = NULL;
576                 storebuf.size = 0;
577                 storebuf.offset = 0; 
578         
579                 free_packet_list(packets);
580                 packets = NULL;
581         }
582
583         /*
584          * Walk through our uids storing the words into the db with the keyid.
585          */
586         if (!deadlock) {
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                 for (curword = wordlist; curword != NULL && !deadlock;
595                                 curword = curword->next) {
596                         memset(&key, 0, sizeof(key));
597                         memset(&data, 0, sizeof(data));
598                         key.data = curword->object;
599                         key.size = strlen(key.data);
600                         data.data = worddb_data;
601                         data.size = sizeof(worddb_data);
602
603                         /*
604                          * Our data is the key creation time followed by the
605                          * key id.
606                          */
607                         worddb_data[ 0] = publickey->publickey->data[1];
608                         worddb_data[ 1] = publickey->publickey->data[2];
609                         worddb_data[ 2] = publickey->publickey->data[3];
610                         worddb_data[ 3] = publickey->publickey->data[4];
611                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
612                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
613                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
614                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
615                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
616                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
617                         worddb_data[10] = (keyid >>  8) & 0xFF;
618                         worddb_data[11] = keyid & 0xFF; 
619                         ret = worddb->put(worddb,
620                                 txn,
621                                 &key,
622                                 &data,
623                                 0);
624                         if (ret != 0) {
625                                 logthing(LOGTHING_ERROR,
626                                         "Problem storing word: %s",
627                                         db_strerror(ret));
628                                 if (ret == DB_LOCK_DEADLOCK) {
629                                         deadlock = true;
630                                 }
631                         }
632                 }
633
634                 /*
635                  * Free our UID and word lists.
636                  */
637                 llfree(wordlist, NULL);
638                 for (i = 0; uids[i] != NULL; i++) {
639                         free(uids[i]);
640                         uids[i] = NULL;
641                 }
642                 free(uids);
643                 uids = NULL;
644         }
645
646         if (!intrans) {
647                 endtrans();
648         }
649
650         return deadlock ? -1 : 0 ;
651 }
652
653 /**
654  *      delete_key - Given a keyid delete the key from storage.
655  *      @keyid: The keyid to delete.
656  *      @intrans: If we're already in a transaction.
657  *
658  *      This function deletes a public key from whatever storage mechanism we
659  *      are using. Returns 0 if the key existed.
660  */
661 int delete_key(uint64_t keyid, bool intrans)
662 {
663         struct openpgp_publickey *publickey = NULL;
664         DBT key, data;
665         DBC *cursor = NULL;
666         int ret = 0;
667         int i;
668         char **uids = NULL;
669         char *primary = NULL;
670         unsigned char worddb_data[12];
671         struct ll *wordlist = NULL;
672         struct ll *curword  = NULL;
673         bool deadlock = false;
674
675         keyid &= 0xFFFFFFFF;
676
677         if (!intrans) {
678                 starttrans();
679         }
680
681         fetch_key(keyid, &publickey, true);
682
683         /*
684          * Walk through the uids removing the words from the worddb.
685          */
686         if (publickey != NULL) {
687                 uids = keyuids(publickey, &primary);
688         }
689         if (uids != NULL) {
690                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
691                         wordlist = makewordlist(wordlist, uids[i]);
692                 }
693                                 
694                 ret = worddb->cursor(worddb,
695                         txn,
696                         &cursor,
697                         0);   /* flags */
698
699                 for (curword = wordlist; curword != NULL && !deadlock;
700                                 curword = curword->next) {
701                         memset(&key, 0, sizeof(key));
702                         memset(&data, 0, sizeof(data));
703                         key.data = curword->object;
704                         key.size = strlen(key.data);
705                         data.data = worddb_data;
706                         data.size = sizeof(worddb_data);
707
708                         /*
709                          * Our data is the key creation time followed by the
710                          * key id.
711                          */
712                         worddb_data[ 0] = publickey->publickey->data[1];
713                         worddb_data[ 1] = publickey->publickey->data[2];
714                         worddb_data[ 2] = publickey->publickey->data[3];
715                         worddb_data[ 3] = publickey->publickey->data[4];
716                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
717                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
718                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
719                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
720                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
721                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
722                         worddb_data[10] = (keyid >>  8) & 0xFF;
723                         worddb_data[11] = keyid & 0xFF; 
724
725                         ret = cursor->c_get(cursor,
726                                 &key,
727                                 &data,
728                                 DB_GET_BOTH);
729
730                         if (ret == 0) {
731                                 ret = cursor->c_del(cursor, 0);
732                                 if (ret != 0) {
733                                         logthing(LOGTHING_ERROR,
734                                                 "Problem deleting word: %s",
735                                                 db_strerror(ret));
736                                 }
737                         }
738
739                         if (ret != 0) {
740                                 logthing(LOGTHING_ERROR,
741                                         "Problem deleting word: %s",
742                                         db_strerror(ret));
743                                 if (ret == DB_LOCK_DEADLOCK) {
744                                         deadlock = true;
745                                 }
746                         }
747                 }
748                 ret = cursor->c_close(cursor);
749                 cursor = NULL;
750
751                 /*
752                  * Free our UID and word lists.
753                  */
754                 llfree(wordlist, NULL);
755                 for (i = 0; uids[i] != NULL; i++) {
756                         free(uids[i]);
757                         uids[i] = NULL;
758                 }
759                 free(uids);
760                 uids = NULL;
761                 free_publickey(publickey);
762                 publickey = NULL;
763         }
764
765         if (!deadlock) {
766                 key.data = &keyid;
767                 key.size = sizeof(keyid);
768
769                 keydb(keyid)->del(keydb(keyid),
770                                 txn,
771                                 &key,
772                                 0); /* flags */
773         }
774
775         if (!intrans) {
776                 endtrans();
777         }
778
779         return deadlock ? (-1) : (ret == DB_NOTFOUND);
780 }
781
782 /**
783  *      dumpdb - dump the key database
784  *      @filenamebase: The base filename to use for the dump.
785  *
786  *      Dumps the database into one or more files, which contain pure OpenPGP
787  *      that can be reimported into onak or gpg. filenamebase provides a base
788  *      file name for the dump; several files may be created, all of which will
789  *      begin with this string and then have a unique number and a .pgp
790  *      extension.
791  */
792 int dumpdb(char *filenamebase)
793 {
794         DBT   key, data;
795         DBC  *cursor = NULL;
796         int   ret = 0;
797         int   fd = -1;
798         int   i = 0;
799         char  filename[1024];
800
801         filename[1023] = 0;
802         for (i = 0; i < numdbs; i++) {
803                 ret = dbconns[i]->cursor(dbconns[i],
804                         NULL,
805                         &cursor,
806                         0);   /* flags */
807
808                 snprintf(filename, 1023, "%s.%d.pgp", filenamebase, i);
809                 fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0640);
810                 if (fd == -1) {
811                         logthing(LOGTHING_ERROR,
812                                 "Error opening keydump file (%s): %s",
813                                 filename,
814                                 strerror(errno));
815                 } else {
816                         memset(&key, 0, sizeof(key));
817                         memset(&data, 0, sizeof(data));
818                         ret = cursor->c_get(cursor, &key, &data, DB_NEXT);
819                         while (ret == 0) {
820                                 write(fd, data.data, data.size);
821                                 memset(&key, 0, sizeof(key));
822                                 memset(&data, 0, sizeof(data));
823                                 ret = cursor->c_get(cursor, &key, &data,
824                                                 DB_NEXT);
825                         }
826                         if (ret != DB_NOTFOUND) {
827                                 logthing(LOGTHING_ERROR,
828                                         "Problem reading key: %s",
829                                         db_strerror(ret));
830                         }
831                         close(fd);
832                 }
833
834                 ret = cursor->c_close(cursor);
835                 cursor = NULL;
836         }
837         
838         return 0;
839 }
840
841 /*
842  * Include the basic keydb routines.
843  */
844 #define NEED_GETFULLKEYID 1
845 #define NEED_GETKEYSIGS 1
846 #define NEED_KEYID2UID 1
847 #include "keydb.c"