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