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