Andreas Krennmair:
[apps/madmutt.git] / pgppubring.c
1 /*
2  * Copyright (C) 1997-2001 Thomas Roessler <roessler@does-not-exist.org>
3  * 
4  *     This program is free software; you can redistribute it
5  *     and/or modify it under the terms of the GNU General Public
6  *     License as published by the Free Software Foundation; either
7  *     version 2 of the License, or (at your option) any later
8  *     version.
9  * 
10  *     This program is distributed in the hope that it will be
11  *     useful, but WITHOUT ANY WARRANTY; without even the implied
12  *     warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13  *     PURPOSE.  See the GNU General Public License for more
14  *     details.
15  * 
16  *     You should have received a copy of the GNU General Public
17  *     License along with this program; if not, write to the Free
18  *     Software Foundation, Inc., 59 Temple Place - Suite 330,
19  *     Boston, MA  02111, USA.
20  */
21
22 /*
23  * This is a "simple" PGP key ring dumper.
24  * 
25  * The output format is supposed to be compatible to the one GnuPG
26  * emits and Mutt expects.
27  * 
28  * Note that the code of this program could be considerably less
29  * complex, but most of it was taken from mutt's second generation
30  * key ring parser.
31  * 
32  * You can actually use this to put together some fairly general
33  * PGP key management applications.
34  *
35  */
36
37 #if HAVE_CONFIG_H
38 # include "config.h"
39 #endif
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <time.h>
46 #ifdef HAVE_GETOPT_H
47 # include <getopt.h>
48 #endif
49 #include <errno.h>
50
51 extern char *optarg;
52 extern int optind;
53
54 short Umask;                    /* dirty hack because we need Umask in lib.c but don't want globals.h there */
55
56 #include "sha1.h"
57 #include "md5.h"
58 #include "lib.h"
59 #include "pgplib.h"
60 #include "pgppacket.h"
61
62 #define MD5_DIGEST_LENGTH  16
63
64 #ifdef HAVE_FGETPOS
65 #define FGETPOS(fp,pos) fgetpos((fp),&(pos))
66 #define FSETPOS(fp,pos) fsetpos((fp),&(pos))
67 #else
68 #define FGETPOS(fp,pos) pos=ftell((fp));
69 #define FSETPOS(fp,pos) fseek((fp),(pos),SEEK_SET)
70 #endif
71
72
73 static short dump_signatures = 0;
74 static short dump_fingerprints = 0;
75
76
77 static void pgpring_find_candidates (char *ringfile, const char *hints[],
78                                      int nhints);
79 static void pgpring_dump_keyblock (pgp_key_t p);
80
81 int main (int argc, char *const argv[])
82 {
83   int c;
84
85   short version = 2;
86   short secring = 0;
87
88   const char *_kring = NULL;
89   char *env_pgppath, *env_home;
90
91   char pgppath[_POSIX_PATH_MAX];
92   char kring[_POSIX_PATH_MAX];
93
94   while ((c = getopt (argc, argv, "f25sk:S")) != EOF) {
95     switch (c) {
96     case 'S':
97       {
98         dump_signatures = 1;
99         break;
100       }
101
102     case 'f':
103       {
104         dump_fingerprints = 1;
105         break;
106       }
107
108     case 'k':
109       {
110         _kring = optarg;
111         break;
112       }
113
114     case '2':
115     case '5':
116       {
117         version = c - '0';
118         break;
119       }
120
121     case 's':
122       {
123         secring = 1;
124         break;
125       }
126
127     default:
128       {
129         fprintf (stderr,
130                  "usage: %s [-k <key ring> | [-2 | -5] [ -s] [-S] [-f]] [hints]\n",
131                  argv[0]);
132         exit (1);
133       }
134     }
135   }
136
137   if (_kring)
138     strfcpy (kring, _kring, sizeof (kring));
139   else {
140     if ((env_pgppath = getenv ("PGPPATH")))
141       strfcpy (pgppath, env_pgppath, sizeof (pgppath));
142     else if ((env_home = getenv ("HOME")))
143       snprintf (pgppath, sizeof (pgppath), "%s/.pgp", env_home);
144     else {
145       fprintf (stderr, "%s: Can't determine your PGPPATH.\n", argv[0]);
146       exit (1);
147     }
148
149     if (secring)
150       snprintf (kring, sizeof (kring), "%s/secring.%s", pgppath,
151                 version == 2 ? "pgp" : "skr");
152     else
153       snprintf (kring, sizeof (kring), "%s/pubring.%s", pgppath,
154                 version == 2 ? "pgp" : "pkr");
155   }
156
157   pgpring_find_candidates (kring, (const char **) argv + optind,
158                            argc - optind);
159
160   return 0;
161 }
162
163
164 /* The actual key ring parser */
165
166 static void pgp_make_pgp2_fingerprint (unsigned char *buff,
167                                        unsigned char *digest)
168 {
169
170   MD5_CTX context;
171   unsigned int size = 0;
172
173
174   MD5Init (&context);
175
176   size = (buff[0] << 8) + buff[1];
177   size = ((size + 7) / 8);
178   buff = &buff[2];
179
180   MD5Update (&context, buff, size);
181   buff = &buff[size];
182
183   size = (buff[0] << 8) + buff[1];
184   size = ((size + 7) / 8);
185   buff = &buff[2];
186
187   MD5Update (&context, buff, size);
188
189   MD5Final (digest, &context);
190
191 }                               /* pgp_make_pgp2_fingerprint() */
192
193 static pgp_key_t pgp_parse_pgp2_key (unsigned char *buff, size_t l)
194 {
195   pgp_key_t p;
196   unsigned char alg;
197   unsigned char digest[MD5_DIGEST_LENGTH];
198   size_t expl;
199   unsigned long id;
200   time_t gen_time = 0;
201   unsigned short exp_days = 0;
202   size_t j;
203   int i, k;
204   unsigned char scratch[LONG_STRING];
205
206   if (l < 12)
207     return NULL;
208
209   p = pgp_new_keyinfo ();
210
211   for (i = 0, j = 2; i < 4; i++)
212     gen_time = (gen_time << 8) + buff[j++];
213
214   p->gen_time = gen_time;
215
216   for (i = 0; i < 2; i++)
217     exp_days = (exp_days << 8) + buff[j++];
218
219   if (exp_days && time (NULL) > gen_time + exp_days * 24 * 3600)
220     p->flags |= KEYFLAG_EXPIRED;
221
222   alg = buff[j++];
223
224   p->numalg = alg;
225   p->algorithm = pgp_pkalgbytype (alg);
226   p->flags |= pgp_get_abilities (alg);
227
228   if (dump_fingerprints) {
229     /* j now points to the key material, which we need for the fingerprint */
230     p->fp_len = MD5_DIGEST_LENGTH;
231     pgp_make_pgp2_fingerprint (&buff[j], digest);
232     memcpy (p->fingerprint, digest, MD5_DIGEST_LENGTH);
233   }
234   else                          /* just to be usre */
235     memset (p->fingerprint, 0, MD5_DIGEST_LENGTH);
236
237   expl = 0;
238   for (i = 0; i < 2; i++)
239     expl = (expl << 8) + buff[j++];
240
241   p->keylen = expl;
242
243   expl = (expl + 7) / 8;
244   if (expl < 4)
245     goto bailout;
246
247
248   j += expl - 8;
249
250   for (k = 0; k < 2; k++) {
251     for (id = 0, i = 0; i < 4; i++)
252       id = (id << 8) + buff[j++];
253
254     snprintf ((char *) scratch + k * 8, sizeof (scratch) - k * 8,
255               "%08lX", id);
256   }
257
258   p->keyid = safe_strdup ((char *) scratch);
259
260   return p;
261
262 bailout:
263
264   FREE (&p);
265   return NULL;
266 }
267
268 static void pgp_make_pgp3_fingerprint (unsigned char *buff, size_t l,
269                                        unsigned char *digest)
270 {
271   unsigned char dummy;
272   SHA1_CTX context;
273
274   SHA1_Init (&context);
275
276   dummy = buff[0] & 0x3f;
277
278   if (dummy == PT_SUBSECKEY || dummy == PT_SUBKEY || dummy == PT_SECKEY)
279     dummy = PT_PUBKEY;
280
281   dummy = (dummy << 2) | 0x81;
282   SHA1_Update (&context, &dummy, 1);
283   dummy = ((l - 1) >> 8) & 0xff;
284   SHA1_Update (&context, &dummy, 1);
285   dummy = (l - 1) & 0xff;
286   SHA1_Update (&context, &dummy, 1);
287   SHA1_Update (&context, buff + 1, l - 1);
288   SHA1_Final (digest, &context);
289
290 }
291
292 static void skip_bignum (unsigned char *buff, size_t l, size_t j,
293                          size_t * toff, size_t n)
294 {
295   size_t len;
296
297   do {
298     len = (buff[j] << 8) + buff[j + 1];
299     j += (len + 7) / 8 + 2;
300   }
301   while (j <= l && --n > 0);
302
303   if (toff)
304     *toff = j;
305 }
306
307
308 static pgp_key_t pgp_parse_pgp3_key (unsigned char *buff, size_t l)
309 {
310   pgp_key_t p;
311   unsigned char alg;
312   unsigned char digest[SHA_DIGEST_LENGTH];
313   unsigned char scratch[LONG_STRING];
314   time_t gen_time = 0;
315   unsigned long id;
316   int i, k;
317   short len;
318   size_t j;
319
320   p = pgp_new_keyinfo ();
321   j = 2;
322
323   for (i = 0; i < 4; i++)
324     gen_time = (gen_time << 8) + buff[j++];
325
326   p->gen_time = gen_time;
327
328   alg = buff[j++];
329
330   p->numalg = alg;
331   p->algorithm = pgp_pkalgbytype (alg);
332   p->flags |= pgp_get_abilities (alg);
333
334   if (alg == 17)
335     skip_bignum (buff, l, j, &j, 3);
336   else if (alg == 16 || alg == 20)
337     skip_bignum (buff, l, j, &j, 2);
338
339   len = (buff[j] << 8) + buff[j + 1];
340   p->keylen = len;
341
342
343   if (alg >= 1 && alg <= 3)
344     skip_bignum (buff, l, j, &j, 2);
345   else if (alg == 17 || alg == 16 || alg == 20)
346     skip_bignum (buff, l, j, &j, 1);
347
348   pgp_make_pgp3_fingerprint (buff, j, digest);
349   p->fp_len = SHA_DIGEST_LENGTH;
350
351   for (k = 0; k < 2; k++) {
352     for (id = 0, i = SHA_DIGEST_LENGTH - 8 + k * 4;
353          i < SHA_DIGEST_LENGTH + (k - 1) * 4; i++)
354       id = (id << 8) + digest[i];
355
356     snprintf ((char *) scratch + k * 8, sizeof (scratch) - k * 8, "%08lX",
357               id);
358   }
359
360   p->keyid = safe_strdup ((char *) scratch);
361
362   return p;
363 }
364
365 static pgp_key_t pgp_parse_keyinfo (unsigned char *buff, size_t l)
366 {
367   if (!buff || l < 2)
368     return NULL;
369
370   switch (buff[1]) {
371   case 2:
372   case 3:
373     return pgp_parse_pgp2_key (buff, l);
374   case 4:
375     return pgp_parse_pgp3_key (buff, l);
376   default:
377     return NULL;
378   }
379 }
380
381 static int pgp_parse_pgp2_sig (unsigned char *buff, size_t l,
382                                pgp_key_t p, pgp_sig_t * s)
383 {
384   unsigned char sigtype;
385   time_t sig_gen_time;
386   unsigned long signerid1;
387   unsigned long signerid2;
388   size_t j;
389   int i;
390
391   if (l < 22)
392     return -1;
393
394   j = 3;
395   sigtype = buff[j++];
396
397   sig_gen_time = 0;
398   for (i = 0; i < 4; i++)
399     sig_gen_time = (sig_gen_time << 8) + buff[j++];
400
401   signerid1 = signerid2 = 0;
402   for (i = 0; i < 4; i++)
403     signerid1 = (signerid1 << 8) + buff[j++];
404
405   for (i = 0; i < 4; i++)
406     signerid2 = (signerid2 << 8) + buff[j++];
407
408
409   if (sigtype == 0x20 || sigtype == 0x28)
410     p->flags |= KEYFLAG_REVOKED;
411
412   if (s) {
413     s->sigtype = sigtype;
414     s->sid1 = signerid1;
415     s->sid2 = signerid2;
416   }
417
418   return 0;
419 }
420
421 static int pgp_parse_pgp3_sig (unsigned char *buff, size_t l,
422                                pgp_key_t p, pgp_sig_t * s)
423 {
424   unsigned char sigtype;
425   unsigned char pkalg;
426   unsigned char hashalg;
427   unsigned char skt;
428   time_t sig_gen_time = -1;
429   long validity = -1;
430   long key_validity = -1;
431   unsigned long signerid1 = 0;
432   unsigned long signerid2 = 0;
433   size_t ml;
434   size_t j;
435   int i;
436   short ii;
437   short have_critical_spks = 0;
438
439   if (l < 7)
440     return -1;
441
442   j = 2;
443
444   sigtype = buff[j++];
445   pkalg = buff[j++];
446   hashalg = buff[j++];
447
448   for (ii = 0; ii < 2; ii++) {
449     size_t skl;
450     size_t nextone;
451
452     ml = (buff[j] << 8) + buff[j + 1];
453     j += 2;
454
455     if (j + ml > l)
456       break;
457
458     nextone = j;
459     while (ml) {
460       j = nextone;
461       skl = buff[j++];
462       if (!--ml)
463         break;
464
465       if (skl >= 192) {
466         skl = (skl - 192) * 256 + buff[j++] + 192;
467         if (!--ml)
468           break;
469       }
470
471       if ((int) ml - (int) skl < 0)
472         break;
473       ml -= skl;
474
475       nextone = j + skl;
476       skt = buff[j++];
477
478       switch (skt & 0x7f) {
479       case 2:                  /* creation time */
480         {
481           if (skl < 4)
482             break;
483           sig_gen_time = 0;
484           for (i = 0; i < 4; i++)
485             sig_gen_time = (sig_gen_time << 8) + buff[j++];
486
487           break;
488         }
489       case 3:                  /* expiration time */
490         {
491           if (skl < 4)
492             break;
493           validity = 0;
494           for (i = 0; i < 4; i++)
495             validity = (validity << 8) + buff[j++];
496           break;
497         }
498       case 9:                  /* key expiration time */
499         {
500           if (skl < 4)
501             break;
502           key_validity = 0;
503           for (i = 0; i < 4; i++)
504             key_validity = (key_validity << 8) + buff[j++];
505           break;
506         }
507       case 16:                 /* issuer key ID */
508         {
509           if (skl < 8)
510             break;
511           signerid2 = signerid1 = 0;
512           for (i = 0; i < 4; i++)
513             signerid1 = (signerid1 << 8) + buff[j++];
514           for (i = 0; i < 4; i++)
515             signerid2 = (signerid2 << 8) + buff[j++];
516
517           break;
518         }
519       case 10:                 /* CMR key */
520         break;
521       case 4:                  /* exportable */
522       case 5:                  /* trust */
523       case 6:                  /* regexp */
524       case 7:                  /* revocable */
525       case 11:                 /* Pref. symm. alg. */
526       case 12:                 /* revocation key */
527       case 20:                 /* notation data */
528       case 21:                 /* pref. hash */
529       case 22:                 /* pref. comp.alg. */
530       case 23:                 /* key server prefs. */
531       case 24:                 /* pref. key server */
532       default:
533         {
534           if (skt & 0x80)
535             have_critical_spks = 1;
536         }
537       }
538     }
539     j = nextone;
540   }
541
542   if (sigtype == 0x20 || sigtype == 0x28)
543     p->flags |= KEYFLAG_REVOKED;
544   if (key_validity != -1 && time (NULL) > p->gen_time + key_validity)
545     p->flags |= KEYFLAG_EXPIRED;
546   if (have_critical_spks)
547     p->flags |= KEYFLAG_CRITICAL;
548
549   if (s) {
550     s->sigtype = sigtype;
551     s->sid1 = signerid1;
552     s->sid2 = signerid2;
553   }
554
555
556   return 0;
557
558 }
559
560
561 static int pgp_parse_sig (unsigned char *buff, size_t l,
562                           pgp_key_t p, pgp_sig_t * sig)
563 {
564   if (!buff || l < 2 || !p)
565     return -1;
566
567   switch (buff[1]) {
568   case 2:
569   case 3:
570     return pgp_parse_pgp2_sig (buff, l, p, sig);
571   case 4:
572     return pgp_parse_pgp3_sig (buff, l, p, sig);
573   default:
574     return -1;
575   }
576 }
577
578 /* parse one key block, including all subkeys. */
579
580 static pgp_key_t pgp_parse_keyblock (FILE * fp)
581 {
582   unsigned char *buff;
583   unsigned char pt = 0;
584   unsigned char last_pt;
585   size_t l;
586   short err = 0;
587
588 #ifdef HAVE_FGETPOS
589   fpos_t pos;
590 #else
591   long pos;
592 #endif
593
594   pgp_key_t root = NULL;
595   pgp_key_t *last = &root;
596   pgp_key_t p = NULL;
597   pgp_uid_t *uid = NULL;
598   pgp_uid_t **addr = NULL;
599   pgp_sig_t **lsig = NULL;
600
601   FGETPOS (fp, pos);
602
603   while (!err && (buff = pgp_read_packet (fp, &l)) != NULL) {
604     last_pt = pt;
605     pt = buff[0] & 0x3f;
606
607     /* check if we have read the complete key block. */
608
609     if ((pt == PT_SECKEY || pt == PT_PUBKEY) && root) {
610       FSETPOS (fp, pos);
611       return root;
612     }
613
614     switch (pt) {
615     case PT_SECKEY:
616     case PT_PUBKEY:
617     case PT_SUBKEY:
618     case PT_SUBSECKEY:
619       {
620         if (!(*last = p = pgp_parse_keyinfo (buff, l))) {
621           err = 1;
622           break;
623         }
624
625         last = &p->next;
626         addr = &p->address;
627         lsig = &p->sigs;
628
629         if (pt == PT_SUBKEY || pt == PT_SUBSECKEY) {
630           p->flags |= KEYFLAG_SUBKEY;
631           if (p != root) {
632             p->parent = root;
633             p->address = pgp_copy_uids (root->address, p);
634             while (*addr)
635               addr = &(*addr)->next;
636           }
637         }
638
639         if (pt == PT_SECKEY || pt == PT_SUBSECKEY)
640           p->flags |= KEYFLAG_SECRET;
641
642         break;
643       }
644
645     case PT_SIG:
646       {
647         if (lsig) {
648           pgp_sig_t *signature = safe_calloc (sizeof (pgp_sig_t), 1);
649
650           *lsig = signature;
651           lsig = &signature->next;
652
653           pgp_parse_sig (buff, l, p, signature);
654         }
655         break;
656       }
657
658     case PT_TRUST:
659       {
660         if (p && (last_pt == PT_SECKEY || last_pt == PT_PUBKEY ||
661                   last_pt == PT_SUBKEY || last_pt == PT_SUBSECKEY)) {
662           if (buff[1] & 0x20) {
663             p->flags |= KEYFLAG_DISABLED;
664           }
665         }
666         else if (last_pt == PT_NAME && uid) {
667           uid->trust = buff[1];
668         }
669         break;
670       }
671     case PT_NAME:
672       {
673         char *chr;
674
675
676         if (!addr)
677           break;
678
679         chr = safe_malloc (l);
680         memcpy (chr, buff + 1, l - 1);
681         chr[l - 1] = '\0';
682
683
684         *addr = uid = safe_calloc (1, sizeof (pgp_uid_t));      /* XXX */
685         uid->addr = chr;
686         uid->parent = p;
687         uid->trust = 0;
688         addr = &uid->next;
689         lsig = &uid->sigs;
690
691         /* the following tags are generated by
692          * pgp 2.6.3in.
693          */
694
695         if (strstr (chr, "ENCR"))
696           p->flags |= KEYFLAG_PREFER_ENCRYPTION;
697         if (strstr (chr, "SIGN"))
698           p->flags |= KEYFLAG_PREFER_SIGNING;
699
700         break;
701       }
702     }
703
704     FGETPOS (fp, pos);
705   }
706
707   if (err)
708     pgp_free_key (&root);
709
710   return root;
711 }
712
713 static int pgpring_string_matches_hint (const char *s, const char *hints[],
714                                         int nhints)
715 {
716   int i;
717
718   if (!hints || !nhints)
719     return 1;
720
721   for (i = 0; i < nhints; i++) {
722     if (mutt_stristr (s, hints[i]) != NULL)
723       return 1;
724   }
725
726   return 0;
727 }
728
729 /* 
730  * Go through the key ring file and look for keys with
731  * matching IDs.
732  */
733
734 static void pgpring_find_candidates (char *ringfile, const char *hints[],
735                                      int nhints)
736 {
737   FILE *rfp;
738
739 #ifdef HAVE_FGETPOS
740   fpos_t pos, keypos;
741 #else
742   long pos, keypos;
743 #endif
744
745   unsigned char *buff = NULL;
746   unsigned char pt = 0;
747   size_t l = 0;
748
749   short err = 0;
750
751   if ((rfp = fopen (ringfile, "r")) == NULL) {
752     char *error_buf;
753     size_t error_buf_len;
754
755     error_buf_len = sizeof ("fopen: ") - 1 + strlen (ringfile) + 1;
756     error_buf = safe_malloc (error_buf_len);
757     snprintf (error_buf, error_buf_len, "fopen: %s", ringfile);
758     perror (error_buf);
759     FREE (&error_buf);
760     return;
761   }
762
763   FGETPOS (rfp, pos);
764   FGETPOS (rfp, keypos);
765
766   while (!err && (buff = pgp_read_packet (rfp, &l)) != NULL) {
767     pt = buff[0] & 0x3f;
768
769     if (l < 1)
770       continue;
771
772     if ((pt == PT_SECKEY) || (pt == PT_PUBKEY)) {
773       keypos = pos;
774     }
775     else if (pt == PT_NAME) {
776       char *tmp = safe_malloc (l);
777
778       memcpy (tmp, buff + 1, l - 1);
779       tmp[l - 1] = '\0';
780
781       /* mutt_decode_utf8_string (tmp, chs); */
782
783       if (pgpring_string_matches_hint (tmp, hints, nhints)) {
784         pgp_key_t p;
785
786         FSETPOS (rfp, keypos);
787
788         /* Not bailing out here would lead us into an endless loop. */
789
790         if ((p = pgp_parse_keyblock (rfp)) == NULL)
791           err = 1;
792
793         pgpring_dump_keyblock (p);
794         pgp_free_key (&p);
795       }
796
797       FREE (&tmp);
798     }
799
800     FGETPOS (rfp, pos);
801   }
802
803   fclose (rfp);
804
805 }
806
807 static void print_userid (const char *id)
808 {
809   for (; id && *id; id++) {
810     if (*id >= ' ' && *id <= 'z' && *id != ':')
811       putchar (*id);
812     else
813       printf ("\\x%02x", (*id) & 0xff);
814   }
815 }
816
817 static void print_fingerprint (pgp_key_t p)
818 {
819   int i = 0;
820
821   printf ("fpr:::::::::");
822   for (i = 0; i < p->fp_len; i++)
823     printf ("%02X", p->fingerprint[i]);
824   printf (":\n");
825
826 }                               /* print_fingerprint() */
827
828
829 static void pgpring_dump_signatures (pgp_sig_t * sig)
830 {
831   for (; sig; sig = sig->next) {
832     if (sig->sigtype == 0x10 || sig->sigtype == 0x11 ||
833         sig->sigtype == 0x12 || sig->sigtype == 0x13)
834       printf ("sig::::%08lX%08lX::::::%X:\n",
835               sig->sid1, sig->sid2, sig->sigtype);
836     else if (sig->sigtype == 0x20)
837       printf ("rev::::%08lX%08lX::::::%X:\n",
838               sig->sid1, sig->sid2, sig->sigtype);
839   }
840 }
841
842
843 static char gnupg_trustletter (int t)
844 {
845   switch (t) {
846   case 1:
847     return 'n';
848   case 2:
849     return 'm';
850   case 3:
851     return 'f';
852   }
853   return 'q';
854 }
855
856 static void pgpring_dump_keyblock (pgp_key_t p)
857 {
858   pgp_uid_t *uid;
859   short first;
860   struct tm *tp;
861   time_t t;
862
863   for (; p; p = p->next) {
864     first = 1;
865
866     if (p->flags & KEYFLAG_SECRET) {
867       if (p->flags & KEYFLAG_SUBKEY)
868         printf ("ssb:");
869       else
870         printf ("sec:");
871     }
872     else {
873       if (p->flags & KEYFLAG_SUBKEY)
874         printf ("sub:");
875       else
876         printf ("pub:");
877     }
878
879     if (p->flags & KEYFLAG_REVOKED)
880       putchar ('r');
881     if (p->flags & KEYFLAG_EXPIRED)
882       putchar ('e');
883     if (p->flags & KEYFLAG_DISABLED)
884       putchar ('d');
885
886     for (uid = p->address; uid; uid = uid->next, first = 0) {
887       if (!first) {
888         printf ("uid:%c::::::::", gnupg_trustletter (uid->trust));
889         print_userid (uid->addr);
890         printf (":\n");
891       }
892       else {
893         if (p->flags & KEYFLAG_SECRET)
894           putchar ('u');
895         else
896           putchar (gnupg_trustletter (uid->trust));
897
898         t = p->gen_time;
899         tp = gmtime (&t);
900
901         printf (":%d:%d:%s:%04d-%02d-%02d::::", p->keylen, p->numalg,
902                 p->keyid, 1900 + tp->tm_year, tp->tm_mon + 1, tp->tm_mday);
903
904         print_userid (uid->addr);
905         printf ("::");
906
907         if (pgp_canencrypt (p->numalg))
908           putchar ('e');
909         if (pgp_cansign (p->numalg))
910           putchar ('s');
911         if (p->flags & KEYFLAG_DISABLED)
912           putchar ('D');
913         printf (":\n");
914
915         if (dump_fingerprints)
916           print_fingerprint (p);
917       }
918
919       if (dump_signatures) {
920         if (first)
921           pgpring_dump_signatures (p->sigs);
922         pgpring_dump_signatures (uid->sigs);
923       }
924     }
925   }
926 }
927
928 /*
929  * The mutt_gettext () defined in gettext.c requires iconv,
930  * so we do without charset conversion here.
931  */
932
933 char *mutt_gettext (const char *message)
934 {
935   return (char *) message;
936 }