Fixed an email/DN bug.
[gnupg.git] / sm / certdump.c
1 /* certdump.c - Dump a certificate for debugging
2  * Copyright (C) 2001, 2004, 2007 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <unistd.h> 
26 #include <time.h>
27 #include <assert.h>
28 #ifdef HAVE_LOCALE_H
29 #include <locale.h>
30 #endif
31 #ifdef HAVE_LANGINFO_CODESET
32 #include <langinfo.h>
33 #endif
34
35 #include "gpgsm.h"
36 #include <gcrypt.h>
37 #include <ksba.h>
38
39 #include "keydb.h"
40 #include "i18n.h"
41
42 #ifdef HAVE_FOPENCOOKIE
43 typedef ssize_t my_funopen_hook_ret_t;
44 #else
45 typedef int     my_funopen_hook_ret_t;
46 #endif
47
48
49 struct dn_array_s {
50   char *key;
51   char *value;
52   int   multivalued;
53   int   done;
54 };
55
56
57 /* Print the first element of an S-Expression. */
58 void
59 gpgsm_print_serial (estream_t fp, ksba_const_sexp_t sn)
60 {
61   const char *p = (const char *)sn;
62   unsigned long n;
63   char *endp;
64
65   if (!p)
66     es_fputs (_("none"), fp);
67   else if (*p != '(')
68     es_fputs ("[Internal error - not an S-expression]", fp);
69   else
70     {
71       p++;
72       n = strtoul (p, &endp, 10);
73       p = endp;
74       if (*p++ != ':')
75         es_fputs ("[Internal Error - invalid S-expression]", fp);
76       else
77         es_write_hexstring (fp, p, n, 0, NULL);
78     }
79 }
80
81
82 /* Dump the serial number or any other simple S-expression. */
83 void
84 gpgsm_dump_serial (ksba_const_sexp_t sn)
85 {
86   const char *p = (const char *)sn;
87   unsigned long n;
88   char *endp;
89
90   if (!p)
91     log_printf ("none");
92   else if (*p != '(')
93     log_printf ("ERROR - not an S-expression");
94   else
95     {
96       p++;
97       n = strtoul (p, &endp, 10);
98       p = endp;
99       if (*p!=':')
100         log_printf ("ERROR - invalid S-expression");
101       else
102         {
103           for (p++; n; n--, p++)
104             log_printf ("%02X", *(const unsigned char *)p);
105         }
106     }
107 }
108
109
110 char *
111 gpgsm_format_serial (ksba_const_sexp_t sn)
112 {
113   const char *p = (const char *)sn;
114   unsigned long n;
115   char *endp;
116   char *buffer;
117   int i;
118
119   if (!p)
120     return NULL;
121
122   if (*p != '(')
123     BUG (); /* Not a valid S-expression. */
124
125   p++;
126   n = strtoul (p, &endp, 10);
127   p = endp;
128   if (*p!=':')
129     BUG (); /* Not a valid S-expression. */
130   p++;
131
132   buffer = xtrymalloc (n*2+1);
133   if (buffer)
134     {
135       for (i=0; n; n--, p++, i+=2)
136         sprintf (buffer+i, "%02X", *(unsigned char *)p);
137       buffer[i] = 0;
138     }
139   return buffer;
140 }
141
142
143
144
145 void
146 gpgsm_print_time (estream_t fp, ksba_isotime_t t)
147 {
148   if (!t || !*t)
149     es_fputs (_("none"), fp);
150   else
151     es_fprintf (fp, "%.4s-%.2s-%.2s %.2s:%.2s:%s",
152                 t, t+4, t+6, t+9, t+11, t+13);
153 }
154
155
156 void
157 gpgsm_dump_time (ksba_isotime_t t)
158 {
159   if (!t || !*t)
160     log_printf (_("[none]"));
161   else
162     log_printf ("%.4s-%.2s-%.2s %.2s:%.2s:%s",
163                 t, t+4, t+6, t+9, t+11, t+13);
164 }
165
166
167
168
169 void
170 gpgsm_dump_string (const char *string)
171 {
172
173   if (!string)
174     log_printf ("[error]");
175   else
176     {
177       const unsigned char *s;
178
179       for (s=(const unsigned char*)string; *s; s++)
180         {
181           if (*s < ' ' || (*s >= 0x7f && *s <= 0xa0))
182             break;
183         }
184       if (!*s && *string != '[')
185         log_printf ("%s", string);
186       else
187         {
188           log_printf ( "[ ");
189           log_printhex (NULL, string, strlen (string));
190           log_printf ( " ]");
191         }
192     }
193 }
194
195
196 /* This simple dump function is mainly used for debugging purposes. */
197 void 
198 gpgsm_dump_cert (const char *text, ksba_cert_t cert)
199 {
200   ksba_sexp_t sexp;
201   char *p;
202   char *dn;
203   ksba_isotime_t t;
204
205   log_debug ("BEGIN Certificate `%s':\n", text? text:"");
206   if (cert)
207     {
208       sexp = ksba_cert_get_serial (cert);
209       log_debug ("     serial: ");
210       gpgsm_dump_serial (sexp);
211       ksba_free (sexp);
212       log_printf ("\n");
213
214       ksba_cert_get_validity (cert, 0, t);
215       log_debug ("  notBefore: ");
216       gpgsm_dump_time (t);
217       log_printf ("\n");
218       ksba_cert_get_validity (cert, 1, t);
219       log_debug ("   notAfter: ");
220       gpgsm_dump_time (t);
221       log_printf ("\n");
222
223       dn = ksba_cert_get_issuer (cert, 0);
224       log_debug ("     issuer: ");
225       gpgsm_dump_string (dn);
226       ksba_free (dn);
227       log_printf ("\n");
228     
229       dn = ksba_cert_get_subject (cert, 0);
230       log_debug ("    subject: ");
231       gpgsm_dump_string (dn);
232       ksba_free (dn);
233       log_printf ("\n");
234
235       log_debug ("  hash algo: %s\n", ksba_cert_get_digest_algo (cert));
236
237       p = gpgsm_get_fingerprint_string (cert, 0);
238       log_debug ("  SHA1 Fingerprint: %s\n", p);
239       xfree (p);
240     }
241   log_debug ("END Certificate\n");
242 }
243
244
245 /* Return a new string holding the format serial number and issuer
246    ("#SN/issuer").  No filtering on invalid characters is done.
247    Caller must release the string.  On memory failure NULL is
248    returned.  */
249 char *
250 gpgsm_format_sn_issuer (ksba_sexp_t sn, const char *issuer)
251 {
252   char *p, *p1;
253
254   if (sn && issuer)
255     {
256       p1 = gpgsm_format_serial (sn);
257       if (!p1)
258         p = xtrystrdup ("[invalid SN]");
259       else
260         {
261           p = xtrymalloc (strlen (p1) + strlen (issuer) + 2 + 1);
262           if (p)
263             {
264               *p = '#';
265               strcpy (stpcpy (stpcpy (p+1, p1),"/"), issuer);
266             }
267           xfree (p1);
268         }
269     }
270   else
271     p = xtrystrdup ("[invalid SN/issuer]");
272   return p;
273 }
274
275
276 /* Log the certificate's name in "#SN/ISSUERDN" format along with
277    TEXT. */
278 void 
279 gpgsm_cert_log_name (const char *text, ksba_cert_t cert)
280 {
281   log_info ("%s", text? text:"certificate" );
282   if (cert)
283     {
284       ksba_sexp_t sn;
285       char *p;
286
287       p = ksba_cert_get_issuer (cert, 0);
288       sn = ksba_cert_get_serial (cert);
289       if (p && sn)
290         {
291           log_printf (" #");
292           gpgsm_dump_serial (sn);
293           log_printf ("/");
294           gpgsm_dump_string (p);
295         }
296       else
297         log_printf (" [invalid]");
298       ksba_free (sn);
299       xfree (p);
300     }
301   log_printf ("\n");
302 }
303
304
305
306
307
308 \f
309 /* helper for the rfc2253 string parser */
310 static const unsigned char *
311 parse_dn_part (struct dn_array_s *array, const unsigned char *string)
312 {
313   static struct {
314     const char *label;
315     const char *oid;
316   } label_map[] = {
317     /* Warning: When adding new labels, make sure that the buffer
318        below we be allocated large enough. */
319     {"EMail",        "1.2.840.113549.1.9.1" },
320     {"T",            "2.5.4.12" },
321     {"GN",           "2.5.4.42" },
322     {"SN",           "2.5.4.4" },
323     {"NameDistinguisher", "0.2.262.1.10.7.20"}, 
324     {"ADDR",         "2.5.4.16" },
325     {"BC",           "2.5.4.15" },
326     {"D",            "2.5.4.13" },
327     {"PostalCode",   "2.5.4.17" },
328     {"Pseudo",       "2.5.4.65" },
329     {"SerialNumber", "2.5.4.5" },
330     {NULL, NULL}
331   };
332   const unsigned char *s, *s1;
333   size_t n;
334   char *p;
335   int i;
336
337   /* Parse attributeType */
338   for (s = string+1; *s && *s != '='; s++)
339     ;
340   if (!*s)
341     return NULL; /* error */
342   n = s - string;
343   if (!n)
344     return NULL; /* empty key */
345
346   /* We need to allocate a few bytes more due to the possible mapping
347      from the shorter OID to the longer label. */
348   array->key = p = xtrymalloc (n+10);
349   if (!array->key)
350     return NULL;
351   memcpy (p, string, n); 
352   p[n] = 0;
353   trim_trailing_spaces (p);
354
355   if (digitp (p))
356     {
357       for (i=0; label_map[i].label; i++ )
358         if ( !strcmp (p, label_map[i].oid) )
359           {
360             strcpy (p, label_map[i].label);
361             break;
362           }
363     }
364   string = s + 1;
365
366   if (*string == '#')
367     { /* hexstring */
368       string++;
369       for (s=string; hexdigitp (s); s++)
370         s++;
371       n = s - string;
372       if (!n || (n & 1))
373         return NULL; /* Empty or odd number of digits. */
374       n /= 2;
375       array->value = p = xtrymalloc (n+1);
376       if (!p)
377         return NULL;
378       for (s1=string; n; s1 += 2, n--, p++)
379         {
380           *(unsigned char *)p = xtoi_2 (s1);
381           if (!*p)
382             *p = 0x01; /* Better print a wrong value than truncating
383                           the string. */
384         }
385       *p = 0;
386    }
387   else
388     { /* regular v3 quoted string */
389       for (n=0, s=string; *s; s++)
390         {
391           if (*s == '\\')
392             { /* pair */
393               s++;
394               if (*s == ',' || *s == '=' || *s == '+'
395                   || *s == '<' || *s == '>' || *s == '#' || *s == ';' 
396                   || *s == '\\' || *s == '\"' || *s == ' ')
397                 n++;
398               else if (hexdigitp (s) && hexdigitp (s+1))
399                 {
400                   s++;
401                   n++;
402                 }
403               else
404                 return NULL; /* invalid escape sequence */
405             }
406           else if (*s == '\"')
407             return NULL; /* invalid encoding */
408           else if (*s == ',' || *s == '=' || *s == '+'
409                    || *s == '<' || *s == '>' || *s == ';' )
410             break; 
411           else
412             n++;
413         }
414
415       array->value = p = xtrymalloc (n+1);
416       if (!p)
417         return NULL;
418       for (s=string; n; s++, n--)
419         {
420           if (*s == '\\')
421             { 
422               s++;
423               if (hexdigitp (s))
424                 {
425                   *(unsigned char *)p++ = xtoi_2 (s);
426                   s++;
427                 }
428               else
429                 *p++ = *s;
430             }
431           else
432             *p++ = *s;
433         }
434       *p = 0;
435     }
436   return s;
437 }
438
439
440 /* Parse a DN and return an array-ized one.  This is not a validating
441    parser and it does not support any old-stylish syntax; KSBA is
442    expected to return only rfc2253 compatible strings. */
443 static struct dn_array_s *
444 parse_dn (const unsigned char *string)
445 {
446   struct dn_array_s *array;
447   size_t arrayidx, arraysize;
448   int i;
449
450   arraysize = 7; /* C,ST,L,O,OU,CN,email */
451   arrayidx = 0;
452   array = xtrymalloc ((arraysize+1) * sizeof *array);
453   if (!array)
454     return NULL;
455   while (*string)
456     {
457       while (*string == ' ')
458         string++;
459       if (!*string)
460         break; /* ready */
461       if (arrayidx >= arraysize)
462         { 
463           struct dn_array_s *a2;
464
465           arraysize += 5;
466           a2 = xtryrealloc (array, (arraysize+1) * sizeof *array);
467           if (!a2)
468             goto failure;
469           array = a2;
470         }
471       array[arrayidx].key = NULL;
472       array[arrayidx].value = NULL;
473       string = parse_dn_part (array+arrayidx, string);
474       if (!string)
475         goto failure;
476       while (*string == ' ')
477         string++;
478       array[arrayidx].multivalued = (*string == '+');
479       array[arrayidx].done = 0;
480       arrayidx++;
481       if (*string && *string != ',' && *string != ';' && *string != '+')
482         goto failure; /* invalid delimiter */
483       if (*string)
484         string++;
485     }
486   array[arrayidx].key = NULL;
487   array[arrayidx].value = NULL;
488   return array;
489
490  failure:
491   for (i=0; i < arrayidx; i++)
492     {
493       xfree (array[i].key);
494       xfree (array[i].value);
495     }
496   xfree (array);
497   return NULL;
498 }
499
500
501 /* Print a DN part to STREAM or if STREAM is NULL to FP. */
502 static void
503 print_dn_part (FILE *fp, estream_t stream,
504                struct dn_array_s *dn, const char *key, int translate)
505 {
506   struct dn_array_s *first_dn = dn;
507
508   for (; dn->key; dn++)
509     {
510       if (!dn->done && !strcmp (dn->key, key))
511         {
512           /* Forward to the last multi-valued RDN, so that we can
513              print them all in reverse in the correct order.  Note
514              that this overrides the the standard sequence but that
515              seems to a reasonable thing to do with multi-valued
516              RDNs. */
517           while (dn->multivalued && dn[1].key)
518             dn++;
519         next:
520           if (!dn->done && dn->value && *dn->value)
521             {
522               if (stream)
523                 {
524                   es_fprintf (stream, "/%s=", dn->key);
525                   if (translate)
526                     es_write_sanitized_utf8_buffer (stream, dn->value,
527                                                     strlen (dn->value),
528                                                     "/", NULL);
529                   else
530                     es_write_sanitized (stream, dn->value, strlen (dn->value),
531                                         "/", NULL);
532                 }
533               else
534                 {
535                   fprintf (fp, "/%s=", dn->key);
536                   if (translate)
537                     print_sanitized_utf8_string (fp, dn->value, '/');
538                   else
539                     print_sanitized_string (fp, dn->value, '/');
540                 }
541             }
542           dn->done = 1;
543           if (dn > first_dn && dn[-1].multivalued)
544             {
545               dn--;
546               goto next;
547             }
548         }
549     }
550 }
551
552 /* Print all parts of a DN in a "standard" sequence.  We first print
553    all the known parts, followed by the uncommon ones */
554 static void
555 print_dn_parts (FILE *fp, estream_t stream,
556                 struct dn_array_s *dn, int translate)
557 {
558   const char *stdpart[] = {
559     "CN", "OU", "O", "STREET", "L", "ST", "C", "EMail", NULL 
560   };
561   int i;
562   
563   for (i=0; stdpart[i]; i++)
564       print_dn_part (fp, stream, dn, stdpart[i], translate);
565
566   /* Now print the rest without any specific ordering */
567   for (; dn->key; dn++)
568     print_dn_part (fp, stream, dn, dn->key, translate);
569 }
570
571
572 /* Print the S-Expression in BUF, which has a valid length of BUFLEN,
573    as a human readable string in one line to FP. */
574 static void
575 pretty_print_sexp (FILE *fp, const unsigned char *buf, size_t buflen)
576 {
577   size_t len;
578   gcry_sexp_t sexp;
579   char *result, *p;
580
581   if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) )
582     {
583       fputs (_("[Error - invalid encoding]"), fp);
584       return;
585     }
586   len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
587   assert (len);
588   result = xtrymalloc (len);
589   if (!result)
590     {
591       fputs (_("[Error - out of core]"), fp);
592       gcry_sexp_release (sexp);
593       return;
594     }
595   len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
596   assert (len);
597   for (p = result; len; len--, p++)
598     {
599       if (*p == '\n')
600         {
601           if (len > 1) /* Avoid printing the trailing LF. */
602             fputs ("\\n", fp);
603         }
604       else if (*p == '\r')
605         fputs ("\\r", fp);
606       else if (*p == '\v')
607         fputs ("\\v", fp);
608       else if (*p == '\t')
609         fputs ("\\t", fp);
610       else
611         putc (*p, fp);
612     }
613   xfree (result);
614   gcry_sexp_release (sexp);
615 }
616
617 /* Print the S-Expression in BUF to extended STREAM, which has a valid
618    length of BUFLEN, as a human readable string in one line to FP. */
619 static void
620 pretty_es_print_sexp (estream_t fp, const unsigned char *buf, size_t buflen)
621 {
622   size_t len;
623   gcry_sexp_t sexp;
624   char *result, *p;
625
626   if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) )
627     {
628       es_fputs (_("[Error - invalid encoding]"), fp);
629       return;
630     }
631   len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
632   assert (len);
633   result = xtrymalloc (len);
634   if (!result)
635     {
636       es_fputs (_("[Error - out of core]"), fp);
637       gcry_sexp_release (sexp);
638       return;
639     }
640   len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
641   assert (len);
642   for (p = result; len; len--, p++)
643     {
644       if (*p == '\n')
645         {
646           if (len > 1) /* Avoid printing the trailing LF. */
647             es_fputs ("\\n", fp);
648         }
649       else if (*p == '\r')
650         es_fputs ("\\r", fp);
651       else if (*p == '\v')
652         es_fputs ("\\v", fp);
653       else if (*p == '\t')
654         es_fputs ("\\t", fp);
655       else
656         es_putc (*p, fp);
657     }
658   xfree (result);
659   gcry_sexp_release (sexp);
660 }
661
662
663
664
665 void
666 gpgsm_print_name2 (FILE *fp, const char *name, int translate)
667 {
668   const unsigned char *s = (const unsigned char *)name;
669   int i;
670
671   if (!s)
672     {
673       fputs (_("[Error - No name]"), fp);
674     }
675   else if (*s == '<')
676     {
677       const char *s2 = strchr ( (char*)s+1, '>');
678       if (s2)
679         {
680           if (translate)
681             print_sanitized_utf8_buffer (fp, s + 1, s2 - (char*)s - 1, 0);
682           else
683             print_sanitized_buffer (fp, s + 1, s2 - (char*)s - 1, 0);
684         }
685     }
686   else if (*s == '(')
687     {
688       pretty_print_sexp (fp, s, gcry_sexp_canon_len (s, 0, NULL, NULL));
689     }
690   else if (!((*s >= '0' && *s < '9')
691              || (*s >= 'A' && *s <= 'Z')
692              || (*s >= 'a' && *s <= 'z')))
693     fputs (_("[Error - invalid encoding]"), fp);
694   else
695     {
696       struct dn_array_s *dn = parse_dn (s);
697       if (!dn)
698         fputs (_("[Error - invalid DN]"), fp);
699       else 
700         {
701           print_dn_parts (fp, NULL, dn, translate);          
702           for (i=0; dn[i].key; i++)
703             {
704               xfree (dn[i].key);
705               xfree (dn[i].value);
706             }
707           xfree (dn);
708         }
709     }
710 }
711
712
713 void
714 gpgsm_print_name (FILE *fp, const char *name)
715 {
716   gpgsm_print_name2 (fp, name, 1);
717 }
718
719
720 /* This is avariant of gpgsm_print_name sending it output to an estream. */
721 void
722 gpgsm_es_print_name (estream_t fp, const char *name)
723 {
724   const unsigned char *s = (const unsigned char *)name;
725   int i;
726
727   if (!s)
728     {
729       es_fputs (_("[Error - No name]"), fp);
730     }
731   else if (*s == '<')
732     {
733       const char *s2 = strchr ( (char*)s+1, '>');
734
735       if (s2)
736         es_write_sanitized_utf8_buffer (fp, s + 1, s2 - (char*)s - 1,
737                                         NULL, NULL);
738     }
739   else if (*s == '(')
740     {
741       pretty_es_print_sexp (fp, s, gcry_sexp_canon_len (s, 0, NULL, NULL));
742     }
743   else if (!((*s >= '0' && *s < '9')
744              || (*s >= 'A' && *s <= 'Z')
745              || (*s >= 'a' && *s <= 'z')))
746     es_fputs (_("[Error - invalid encoding]"), fp);
747   else
748     {
749       struct dn_array_s *dn = parse_dn (s);
750
751       if (!dn)
752         es_fputs (_("[Error - invalid DN]"), fp);
753       else 
754         {
755           print_dn_parts (NULL, fp, dn, 1);          
756           for (i=0; dn[i].key; i++)
757             {
758               xfree (dn[i].key);
759               xfree (dn[i].value);
760             }
761           xfree (dn);
762         }
763     }
764 }
765
766
767
768
769 #if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN)
770 /* A cookie structure used for the memory stream. */
771 struct format_name_cookie 
772 {
773   char *buffer;         /* Malloced buffer with the data to deliver. */
774   size_t size;          /* Allocated size of this buffer. */
775   size_t len;           /* strlen (buffer). */
776   int error;            /* system error code if any. */
777 };
778
779 /* The writer function for the memory stream. */
780 static my_funopen_hook_ret_t
781 format_name_writer (void *cookie, const char *buffer, size_t size)
782 {
783   struct format_name_cookie *c = cookie;
784   char *p;
785
786   if (c->buffer)
787     p = xtryrealloc (c->buffer, c->size + size + 1);
788   else
789     p = xtrymalloc (size + 1);
790   if (!p)
791     {
792       c->error = errno;
793       xfree (c->buffer);
794       errno = c->error;
795       return -1;
796     }
797   c->buffer = p;
798   memcpy (p + c->len, buffer, size);
799   c->len += size;
800   p[c->len] = 0; /* Terminate string. */ 
801
802   return size;
803 }
804 #endif /*HAVE_FOPENCOOKIE || HAVE_FUNOPEN*/
805
806
807 /* Format NAME which is expected to be in rfc2253 format into a better
808    human readable format. Caller must free the returned string.  NULL
809    is returned in case of an error.  With TRANSLATE set to true the
810    name will be translated to the native encoding.  Note that NAME is
811    internally always UTF-8 encoded. */
812 char *
813 gpgsm_format_name2 (const char *name, int translate)
814 {
815 #if defined (HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN)
816   FILE *fp;
817   struct format_name_cookie cookie;
818
819   memset (&cookie, 0, sizeof cookie);
820
821 #ifdef HAVE_FOPENCOOKIE
822   {
823     cookie_io_functions_t io = { NULL };
824     io.write = format_name_writer;
825     
826     fp = fopencookie (&cookie, "w", io);
827   }
828 #else /*!HAVE_FOPENCOOKIE*/
829   {
830     fp = funopen (&cookie, NULL, format_name_writer, NULL, NULL);
831   }
832 #endif /*!HAVE_FOPENCOOKIE*/
833   if (!fp)
834     {
835       int save_errno = errno;
836       log_error ("error creating memory stream: %s\n", strerror (errno));
837       errno = save_errno;
838       return NULL;
839     }
840   gpgsm_print_name2 (fp, name, translate);
841   fclose (fp);
842   if (cookie.error || !cookie.buffer)
843     {
844       xfree (cookie.buffer);
845       errno = cookie.error;
846       return NULL;
847     }
848   return cookie.buffer;
849 #else /* No fun - use the name verbatim. */
850   return xtrystrdup (name);
851 #endif /* No fun. */
852 }
853
854 char *
855 gpgsm_format_name (const char *name)
856 {
857   return gpgsm_format_name2 (name, 1);
858 }
859
860
861 /* Return fingerprint and a percent escaped name in a human readable
862    format suitable for status messages like GOODSIG.  May return NULL
863    on error (out of core). */
864 char *
865 gpgsm_fpr_and_name_for_status (ksba_cert_t cert)
866 {
867   char *fpr, *name, *p;
868   char *buffer;
869
870   fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
871   if (!fpr)
872     return NULL;
873   
874   name = ksba_cert_get_subject (cert, 0);
875   if (!name)
876     {
877       xfree (fpr);
878       return NULL;
879     }
880
881   p = gpgsm_format_name2 (name, 0);
882   ksba_free (name);
883   name = p;
884   if (!name)
885     {
886       xfree (fpr);
887       return NULL;
888     }
889
890   buffer = xtrymalloc (strlen (fpr) + 1 + 3*strlen (name) + 1);
891   if (buffer)
892     {
893       const char *s;
894
895       p = stpcpy (stpcpy (buffer, fpr), " ");
896       for (s = name; *s; s++)
897         {
898           if (*s < ' ')
899             {
900               sprintf (p, "%%%02X", *(const unsigned char*)s);
901               p += 3;
902             }
903           else
904             *p++ = *s;
905         }
906       *p = 0;
907     }
908   xfree (fpr);
909   xfree (name);
910   return buffer;
911 }
912
913
914 /* Create a key description for the CERT, this may be passed to the
915    pinentry.  The caller must free the returned string. NULL may be
916    returned on error. */
917 char *
918 gpgsm_format_keydesc (ksba_cert_t cert)
919 {
920   int rc;
921   char *name, *subject, *buffer, *p;
922   const char *s;
923   ksba_isotime_t t;
924   char created[20];
925   char expires[20];
926   char *sn;
927   ksba_sexp_t sexp;
928   char *orig_codeset;
929
930   name = ksba_cert_get_subject (cert, 0);
931   subject = name? gpgsm_format_name2 (name, 0) : NULL;
932   ksba_free (name); name = NULL;
933
934   sexp = ksba_cert_get_serial (cert);
935   sn = sexp? gpgsm_format_serial (sexp) : NULL;
936   ksba_free (sexp);
937
938   ksba_cert_get_validity (cert, 0, t);
939   if (*t)
940     sprintf (created, "%.4s-%.2s-%.2s", t, t+4, t+6);
941   else
942     *created = 0;
943   ksba_cert_get_validity (cert, 1, t);
944   if (*t)
945     sprintf (expires, "%.4s-%.2s-%.2s", t, t+4, t+6);
946   else
947     *expires = 0;
948
949   orig_codeset = i18n_switchto_utf8 ();
950
951   rc = asprintf (&name,
952                  _("Please enter the passphrase to unlock the"
953                    " secret key for the X.509 certificate:\n"
954                    "\"%s\"\n"
955                    "S/N %s, ID 0x%08lX,\n"
956                    "created %s, expires %s.\n" ),
957                  subject? subject:"?",
958                  sn? sn: "?",
959                  gpgsm_get_short_fingerprint (cert),
960                  created, expires);
961
962   i18n_switchback (orig_codeset);
963
964   if (rc < 0)
965     {
966       int save_errno = errno;
967       xfree (subject);
968       xfree (sn);
969       errno = save_errno;
970       return NULL;
971     }
972   
973   xfree (subject);
974   xfree (sn);
975
976   buffer = p = xtrymalloc (strlen (name) * 3 + 1);
977   for (s=name; *s; s++)
978     {
979       if (*s < ' ' || *s == '+')
980         {
981           sprintf (p, "%%%02X", *(unsigned char *)s);
982           p += 3;
983         }
984       else if (*s == ' ')
985         *p++ = '+';
986       else
987         *p++ = *s;
988     }
989   *p = 0;
990   free (name); 
991
992   return buffer;
993 }
994