Nico Golde:
[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[], int nhints);
78 static void pgpring_dump_keyblock (pgp_key_t p);
79
80 int main (int argc, char * const argv[])
81 {
82   int c;
83   
84   short version = 2;
85   short secring = 0;
86   
87   const char *_kring = NULL;
88   char *env_pgppath, *env_home;
89
90   char pgppath[_POSIX_PATH_MAX];
91   char kring[_POSIX_PATH_MAX];
92
93   while ((c = getopt (argc, argv, "f25sk:S")) != EOF)
94   {
95     switch (c)
96     {
97       case 'S':
98       {
99         dump_signatures = 1;
100         break;
101       }
102
103       case 'f':
104       {
105         dump_fingerprints = 1;
106         break;
107       }
108
109       case 'k':
110       {
111         _kring = optarg;
112         break;
113       }
114       
115       case '2': 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, "usage: %s [-k <key ring> | [-2 | -5] [ -s] [-S] [-f]] [hints]\n",
130                  argv[0]);
131         exit (1);
132       }
133     }
134   }
135
136   if (_kring)
137     strfcpy (kring, _kring, sizeof (kring));
138   else
139   {
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     {
146       fprintf (stderr, "%s: Can't determine your PGPPATH.\n", argv[0]);
147       exit (1);
148     }
149     
150     if (secring)
151       snprintf (kring, sizeof (kring), "%s/secring.%s", pgppath, version == 2 ? "pgp" : "skr");
152     else
153       snprintf (kring, sizeof (kring), "%s/pubring.%s", pgppath, version == 2 ? "pgp" : "pkr");
154   }
155   
156   pgpring_find_candidates (kring, (const char**) argv + optind, argc - optind);
157     
158   return 0;
159 }
160
161
162 /* The actual key ring parser */
163
164 static void pgp_make_pgp2_fingerprint (unsigned char *buff,
165                                        unsigned char *digest)
166 {
167
168   MD5_CTX context;
169   unsigned int size = 0;
170
171
172   MD5Init (&context);
173
174   size = (buff[0] << 8) + buff[1];
175   size = ((size + 7) / 8);
176   buff = &buff[2];
177
178   MD5Update (&context, buff, size);
179   buff = &buff[size];
180
181   size = (buff[0] << 8) + buff[1];
182   size = ((size + 7) / 8);
183   buff = &buff[2];
184
185   MD5Update (&context, buff, size);
186
187   MD5Final (digest, &context);
188
189 } /* pgp_make_pgp2_fingerprint() */
190
191 static pgp_key_t pgp_parse_pgp2_key (unsigned char *buff, size_t l)
192 {
193   pgp_key_t p;
194   unsigned char alg;
195   unsigned char digest[MD5_DIGEST_LENGTH];
196   size_t expl;
197   unsigned long id;
198   time_t gen_time = 0;
199   unsigned short exp_days = 0;
200   size_t j;
201   int i, k;
202   unsigned char scratch[LONG_STRING];
203
204   if (l < 12)
205     return NULL;
206
207   p = pgp_new_keyinfo();
208
209   for (i = 0, j = 2; i < 4; i++)
210     gen_time = (gen_time << 8) + buff[j++];
211
212   p->gen_time = gen_time;
213
214   for (i = 0; i < 2; i++)
215     exp_days = (exp_days << 8) + buff[j++];
216
217   if (exp_days && time (NULL) > gen_time + exp_days * 24 * 3600)
218     p->flags |= KEYFLAG_EXPIRED;
219
220   alg = buff[j++];
221
222   p->numalg = alg;
223   p->algorithm = pgp_pkalgbytype (alg);
224   p->flags |= pgp_get_abilities (alg);
225
226   if (dump_fingerprints)
227   {
228     /* j now points to the key material, which we need for the fingerprint */
229     p->fp_len = MD5_DIGEST_LENGTH;
230     pgp_make_pgp2_fingerprint (&buff[j], digest);
231     memcpy (p->fingerprint, digest, MD5_DIGEST_LENGTH);
232   }
233   else  /* just to be usre */
234     memset (p->fingerprint, 0, MD5_DIGEST_LENGTH);
235     
236   expl = 0;
237   for (i = 0; i < 2; i++)
238     expl = (expl << 8) + buff[j++];
239
240   p->keylen = expl;
241
242   expl = (expl + 7) / 8;
243   if (expl < 4)
244     goto bailout;
245
246
247   j += expl - 8;
248
249   for (k = 0; k < 2; k++)
250   {
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   {
299     len = (buff[j] << 8) + buff[j + 1];
300     j += (len + 7) / 8 + 2;
301   }
302   while (j <= l && --n > 0);
303
304   if (toff)
305     *toff = j;
306 }
307
308
309 static pgp_key_t pgp_parse_pgp3_key (unsigned char *buff, size_t l)
310 {
311   pgp_key_t p;
312   unsigned char alg;
313   unsigned char digest[SHA_DIGEST_LENGTH];
314   unsigned char scratch[LONG_STRING];
315   time_t gen_time = 0;
316   unsigned long id;
317   int i, k;
318   short len;
319   size_t j;
320
321   p = pgp_new_keyinfo ();
322   j = 2;
323
324   for (i = 0; i < 4; i++)
325     gen_time = (gen_time << 8) + buff[j++];
326
327   p->gen_time = gen_time;
328
329   alg = buff[j++];
330
331   p->numalg = alg;
332   p->algorithm = pgp_pkalgbytype (alg);
333   p->flags |= pgp_get_abilities (alg);
334
335   if (alg == 17)
336     skip_bignum (buff, l, j, &j, 3);
337   else if (alg == 16 || alg == 20)
338     skip_bignum (buff, l, j, &j, 2);
339
340   len = (buff[j] << 8) + buff[j + 1];
341   p->keylen = len;
342
343
344   if (alg >= 1 && alg <= 3)
345     skip_bignum (buff, l, j, &j, 2);
346   else if (alg == 17 || alg == 16 || alg == 20)
347     skip_bignum (buff, l, j, &j, 1);
348
349   pgp_make_pgp3_fingerprint (buff, j, digest);
350   p->fp_len = SHA_DIGEST_LENGTH;
351   
352   for (k = 0; k < 2; k++)
353   {
354     for (id = 0, i = SHA_DIGEST_LENGTH - 8 + k * 4;
355          i < SHA_DIGEST_LENGTH + (k - 1) * 4; i++)
356       id = (id << 8) + digest[i];
357
358     snprintf ((char *) scratch + k * 8, sizeof (scratch) - k * 8, "%08lX", id);
359   }
360
361   p->keyid = safe_strdup ((char *) scratch);
362
363   return p;
364 }
365
366 static pgp_key_t pgp_parse_keyinfo (unsigned char *buff, size_t l)
367 {
368   if (!buff || l < 2)
369     return NULL;
370
371   switch (buff[1])
372   {
373   case 2:
374   case 3:
375     return pgp_parse_pgp2_key (buff, l);
376   case 4:
377     return pgp_parse_pgp3_key (buff, l);
378   default:
379     return NULL;
380   }
381 }
382
383 static int pgp_parse_pgp2_sig (unsigned char *buff, size_t l,
384                                pgp_key_t p, pgp_sig_t *s)
385 {
386   unsigned char sigtype;
387   time_t sig_gen_time;
388   unsigned long signerid1;
389   unsigned long signerid2;
390   size_t j;
391   int i;
392
393   if (l < 22)
394     return -1;
395
396   j = 3;
397   sigtype = buff[j++];
398
399   sig_gen_time = 0;
400   for (i = 0; i < 4; i++)
401     sig_gen_time = (sig_gen_time << 8) + buff[j++];
402
403   signerid1 = signerid2 = 0;
404   for (i = 0; i < 4; i++)
405     signerid1 = (signerid1 << 8) + buff[j++];
406
407   for (i = 0; i < 4; i++)
408     signerid2 = (signerid2 << 8) + buff[j++];
409
410   
411   if (sigtype == 0x20 || sigtype == 0x28)
412     p->flags |= KEYFLAG_REVOKED;
413
414   if (s)
415   {
416     s->sigtype = sigtype;
417     s->sid1    = signerid1;
418     s->sid2    = signerid2;
419   }
420   
421   return 0;
422 }
423
424 static int pgp_parse_pgp3_sig (unsigned char *buff, size_t l,
425                                pgp_key_t p, pgp_sig_t *s)
426 {
427   unsigned char sigtype;
428   unsigned char pkalg;
429   unsigned char hashalg;
430   unsigned char skt;
431   time_t sig_gen_time = -1;
432   long validity = -1;
433   long key_validity = -1;
434   unsigned long signerid1 = 0;
435   unsigned long signerid2 = 0;
436   size_t ml;
437   size_t j;
438   int i;
439   short ii;
440   short have_critical_spks = 0;
441
442   if (l < 7)
443     return -1;
444
445   j = 2;
446
447   sigtype = buff[j++];
448   pkalg = buff[j++];
449   hashalg = buff[j++];
450
451   for (ii = 0; ii < 2; ii++)
452   {
453     size_t skl;
454     size_t nextone;
455
456     ml = (buff[j] << 8) + buff[j + 1];
457     j += 2;
458
459     if (j + ml > l)
460       break;
461
462     nextone = j;
463     while (ml)
464     {
465       j = nextone;
466       skl = buff[j++];
467       if (!--ml)
468         break;
469
470       if (skl >= 192)
471       {
472         skl = (skl - 192) * 256 + buff[j++] + 192;
473         if (!--ml)
474           break;
475       }
476
477       if ((int) ml - (int) skl < 0)
478         break;
479       ml -= skl;
480
481       nextone = j + skl;
482       skt = buff[j++];
483
484       switch (skt & 0x7f)
485       {
486         case 2:                 /* creation time */
487         {
488           if (skl < 4)
489             break;
490           sig_gen_time = 0;
491           for (i = 0; i < 4; i++)
492             sig_gen_time = (sig_gen_time << 8) + buff[j++];
493
494           break;
495         }
496         case 3:                 /* expiration time */
497         {
498           if (skl < 4)
499             break;
500           validity = 0;
501           for (i = 0; i < 4; i++)
502             validity = (validity << 8) + buff[j++];
503           break;
504         }
505         case 9:                 /* key expiration time */
506         {
507           if (skl < 4)
508             break;
509           key_validity = 0;
510           for (i = 0; i < 4; i++)
511             key_validity = (key_validity << 8) + buff[j++];
512           break;
513         }
514         case 16:                        /* issuer key ID */
515         {
516           if (skl < 8)
517             break;
518           signerid2 = signerid1 = 0;
519           for (i = 0; i < 4; i++)
520             signerid1 = (signerid1 << 8) + buff[j++];
521           for (i = 0; i < 4; i++)
522             signerid2 = (signerid2 << 8) + buff[j++];
523           
524           break;
525         }
526         case 10:                        /* CMR key */
527         break;
528         case 4:                         /* exportable */
529         case 5:                         /* trust */
530         case 6:                         /* regexp */
531         case 7:                         /* revocable */
532         case 11:                        /* Pref. symm. alg. */
533         case 12:                        /* revocation key */
534         case 20:                        /* notation data */
535         case 21:                        /* pref. hash */
536         case 22:                        /* pref. comp.alg. */
537         case 23:                        /* key server prefs. */
538         case 24:                        /* pref. key server */
539         default:
540         {
541           if (skt & 0x80)
542             have_critical_spks = 1;
543         }
544       }
545     }
546     j = nextone;
547   }
548
549   if (sigtype == 0x20 || sigtype == 0x28)
550     p->flags |= KEYFLAG_REVOKED;
551   if (key_validity != -1 && time (NULL) > p->gen_time + key_validity)
552     p->flags |= KEYFLAG_EXPIRED;
553   if (have_critical_spks)
554     p->flags |= KEYFLAG_CRITICAL;
555
556   if (s)
557   {
558     s->sigtype = sigtype;
559     s->sid1    = signerid1;
560     s->sid2    = signerid2;
561   }
562
563   
564   return 0;
565
566 }
567
568
569 static int pgp_parse_sig (unsigned char *buff, size_t l,
570                           pgp_key_t p, pgp_sig_t *sig)
571 {
572   if (!buff || l < 2 || !p)
573     return -1;
574
575   switch (buff[1])
576   {
577   case 2:
578   case 3:
579     return pgp_parse_pgp2_sig (buff, l, p, sig);      
580   case 4:
581     return pgp_parse_pgp3_sig (buff, l, p, sig);
582   default:
583     return -1;
584   }
585 }
586
587 /* parse one key block, including all subkeys. */
588
589 static pgp_key_t pgp_parse_keyblock (FILE * fp)
590 {
591   unsigned char *buff;
592   unsigned char pt = 0;
593   unsigned char last_pt;
594   size_t l;
595   short err = 0;
596
597 #ifdef HAVE_FGETPOS
598   fpos_t pos;
599 #else
600   long pos;
601 #endif
602
603   pgp_key_t root = NULL;
604   pgp_key_t *last = &root;
605   pgp_key_t p = NULL;
606   pgp_uid_t *uid = NULL;
607   pgp_uid_t **addr = NULL;
608   pgp_sig_t **lsig = NULL;
609
610   FGETPOS(fp,pos);
611   
612   while (!err && (buff = pgp_read_packet (fp, &l)) != NULL)
613   {
614     last_pt = pt;
615     pt = buff[0] & 0x3f;
616
617     /* check if we have read the complete key block. */
618     
619     if ((pt == PT_SECKEY || pt == PT_PUBKEY) && root)
620     {
621       FSETPOS(fp, pos);
622       return root;
623     }
624     
625     switch (pt)
626     {
627       case PT_SECKEY:
628       case PT_PUBKEY:
629       case PT_SUBKEY:
630       case PT_SUBSECKEY:
631       {
632         if (!(*last = p = pgp_parse_keyinfo (buff, l)))
633         {
634           err = 1;
635           break;
636         }
637
638         last = &p->next;
639         addr = &p->address;
640         lsig = &p->sigs;
641         
642         if (pt == PT_SUBKEY || pt == PT_SUBSECKEY)
643         {
644           p->flags |= KEYFLAG_SUBKEY;
645           if (p != root)
646           {
647             p->parent  = root;
648             p->address = pgp_copy_uids (root->address, p);
649             while (*addr) addr = &(*addr)->next;
650           }
651         }
652         
653         if (pt == PT_SECKEY || pt == PT_SUBSECKEY)
654           p->flags |= KEYFLAG_SECRET;
655
656         break;
657       }
658
659       case PT_SIG:
660       {
661         if (lsig)
662         {
663           pgp_sig_t *signature = safe_calloc (sizeof (pgp_sig_t), 1);
664           *lsig = signature;
665           lsig = &signature->next;
666           
667           pgp_parse_sig (buff, l, p, signature);
668         }
669         break;
670       }
671
672       case PT_TRUST:
673       {
674         if (p && (last_pt == PT_SECKEY || last_pt == PT_PUBKEY ||
675                   last_pt == PT_SUBKEY || last_pt == PT_SUBSECKEY))
676         {
677           if (buff[1] & 0x20)
678           {
679             p->flags |= KEYFLAG_DISABLED;
680           }
681         }
682         else if (last_pt == PT_NAME && uid)
683         {
684           uid->trust = buff[1];
685         }
686         break;
687       }
688       case PT_NAME:
689       {
690         char *chr;
691
692
693         if (!addr)
694           break;
695
696         chr = safe_malloc (l);
697         memcpy (chr, buff + 1, l - 1);
698         chr[l - 1] = '\0';
699
700
701         *addr = uid = safe_calloc (1, sizeof (pgp_uid_t)); /* XXX */
702         uid->addr = chr;
703         uid->parent = p;
704         uid->trust = 0;
705         addr = &uid->next;
706         lsig = &uid->sigs;
707         
708         /* the following tags are generated by
709          * pgp 2.6.3in.
710          */
711
712         if (strstr (chr, "ENCR"))
713           p->flags |= KEYFLAG_PREFER_ENCRYPTION;
714         if (strstr (chr, "SIGN"))
715           p->flags |= KEYFLAG_PREFER_SIGNING;
716
717         break;
718       }
719     }
720
721     FGETPOS(fp,pos);
722   }
723
724   if (err)
725     pgp_free_key (&root);
726   
727   return root;  
728 }
729
730 static int pgpring_string_matches_hint (const char *s, const char *hints[], int nhints)
731 {
732   int i;
733
734   if (!hints || !nhints)
735     return 1;
736
737   for (i = 0; i < nhints; i++)
738   {
739     if (mutt_stristr (s, hints[i]) != NULL)
740       return 1;
741   }
742
743   return 0;
744 }
745
746 /* 
747  * Go through the key ring file and look for keys with
748  * matching IDs.
749  */
750
751 static void pgpring_find_candidates (char *ringfile, const char *hints[], int nhints)
752 {
753   FILE *rfp;
754 #ifdef HAVE_FGETPOS
755   fpos_t pos, keypos;
756 #else
757   long pos, keypos;
758 #endif
759
760   unsigned char *buff = NULL;
761   unsigned char pt = 0;
762   size_t l = 0;
763
764   short err = 0;
765   
766   if ((rfp = fopen (ringfile, "r")) == NULL)
767   {
768     char *error_buf;
769     size_t error_buf_len;
770
771     error_buf_len = sizeof ("fopen: ") - 1 + strlen (ringfile) + 1;
772     error_buf = safe_malloc (error_buf_len);
773     snprintf (error_buf, error_buf_len, "fopen: %s", ringfile);
774     perror (error_buf);
775     FREE (&error_buf);
776     return;
777   }
778
779   FGETPOS(rfp,pos);
780   FGETPOS(rfp,keypos);
781
782   while (!err && (buff = pgp_read_packet (rfp, &l)) != NULL)
783   {
784     pt = buff[0] & 0x3f;
785     
786     if (l < 1)
787       continue;
788     
789     if ((pt == PT_SECKEY) || (pt == PT_PUBKEY))
790     {
791       keypos = pos;
792     }
793     else if (pt == PT_NAME)
794     {
795       char *tmp = safe_malloc (l);
796
797       memcpy (tmp, buff + 1, l - 1);
798       tmp[l - 1] = '\0';
799
800       /* mutt_decode_utf8_string (tmp, chs); */
801
802       if (pgpring_string_matches_hint (tmp, hints, nhints))
803       {
804         pgp_key_t p;
805
806         FSETPOS(rfp, keypos);
807
808         /* Not bailing out here would lead us into an endless loop. */
809
810         if ((p = pgp_parse_keyblock (rfp)) == NULL)
811           err = 1;
812         
813         pgpring_dump_keyblock (p);
814         pgp_free_key (&p);
815       }
816
817       FREE (&tmp);
818     }
819
820     FGETPOS(rfp,pos);
821   }
822
823   fclose (rfp);
824
825 }
826
827 static void print_userid (const char *id)
828 {
829   for (; id && *id; id++)
830   {
831     if (*id >= ' ' && *id <= 'z' && *id != ':')
832       putchar (*id);
833     else
834       printf ("\\x%02x", (*id) & 0xff);
835   }
836 }
837
838 static void print_fingerprint (pgp_key_t p) 
839 {
840   int i = 0;
841
842   printf ("fpr:::::::::");
843   for (i = 0; i < p->fp_len; i++)
844     printf ("%02X", p->fingerprint[i]);
845   printf (":\n");
846
847 } /* print_fingerprint() */
848
849
850 static void pgpring_dump_signatures (pgp_sig_t *sig)
851 {
852   for (; sig; sig = sig->next)
853   {
854     if (sig->sigtype == 0x10 || sig->sigtype == 0x11 ||
855         sig->sigtype == 0x12 || sig->sigtype == 0x13)
856       printf ("sig::::%08lX%08lX::::::%X:\n",
857               sig->sid1, sig->sid2, sig->sigtype);
858     else if (sig->sigtype == 0x20)
859       printf ("rev::::%08lX%08lX::::::%X:\n",
860               sig->sid1, sig->sid2, sig->sigtype);
861   }
862 }
863
864
865 static char gnupg_trustletter (int t)
866 {
867   switch (t)
868   {
869     case 1: return 'n';
870     case 2: return 'm';
871     case 3: return 'f';
872   }
873   return 'q';
874 }
875
876 static void pgpring_dump_keyblock (pgp_key_t p)
877 {
878   pgp_uid_t *uid;
879   short first;
880   struct tm *tp;
881   time_t t;
882   
883   for (; p; p = p->next)
884   {
885     first = 1;
886
887     if (p->flags & KEYFLAG_SECRET)
888     {
889       if (p->flags & KEYFLAG_SUBKEY)
890         printf ("ssb:");
891       else
892         printf ("sec:");
893     }
894     else 
895     {
896       if (p->flags & KEYFLAG_SUBKEY)
897         printf ("sub:");
898       else
899         printf ("pub:");
900     }
901     
902     if (p->flags & KEYFLAG_REVOKED)
903       putchar ('r');
904     if (p->flags & KEYFLAG_EXPIRED)
905       putchar ('e');
906     if (p->flags & KEYFLAG_DISABLED)
907       putchar ('d');
908
909     for (uid = p->address; uid; uid = uid->next, first = 0)
910     {
911       if (!first)
912       {
913         printf ("uid:%c::::::::", gnupg_trustletter (uid->trust));
914         print_userid (uid->addr);
915         printf (":\n");
916       }
917       else
918       {
919         if (p->flags & KEYFLAG_SECRET)
920           putchar ('u');
921         else
922           putchar (gnupg_trustletter (uid->trust));
923
924         t = p->gen_time;
925         tp = gmtime (&t);
926
927         printf (":%d:%d:%s:%04d-%02d-%02d::::", p->keylen, p->numalg, p->keyid,
928                 1900 + tp->tm_year, tp->tm_mon + 1, tp->tm_mday);
929         
930         print_userid (uid->addr);
931         printf ("::");
932
933         if(pgp_canencrypt(p->numalg))
934           putchar ('e');
935         if(pgp_cansign(p->numalg))
936           putchar ('s');
937         if (p->flags & KEYFLAG_DISABLED)
938           putchar ('D');
939         printf (":\n");
940
941         if (dump_fingerprints) 
942           print_fingerprint (p);
943       }
944       
945       if (dump_signatures)
946       {
947         if (first) pgpring_dump_signatures (p->sigs);
948         pgpring_dump_signatures (uid->sigs);
949       }
950     }
951   }
952 }
953
954 /*
955  * The mutt_gettext () defined in gettext.c requires iconv,
956  * so we do without charset conversion here.
957  */
958
959 char *mutt_gettext (const char *message)
960 {
961   return (char *)message;
962 }