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