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