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