[svn] GnuPG - r4463 - in trunk: . common doc sm

svn author wk cvs at cvs.gnupg.org
Mon Mar 19 15:35:07 CET 2007


Author: wk
Date: 2007-03-19 15:35:04 +0100 (Mon, 19 Mar 2007)
New Revision: 4463

Modified:
   trunk/NEWS
   trunk/TODO
   trunk/common/ChangeLog
   trunk/common/estream.c
   trunk/common/estream.h
   trunk/common/miscellaneous.c
   trunk/common/util.h
   trunk/doc/vuln-announce-2007-multiple-message.txt
   trunk/sm/ChangeLog
   trunk/sm/certchain.c
   trunk/sm/certdump.c
   trunk/sm/gpgsm.c
   trunk/sm/gpgsm.h
   trunk/sm/keylist.c
   trunk/sm/server.c
Log:
Changes to let the key listing use estream to help systems without
funopen.


Modified: trunk/NEWS
===================================================================
--- trunk/NEWS	2007-03-14 13:26:18 UTC (rev 4462)
+++ trunk/NEWS	2007-03-19 14:35:04 UTC (rev 4463)
@@ -1,7 +1,10 @@
 Noteworthy changes in version 2.0.4
 ------------------------------------------------
 
+ * The Assuan key listing commands are now also working for systems
+   without the funopen/fopencookie API.
 
+
 Noteworthy changes in version 2.0.3 (2007-03-08)
 ------------------------------------------------
 
@@ -10,7 +13,7 @@
    did not permit this, and were thus not using the plaintext boundary
    status tags that GnuPG provides.  This change makes GnuPG reject
    such messages by default which makes those programs safe again.
-   --allow-multiple-messages returns to the old behavior.
+   --allow-multiple-messages returns to the old behavior. [CVE-2007-1263].
 
  * New --verify-option show-primary-uid-only. 
 

Modified: trunk/TODO
===================================================================
--- trunk/TODO	2007-03-14 13:26:18 UTC (rev 4462)
+++ trunk/TODO	2007-03-19 14:35:04 UTC (rev 4463)
@@ -76,8 +76,6 @@
 ** Add a test to check the extkeyusage.
 
 * Windows port
-** gpgsm's LISTKEYS does not yet work
-    Fix is to change everything to libestream
 ** Signals are not support 
     This means we can't reread a configuration
 ** No card status notifications.
@@ -85,7 +83,11 @@
 * sm/
 ** check that we issue NO_SECKEY xxx if a -u key was not found
    We don't. The messages returned are also wrong (recipient vs. signer).
+** cmd_export
+   Does only work on systems with funopen/fopencookie.  Changing is
+   easy.
 
+
 * jnlib/
 ** provide jnlib_malloc and try to remove all jnlib_xmalloc.
 
@@ -124,5 +126,12 @@
 
 * Pinpad Reader
   We do not yet support P15 applications.  The trivial thing using
-  ASCII characters will be easy to implement but the otehr cases need
+  ASCII characters will be easy to implement but the other cases need
   some more work.
+
+* Bugs
+** After disabling scdaemon and sending a HUP
+   scdaemon stays as a zombie and gpg-agent does not perform any more
+   commands.
+
+

Modified: trunk/common/ChangeLog
===================================================================
--- trunk/common/ChangeLog	2007-03-14 13:26:18 UTC (rev 4462)
+++ trunk/common/ChangeLog	2007-03-19 14:35:04 UTC (rev 4463)
@@ -1,3 +1,10 @@
+2007-03-19  Werner Koch  <wk at g10code.com>
+
+	* estream.c (es_fprintf_unlocked): New.
+	(es_write_sanitized): New.
+	(es_write_hexstring): New.
+	(es_write_sanitized_utf8_buffer) [GNUPG_MAJOR_VERSION]: New.
+
 2007-03-09  David Shaw  <dshaw at jabberwocky.com>
 
 	From STABLE-BRANCH-1-4

Modified: trunk/common/estream.c
===================================================================
--- trunk/common/estream.c	2007-03-14 13:26:18 UTC (rev 4462)
+++ trunk/common/estream.c	2007-03-19 14:35:04 UTC (rev 4463)
@@ -1,5 +1,5 @@
 /* estream.c - Extended Stream I/O Library
- * Copyright (C) 2004, 2006 g10 Code GmbH
+ * Copyright (C) 2004, 2006, 2007 g10 Code GmbH
  *
  * This file is part of Libestream.
  *
@@ -49,6 +49,10 @@
 # include <pth.h>
 #endif
 
+#ifdef GNUPG_MAJOR_VERSION
+#include "../common/util.h"
+#endif
+
 #ifndef HAVE_MKSTEMP
 int mkstemp (char *template);
 #endif
@@ -205,7 +209,9 @@
 /* Macros.  */
 
 /* Calculate array dimension.  */
+#ifndef DIM
 #define DIM(array) (sizeof (array) / sizeof (*array))
+#endif
 
 /* Evaluate EXPRESSION, setting VARIABLE to the return code, if
    VARIABLE is zero.  */
@@ -740,6 +746,14 @@
 static int
 es_convert_mode (const char *mode, unsigned int *flags)
 {
+
+  /* FIXME: We need to allow all combinations for mode flags and for
+     binary we need to do a
+
+     #ifdef HAVE_DOSISH_SYSTEM
+       setmode (fd, O_BINARY);
+     #endif
+  */
   struct
   {
     const char *mode;
@@ -2702,6 +2716,21 @@
 }
 
 
+static int
+es_fprintf_unlocked (estream_t ES__RESTRICT stream,
+	    const char *ES__RESTRICT format, ...)
+{
+  int ret;
+  
+  va_list ap;
+  va_start (ap, format);
+  ret = es_print (stream, format, ap);
+  va_end (ap);
+
+  return ret;
+}
+
+
 int
 es_fprintf (estream_t ES__RESTRICT stream,
 	    const char *ES__RESTRICT format, ...)
@@ -2839,3 +2868,163 @@
   return opaque;
 }
 
+
+
+/* Print a BUFFER to STREAM while replacing all control characters and
+   the characters in DELIMITERS by standard C escape sequences.
+   Returns 0 on success or -1 on error.  If BYTES_WRITTEN is not NULL
+   the number of bytes actually written are stored at this
+   address.  */
+int 
+es_write_sanitized (estream_t ES__RESTRICT stream,
+                    const void * ES__RESTRICT buffer, size_t length,
+                    const char * delimiters, 
+                    size_t * ES__RESTRICT bytes_written)
+{
+  const unsigned char *p = buffer;
+  size_t count = 0;
+  int ret;
+
+  ESTREAM_LOCK (stream);
+  for (; length; length--, p++, count++)
+    {
+      if (*p < 0x20 
+          || (*p >= 0x7f && *p < 0xa0)
+          || (delimiters 
+              && (strchr (delimiters, *p) || *p == '\\')))
+        {
+          es_putc_unlocked ('\\', stream);
+          count++;
+          if (*p == '\n')
+            {
+              es_putc_unlocked ('n', stream);
+              count++;
+            }
+          else if (*p == '\r')
+            {
+              es_putc_unlocked ('r', stream);
+              count++;
+            }
+          else if (*p == '\f')
+            {
+              es_putc_unlocked ('f', stream);
+              count++;
+            }
+          else if (*p == '\v')
+            {
+              es_putc_unlocked ('v', stream);
+              count++;
+            }
+          else if (*p == '\b')
+            {
+              es_putc_unlocked ('b', stream);
+              count++;
+            }
+          else if (!*p)
+            {
+              es_putc_unlocked('0', stream);
+              count++;
+            }
+          else
+            {
+              es_fprintf_unlocked (stream, "x%02x", *p);
+              count += 3;
+            }
+	}
+      else
+        {
+          es_putc_unlocked (*p, stream);
+          count++;
+        }
+    }
+
+  if (bytes_written)
+    *bytes_written = count;
+  ret =  es_ferror_unlocked (stream)? -1 : 0;
+  ESTREAM_UNLOCK (stream);
+
+  return ret;
+}
+
+
+/* Write LENGTH bytes of BUFFER to STREAM as a hex encoded string.
+   RESERVED must be 0.  Returns 0 on success or -1 on error.  If
+   BYTES_WRITTEN is not NULL the number of bytes actually written are
+   stored at this address.  */
+int
+es_write_hexstring (estream_t ES__RESTRICT stream,
+                    const void *ES__RESTRICT buffer, size_t length,
+                    int reserved, size_t *ES__RESTRICT bytes_written )
+{
+  int ret;
+  const unsigned char *s;
+  size_t count = 0;
+
+#define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
+
+  if (!length)
+    return 0;
+
+  ESTREAM_LOCK (stream);
+
+  for (s = buffer; length; s++, length--)
+    {
+      es_putc_unlocked ( tohex ((*s>>4)&15), stream);
+      es_putc_unlocked ( tohex (*s&15), stream);
+      count += 2;
+    }
+
+  if (bytes_written)
+    *bytes_written = count;
+  ret = es_ferror_unlocked (stream)? -1 : 0;
+
+  ESTREAM_UNLOCK (stream);
+
+  return ret;
+
+#undef tohex
+}
+
+
+
+#ifdef GNUPG_MAJOR_VERSION
+/* Special estream function to print an UTF8 string in the native
+   encoding.  The interface is the same as es_write_sanitized, however
+   only one delimiter may be supported. 
+
+   THIS IS NOT A STANDARD ESTREAM FUNCTION AND ONLY USED BY GNUPG. */
+int
+es_write_sanitized_utf8_buffer (estream_t stream,
+                                const void *buffer, size_t length, 
+                                const char *delimiters, size_t *bytes_written)
+{
+  const char *p = buffer;
+  size_t i;
+
+  /* We can handle plain ascii simpler, so check for it first. */
+  for (i=0; i < length; i++ ) 
+    {
+      if ( (p[i] & 0x80) )
+        break;
+    }
+  if (i < length)
+    {
+      int delim = delimiters? *delimiters : 0;
+      char *buf;
+      int ret;
+
+      /*(utf8 conversion already does the control character quoting). */
+      buf = utf8_to_native (p, length, delim);
+      if (bytes_written)
+        *bytes_written = strlen (buf);
+      ret = es_fputs (buf, stream);
+      xfree (buf);
+      return i;
+    }
+  else
+    return es_write_sanitized (stream, p, length, delimiters, bytes_written);
+}
+#endif /*GNUPG_MAJOR_VERSION*/
+
+
+

Modified: trunk/common/estream.h
===================================================================
--- trunk/common/estream.h	2007-03-14 13:26:18 UTC (rev 4462)
+++ trunk/common/estream.h	2007-03-19 14:35:04 UTC (rev 4463)
@@ -172,6 +172,13 @@
 int es_write (estream_t ES__RESTRICT stream,
 	      const void *ES__RESTRICT buffer, size_t bytes_to_write,
 	      size_t *ES__RESTRICT bytes_written);
+int es_write_sanitized (estream_t ES__RESTRICT stream,
+                        const void *ES__RESTRICT buffer, size_t length,
+                        const char *delimiters,
+                        size_t *ES__RESTRICT bytes_written);
+int es_write_hexstring (estream_t ES__RESTRICT stream,
+                        const void *ES__RESTRICT buffer, size_t length,
+                        int reserved, size_t *ES__RESTRICT bytes_written);
 
 size_t es_fread (void *ES__RESTRICT ptr, size_t size, size_t nitems,
 		 estream_t ES__RESTRICT stream);
@@ -203,5 +210,15 @@
 void es_opaque_set (estream_t ES__RESTRICT stream, void *ES__RESTRICT opaque);
 void *es_opaque_get (estream_t stream);
 
+
+
+#ifdef GNUPG_MAJOR_VERSION
+int es_write_sanitized_utf8_buffer (estream_t stream,
+                                    const void *buffer, size_t length, 
+                                    const char *delimiters,
+                                    size_t *bytes_written);
+#endif /*GNUPG_MAJOR_VERSION*/
+
+
 #endif /*ESTREAM_H*/
 

Modified: trunk/common/miscellaneous.c
===================================================================
--- trunk/common/miscellaneous.c	2007-03-14 13:26:18 UTC (rev 4462)
+++ trunk/common/miscellaneous.c	2007-03-19 14:35:04 UTC (rev 4463)
@@ -74,6 +74,7 @@
 }
 
 
+
 /*
  * Check if the file is compressed.
  */

Modified: trunk/common/util.h
===================================================================
--- trunk/common/util.h	2007-03-14 13:26:18 UTC (rev 4462)
+++ trunk/common/util.h	2007-03-19 14:35:04 UTC (rev 4463)
@@ -43,7 +43,6 @@
 #include "../jnlib/dotlock.h"
 #include "../jnlib/utf8conv.h"
 
-
 #if __GNUC__ >= 4 
 # define GNUPG_GCC_A_SENTINEL(a) __attribute__ ((sentinel(a)))
 #else

Modified: trunk/doc/vuln-announce-2007-multiple-message.txt
===================================================================
--- trunk/doc/vuln-announce-2007-multiple-message.txt	2007-03-14 13:26:18 UTC (rev 4462)
+++ trunk/doc/vuln-announce-2007-multiple-message.txt	2007-03-19 14:35:04 UTC (rev 4463)
@@ -1,6 +1,6 @@
              Multiple Messages Problem in GnuPG and GPGME
             ==============================================
-                              2007-03-05
+                     2007-03-05  [CVE-2007-1263]
 
 
 Summary

Modified: trunk/sm/ChangeLog
===================================================================
--- trunk/sm/ChangeLog	2007-03-14 13:26:18 UTC (rev 4462)
+++ trunk/sm/ChangeLog	2007-03-19 14:35:04 UTC (rev 4463)
@@ -1,3 +1,25 @@
+2007-03-19  Werner Koch  <wk at g10code.com>
+
+        Change to let the key listing use estream to help systems without
+	funopen.
+	
+	* keylist.c: Use estream in place of stdio functions.
+	* gpgsm.c (open_es_fwrite): New.
+	(main): Use it for the list commands.
+	* server.c (data_line_cookie_functions): New.
+	(data_line_cookie_write, data_line_cookie_close): New.
+	(do_listkeys): Use estream.
+	* certdump.c (gpgsm_print_serial): Changed to use estream.
+	(gpgsm_print_time): Ditto.
+	(pretty_es_print_sexp): New.
+	(gpgsm_es_print_name): New.
+	(print_dn_part): New arg STREAM.  Changed all callers.
+	(print_dn_parts): Ditto.
+	* certchain.c (gpgsm_validate_chain): Changed FP to type
+	estream_t.
+	(do_list, unknown_criticals, allowed_ca, check_cert_policy) 
+	(is_cert_still_valid): Ditto.
+
 2007-01-31  Werner Koch  <wk at g10code.com>
 
 	* gpgsm.c (main): Let --gen-key print a more informative error

Modified: trunk/sm/certchain.c
===================================================================
--- trunk/sm/certchain.c	2007-03-14 13:26:18 UTC (rev 4462)
+++ trunk/sm/certchain.c	2007-03-19 14:35:04 UTC (rev 4463)
@@ -93,7 +93,7 @@
    LISTMODE is false, use the string to print an log_info or, if
    IS_ERROR is true, and log_error. */
 static void
-do_list (int is_error, int listmode, FILE *fp, const char *format, ...)
+do_list (int is_error, int listmode, estream_t fp, const char *format, ...)
 {
   va_list arg_ptr;
 
@@ -102,9 +102,9 @@
     {
       if (fp)
         {
-          fputs ("  [", fp);
-          vfprintf (fp, format, arg_ptr);
-          fputs ("]\n", fp);
+          es_fputs ("  [", fp);
+          es_vfprintf (fp, format, arg_ptr);
+          es_fputs ("]\n", fp);
         }
     }
   else
@@ -133,7 +133,7 @@
 
 
 static int
-unknown_criticals (ksba_cert_t cert, int listmode, FILE *fp)
+unknown_criticals (ksba_cert_t cert, int listmode, estream_t fp)
 {
   static const char *known[] = {
     "2.5.29.15", /* keyUsage */
@@ -183,7 +183,7 @@
    BasicConstraints extension.  The function returns 0 on success and
    the awlloed length of the chain at CHAINLEN. */
 static int
-allowed_ca (ksba_cert_t cert, int *chainlen, int listmode, FILE *fp)
+allowed_ca (ksba_cert_t cert, int *chainlen, int listmode, estream_t fp)
 {
   gpg_error_t err;
   int flag;
@@ -208,7 +208,7 @@
 
 
 static int
-check_cert_policy (ksba_cert_t cert, int listmode, FILE *fplist)
+check_cert_policy (ksba_cert_t cert, int listmode, estream_t fplist)
 {
   gpg_error_t err;
   char *policies;
@@ -645,7 +645,7 @@
 
 /* This is a helper for gpgsm_validate_chain. */
 static gpg_error_t 
-is_cert_still_valid (ctrl_t ctrl, int lm, FILE *fp,
+is_cert_still_valid (ctrl_t ctrl, int lm, estream_t fp,
                      ksba_cert_t subject_cert, ksba_cert_t issuer_cert,
                      int *any_revoked, int *any_no_crl, int *any_crl_too_old)
 {
@@ -704,7 +704,7 @@
 */
 int
 gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t r_exptime,
-                      int listmode, FILE *fp, unsigned int flags)
+                      int listmode, estream_t fp, unsigned int flags)
 {
   int rc = 0, depth = 0, maxdepth;
   char *issuer = NULL;

Modified: trunk/sm/certdump.c
===================================================================
--- trunk/sm/certdump.c	2007-03-14 13:26:18 UTC (rev 4462)
+++ trunk/sm/certdump.c	2007-03-19 14:35:04 UTC (rev 4463)
@@ -1,5 +1,5 @@
 /* certdump.c - Dump a certificate for debugging
- *	Copyright (C) 2001, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2004, 2007 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -56,30 +56,27 @@
 };
 
 
-/* print the first element of an S-Expression */
+/* Print the first element of an S-Expression. */
 void
-gpgsm_print_serial (FILE *fp, ksba_const_sexp_t sn)
+gpgsm_print_serial (estream_t fp, ksba_const_sexp_t sn)
 {
   const char *p = (const char *)sn;
   unsigned long n;
   char *endp;
 
   if (!p)
-    fputs (_("none"), fp);
+    es_fputs (_("none"), fp);
   else if (*p != '(')
-    fputs ("[Internal error - not an S-expression]", fp);
+    es_fputs ("[Internal error - not an S-expression]", fp);
   else
     {
       p++;
       n = strtoul (p, &endp, 10);
       p = endp;
       if (*p!=':')
-        fputs ("[Internal Error - invalid S-expression]", fp);
+        es_fputs ("[Internal Error - invalid S-expression]", fp);
       else
-        {
-          for (p++; n; n--, p++)
-            fprintf (fp, "%02X", *(const unsigned char*)p);
-        }
+        es_write_hexstring (fp, p, strlen (p), 0, NULL);
     }
 }
 
@@ -148,14 +145,16 @@
 
 
 void
-gpgsm_print_time (FILE *fp, ksba_isotime_t t)
+gpgsm_print_time (estream_t fp, ksba_isotime_t t)
 {
   if (!t || !*t)
-    fputs (_("none"), fp);
+    es_fputs (_("none"), fp);
   else
-    fprintf (fp, "%.4s-%.2s-%.2s %.2s:%.2s:%s", t, t+4, t+6, t+9, t+11, t+13);
+    es_fprintf (fp, "%.4s-%.2s-%.2s %.2s:%.2s:%s",
+                t, t+4, t+6, t+9, t+11, t+13);
 }
 
+
 void
 gpgsm_dump_time (ksba_isotime_t t)
 {
@@ -468,8 +467,10 @@
 }
 
 
+/* Print a DN part to STREAM or if STREAM is NULL to FP. */
 static void
-print_dn_part (FILE *fp, struct dn_array_s *dn, const char *key, int translate)
+print_dn_part (FILE *fp, estream_t stream,
+               struct dn_array_s *dn, const char *key, int translate)
 {
   struct dn_array_s *first_dn = dn;
 
@@ -487,11 +488,25 @@
         next:
           if (!dn->done && dn->value && *dn->value)
             {
-              fprintf (fp, "/%s=", dn->key);
-              if (translate)
-                print_sanitized_utf8_string (fp, dn->value, '/');
+              if (stream)
+                {
+                  es_fprintf (stream, "/%s=", dn->key);
+                  if (translate)
+                    es_write_sanitized_utf8_buffer (stream, dn->value,
+                                                    strlen (dn->value),
+                                                    "/", NULL);
+                  else
+                    es_write_sanitized (stream, dn->value, strlen (dn->value),
+                                        "/", NULL);
+                }
               else
-                print_sanitized_string (fp, dn->value, '/');
+                {
+                  fprintf (fp, "/%s=", dn->key);
+                  if (translate)
+                    print_sanitized_utf8_string (fp, dn->value, '/');
+                  else
+                    print_sanitized_string (fp, dn->value, '/');
+                }
             }
           dn->done = 1;
           if (dn > first_dn && dn[-1].multivalued)
@@ -506,7 +521,8 @@
 /* Print all parts of a DN in a "standard" sequence.  We first print
    all the known parts, followed by the uncommon ones */
 static void
-print_dn_parts (FILE *fp, struct dn_array_s *dn, int translate)
+print_dn_parts (FILE *fp, estream_t stream,
+                struct dn_array_s *dn, int translate)
 {
   const char *stdpart[] = {
     "CN", "OU", "O", "STREET", "L", "ST", "C", "EMail", NULL 
@@ -514,11 +530,11 @@
   int i;
   
   for (i=0; stdpart[i]; i++)
-      print_dn_part (fp, dn, stdpart[i], translate);
+      print_dn_part (fp, stream, dn, stdpart[i], translate);
 
   /* Now print the rest without any specific ordering */
   for (; dn->key; dn++)
-    print_dn_part (fp, dn, dn->key, translate);
+    print_dn_part (fp, stream, dn, dn->key, translate);
 }
 
 
@@ -567,7 +583,54 @@
   gcry_sexp_release (sexp);
 }
 
+/* Print the S-Expression in BUF to extended STREAM, which has a valid
+   length of BUFLEN, as a human readable string in one line to FP. */
+static void
+pretty_es_print_sexp (estream_t fp, const unsigned char *buf, size_t buflen)
+{
+  size_t len;
+  gcry_sexp_t sexp;
+  char *result, *p;
 
+  if ( gcry_sexp_sscan (&sexp, NULL, (const char*)buf, buflen) )
+    {
+      es_fputs (_("[Error - invalid encoding]"), fp);
+      return;
+    }
+  len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, NULL, 0);
+  assert (len);
+  result = xtrymalloc (len);
+  if (!result)
+    {
+      es_fputs (_("[Error - out of core]"), fp);
+      gcry_sexp_release (sexp);
+      return;
+    }
+  len = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_ADVANCED, result, len);
+  assert (len);
+  for (p = result; len; len--, p++)
+    {
+      if (*p == '\n')
+        {
+          if (len > 1) /* Avoid printing the trailing LF. */
+            es_fputs ("\\n", fp);
+        }
+      else if (*p == '\r')
+        es_fputs ("\\r", fp);
+      else if (*p == '\v')
+        es_fputs ("\\v", fp);
+      else if (*p == '\t')
+        es_fputs ("\\t", fp);
+      else
+        es_putc (*p, fp);
+    }
+  xfree (result);
+  gcry_sexp_release (sexp);
+}
+
+
+
+
 void
 gpgsm_print_name2 (FILE *fp, const char *name, int translate)
 {
@@ -604,7 +667,7 @@
         fputs (_("[Error - invalid DN]"), fp);
       else 
         {
-          print_dn_parts (fp, dn, translate);          
+          print_dn_parts (fp, NULL, dn, translate);          
           for (i=0; dn[i].key; i++)
             {
               xfree (dn[i].key);
@@ -623,6 +686,55 @@
 }
 
 
+/* This is avariant of gpgsm_print_name sending it output to an estream. */
+void
+gpgsm_es_print_name (estream_t fp, const char *name)
+{
+  const unsigned char *s = (const unsigned char *)name;
+  int i;
+
+  if (!s)
+    {
+      es_fputs (_("[Error - No name]"), fp);
+    }
+  else if (*s == '<')
+    {
+      const char *s2 = strchr ( (char*)s+1, '>');
+
+      if (s2)
+        es_write_sanitized_utf8_buffer (fp, s + 1, s2 - (char*)s - 1,
+                                        NULL, NULL);
+    }
+  else if (*s == '(')
+    {
+      pretty_es_print_sexp (fp, s, gcry_sexp_canon_len (s, 0, NULL, NULL));
+    }
+  else if (!((*s >= '0' && *s < '9')
+             || (*s >= 'A' && *s <= 'Z')
+             || (*s >= 'a' && *s <= 'z')))
+    es_fputs (_("[Error - invalid encoding]"), fp);
+  else
+    {
+      struct dn_array_s *dn = parse_dn (s);
+
+      if (!dn)
+        es_fputs (_("[Error - invalid DN]"), fp);
+      else 
+        {
+          print_dn_parts (NULL, fp, dn, 1);          
+          for (i=0; dn[i].key; i++)
+            {
+              xfree (dn[i].key);
+              xfree (dn[i].value);
+            }
+          xfree (dn);
+        }
+    }
+}
+
+
+
+
 /* A cookie structure used for the memory stream. */
 struct format_name_cookie 
 {

Modified: trunk/sm/gpgsm.c
===================================================================
--- trunk/sm/gpgsm.c	2007-03-14 13:26:18 UTC (rev 4462)
+++ trunk/sm/gpgsm.c	2007-03-19 14:35:04 UTC (rev 4463)
@@ -481,6 +481,7 @@
 static int check_special_filename (const char *fname);
 static int open_read (const char *filename);
 static FILE *open_fwrite (const char *filename);
+static estream_t open_es_fwrite (const char *filename);
 static void run_protect_tool (int argc, char **argv);
 
 
@@ -1570,7 +1571,7 @@
     case aDumpSecretKeys:
       {
         unsigned int mode;
-        FILE *fp;
+        estream_t fp;
 
         switch (cmd)
           {
@@ -1585,13 +1586,12 @@
           default: BUG();
           }
 
-        fp = open_fwrite (opt.outfile?opt.outfile:"-");
+        fp = open_es_fwrite (opt.outfile?opt.outfile:"-");
         for (sl=NULL; argc; argc--, argv++)
           add_to_strlist (&sl, *argv);
         gpgsm_list_keys (&ctrl, sl, fp, mode);
         free_strlist(sl);
-        if (fp != stdout)
-          fclose (fp);
+        es_fclose (fp);
       }
       break;
 
@@ -1816,6 +1816,44 @@
 }
 
 
+/* Open FILENAME for fwrite and return an extended stream.  Stop with
+   an error message in case of problems.  "-" denotes stdout and if
+   special filenames are allowed the given fd is opened instead.
+   Caller must close the returned stream. */
+static estream_t
+open_es_fwrite (const char *filename)
+{
+  int fd;
+  estream_t fp;
+
+  if (filename[0] == '-' && !filename[1])
+    {
+      fflush (stdout);
+      fp = es_fdopen (dup (fileno(stdout)), "wb");
+      return fp;
+    }
+
+  fd = check_special_filename (filename);
+  if (fd != -1)
+    {
+      fp = es_fdopen (dup (fd), "wb");
+      if (!fp)
+        {
+          log_error ("es_fdopen(%d) failed: %s\n", fd, strerror (errno));
+          gpgsm_exit (2);
+        }
+      return fp;
+    }
+  fp = es_fopen (filename, "wb");
+  if (!fp)
+    {
+      log_error (_("can't open `%s': %s\n"), filename, strerror (errno));
+      gpgsm_exit (2);
+    }
+  return fp;
+}
+
+
 static void
 run_protect_tool (int argc, char **argv)
 {

Modified: trunk/sm/gpgsm.h
===================================================================
--- trunk/sm/gpgsm.h	2007-03-14 13:26:18 UTC (rev 4462)
+++ trunk/sm/gpgsm.h	2007-03-19 14:35:04 UTC (rev 4463)
@@ -32,6 +32,7 @@
 #include <ksba.h>
 #include "../common/util.h"
 #include "../common/errors.h"
+#include "../common/estream.h"
 
 #define MAX_DIGEST_LEN 24 
 
@@ -225,10 +226,11 @@
 
 
 /*-- certdump.c --*/
-void gpgsm_print_serial (FILE *fp, ksba_const_sexp_t p);
-void gpgsm_print_time (FILE *fp, ksba_isotime_t t);
+void gpgsm_print_serial (estream_t fp, ksba_const_sexp_t p);
+void gpgsm_print_time (estream_t fp, ksba_isotime_t t);
 void gpgsm_print_name2 (FILE *fp, const char *string, int translate);
 void gpgsm_print_name (FILE *fp, const char *string);
+void gpgsm_es_print_name (estream_t fp, const char *string);
 
 void gpgsm_cert_log_name (const char *text, ksba_cert_t cert);
 
@@ -261,7 +263,7 @@
 int gpgsm_is_root_cert (ksba_cert_t cert);
 int gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert,
                           ksba_isotime_t r_exptime,
-                          int listmode, FILE *listfp,
+                          int listmode, estream_t listfp,
                           unsigned int flags);
 int gpgsm_basic_cert_check (ksba_cert_t cert);
 
@@ -281,7 +283,7 @@
 
 /*-- keylist.c --*/
 gpg_error_t gpgsm_list_keys (ctrl_t ctrl, strlist_t names,
-                             FILE *fp, unsigned int mode);
+                             estream_t fp, unsigned int mode);
 
 /*-- import.c --*/
 int gpgsm_import (ctrl_t ctrl, int in_fd);

Modified: trunk/sm/keylist.c
===================================================================
--- trunk/sm/keylist.c	2007-03-14 13:26:18 UTC (rev 4462)
+++ trunk/sm/keylist.c	2007-03-19 14:35:04 UTC (rev 4463)
@@ -38,9 +38,10 @@
 #include "../kbx/keybox.h" /* for KEYBOX_FLAG_* */
 #include "i18n.h"
 
-struct list_external_parm_s {
+struct list_external_parm_s 
+{
   ctrl_t ctrl;
-  FILE *fp;
+  estream_t fp;
   int print_header;
   int with_colons;
   int with_chain;
@@ -50,7 +51,8 @@
 
 /* This table is to map Extended Key Usage OIDs to human readable
    names.  */
-struct {
+struct
+{
   const char *oid;
   const char *name;
 } key_purpose_map[] = {
@@ -78,7 +80,8 @@
 
 
 /* A table mapping OIDs to a descriptive string. */
-static struct {
+static struct 
+{
   char *oid;
   char *name;
   unsigned int flag;
@@ -201,7 +204,7 @@
 
 
 static void
-print_key_data (ksba_cert_t cert, FILE *fp)
+print_key_data (ksba_cert_t cert, estream_t fp)
 {
 #if 0  
   int n = pk ? pubkey_get_npkey( pk->pubkey_algo ) : 0;
@@ -209,7 +212,7 @@
 
   for(i=0; i < n; i++ ) 
     {
-      fprintf (fp, "pkd:%d:%u:", i, mpi_get_nbits( pk->pkey[i] ) );
+      es_fprintf (fp, "pkd:%d:%u:", i, mpi_get_nbits( pk->pkey[i] ) );
       mpi_print(stdout, pk->pkey[i], 1 );
       putchar(':');
       putchar('\n');
@@ -218,7 +221,7 @@
 }
 
 static void
-print_capabilities (ksba_cert_t cert, FILE *fp)
+print_capabilities (ksba_cert_t cert, estream_t fp)
 {
   gpg_error_t err;
   unsigned int use;
@@ -230,7 +233,7 @@
   if (!err && buflen)
     {
       if (*buffer)
-        putc ('q', fp);
+        es_putc ('q', fp);
     }    
   else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
     ; /* Don't know - will not get marked as 'q' */
@@ -242,12 +245,12 @@
   if (gpg_err_code (err) == GPG_ERR_NO_DATA
       || gpg_err_code (err) == GPG_ERR_NO_VALUE)
     {
-      putc ('e', fp);
-      putc ('s', fp);
-      putc ('c', fp);
-      putc ('E', fp);
-      putc ('S', fp);
-      putc ('C', fp);
+      es_putc ('e', fp);
+      es_putc ('s', fp);
+      es_putc ('c', fp);
+      es_putc ('E', fp);
+      es_putc ('S', fp);
+      es_putc ('C', fp);
       return;
     }
   if (err)
@@ -258,27 +261,27 @@
     } 
 
   if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT)))
-    putc ('e', fp);
+    es_putc ('e', fp);
   if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION)))
-    putc ('s', fp);
+    es_putc ('s', fp);
   if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN))
-    putc ('c', fp);
+    es_putc ('c', fp);
   if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT)))
-    putc ('E', fp);
+    es_putc ('E', fp);
   if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION)))
-    putc ('S', fp);
+    es_putc ('S', fp);
   if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN))
-    putc ('C', fp);
+    es_putc ('C', fp);
 }
 
 
 static void
-print_time (gnupg_isotime_t t, FILE *fp)
+print_time (gnupg_isotime_t t, estream_t fp)
 {
   if (!t || !*t)
     ;
   else 
-    fputs (t, fp);
+    es_fputs (t, fp);
 }
 
 
@@ -330,7 +333,7 @@
 /* List one certificate in colon mode */
 static void
 list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
-                 FILE *fp, int have_secret)
+                 estream_t fp, int have_secret)
 {
   int rc;
   int idx;
@@ -375,7 +378,7 @@
   }
 
 
-  fputs (have_secret? "crs:":"crt:", fp);
+  es_fputs (have_secret? "crs:":"crt:", fp);
 
   /* Note: We can't use multiple flags, like "ei", because the
      validation check does only return one error.  */
@@ -418,18 +421,18 @@
     }
   
   if (*truststring)
-    fputs (truststring, fp);
+    es_fputs (truststring, fp);
 
   algo = gpgsm_get_key_algo_info (cert, &nbits);
-  fprintf (fp, ":%u:%d:%s:", nbits, algo, fpr+24);
+  es_fprintf (fp, ":%u:%d:%s:", nbits, algo, fpr+24);
 
   /* We assume --fixed-list-mode for gpgsm */
   ksba_cert_get_validity (cert, 0, t);
   print_time (t, fp);
-  putc (':', fp);
+  es_putc (':', fp);
   ksba_cert_get_validity (cert, 1, t);
   print_time ( t, fp);
-  putc (':', fp);
+  es_putc (':', fp);
   /* Field 8, serial number: */
   if ((sexp = ksba_cert_get_serial (cert)))
     {
@@ -443,34 +446,34 @@
             len = len*10 + atoi_1 (s);
           if (*s == ':')
             for (s++; len; len--, s++)
-              fprintf (fp,"%02X", *s);
+              es_fprintf (fp,"%02X", *s);
         }
       xfree (sexp);
     }
-  putc (':', fp);
+  es_putc (':', fp);
   /* Field 9, ownertrust - not used here */
-  putc (':', fp);
+  es_putc (':', fp);
   /* field 10, old user ID - we use it here for the issuer DN */
   if ((p = ksba_cert_get_issuer (cert,0)))
     {
-      print_sanitized_string (fp, p, ':');
+      es_write_sanitized (fp, p, strlen (p), ":", NULL);
       xfree (p);
     }
-  putc (':', fp);
+  es_putc (':', fp);
   /* Field 11, signature class - not used */ 
-  putc (':', fp);
+  es_putc (':', fp);
   /* Field 12, capabilities: */ 
   print_capabilities (cert, fp);
-  putc (':', fp);
-  putc ('\n', fp);
+  es_putc (':', fp);
+  es_putc ('\n', fp);
 
   /* FPR record */
-  fprintf (fp, "fpr:::::::::%s:::", fpr);
+  es_fprintf (fp, "fpr:::::::::%s:::", fpr);
   /* Print chaining ID (field 13)*/
   if (chain_id)
-    fputs (chain_id, fp);
-  putc (':', fp);
-  putc ('\n', fp);
+    es_fputs (chain_id, fp);
+  es_putc (':', fp);
+  es_putc ('\n', fp);
   xfree (fpr); fpr = NULL; chain_id = NULL;
   xfree (chain_id_buffer); chain_id_buffer = NULL;
 
@@ -478,7 +481,7 @@
     {
       if ( (p = gpgsm_get_keygrip_hexstring (cert)))
         {
-          fprintf (fp, "grp:::::::::%s:\n", p);
+          es_fprintf (fp, "grp:::::::::%s:\n", p);
           xfree (p);
         }
       print_key_data (cert, fp);
@@ -486,11 +489,11 @@
 
   for (idx=0; (p = ksba_cert_get_subject (cert,idx)); idx++)
     {
-      fprintf (fp, "uid:%s::::::::", truststring);
-      print_sanitized_string (fp, p, ':');
-      putc (':', fp);
-      putc (':', fp);
-      putc ('\n', fp);
+      es_fprintf (fp, "uid:%s::::::::", truststring);
+      es_write_sanitized (fp, p, strlen (p), ":", NULL);
+      es_putc (':', fp);
+      es_putc (':', fp);
+      es_putc ('\n', fp);
       if (!idx)
         {
           /* It would be better to get the faked email address from
@@ -500,11 +503,11 @@
           char *pp = email_kludge (p);
           if (pp)
             {
-              fprintf (fp, "uid:%s::::::::", truststring);
-              print_sanitized_string (fp, pp, ':');
-              putc (':', fp);
-              putc (':', fp);
-              putc ('\n', fp);
+              es_fprintf (fp, "uid:%s::::::::", truststring);
+              es_write_sanitized (fp, pp, strlen (pp), ":", NULL);
+              es_putc (':', fp);
+              es_putc (':', fp);
+              es_putc ('\n', fp);
               xfree (pp);
             }
         }
@@ -514,16 +517,16 @@
 
 
 static void
-print_name_raw (FILE *fp, const char *string)
+print_name_raw (estream_t fp, const char *string)
 {
   if (!string)
-    fputs ("[error]", fp);
+    es_fputs ("[error]", fp);
   else
-    print_sanitized_string (fp, string, 0);
+    es_write_sanitized (fp, string, strlen (string), NULL, NULL);
 }
 
 static void
-print_names_raw (FILE *fp, int indent, ksba_name_t name)
+print_names_raw (estream_t fp, int indent, ksba_name_t name)
 {
   int idx;
   const char *s;
@@ -534,16 +537,16 @@
 
   if (!name)
     {
-      fputs ("none\n", fp);
+      es_fputs ("none\n", fp);
       return;
     }
   
   for (idx=0; (s = ksba_name_enum (name, idx)); idx++)
     {
       char *p = ksba_name_get_uri (name, idx);
-      printf ("%*s", idx||indent_all?indent:0, "");
-      print_sanitized_string (fp, p?p:s, 0);
-      putc ('\n', fp);
+      es_fprintf (fp, "%*s", idx||indent_all?indent:0, "");
+      es_write_sanitized (fp, p?p:s, strlen (p?p:s), NULL, NULL);
+      es_putc ('\n', fp);
       xfree (p);
     }
 }
@@ -554,7 +557,7 @@
    output sanitation.  It is mainly useful for debugging. */
 static void
 list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
-               ksba_cert_t cert, FILE *fp, int have_secret,
+               ksba_cert_t cert, estream_t fp, int have_secret,
                int with_validation)
 {
   gpg_error_t err;
@@ -571,162 +574,163 @@
   unsigned int reason;
 
   sexp = ksba_cert_get_serial (cert);
-  fputs ("Serial number: ", fp);
+  es_fputs ("Serial number: ", fp);
   gpgsm_print_serial (fp, sexp);
   ksba_free (sexp);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
 
   dn = ksba_cert_get_issuer (cert, 0);
-  fputs ("       Issuer: ", fp);
+  es_fputs ("       Issuer: ", fp);
   print_name_raw (fp, dn);
   ksba_free (dn);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
   for (idx=1; (dn = ksba_cert_get_issuer (cert, idx)); idx++)
     {
-      fputs ("          aka: ", fp);
+      es_fputs ("          aka: ", fp);
       print_name_raw (fp, dn);
       ksba_free (dn);
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
 
   dn = ksba_cert_get_subject (cert, 0);
-  fputs ("      Subject: ", fp);
+  es_fputs ("      Subject: ", fp);
   print_name_raw (fp, dn);
   ksba_free (dn);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
   for (idx=1; (dn = ksba_cert_get_subject (cert, idx)); idx++)
     {
-      fputs ("          aka: ", fp);
+      es_fputs ("          aka: ", fp);
       print_name_raw (fp, dn);
       ksba_free (dn);
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
 
   dn = gpgsm_get_fingerprint_string (cert, 0);
-  fprintf (fp, "     sha1_fpr: %s\n", dn?dn:"error");
+  es_fprintf (fp, "     sha1_fpr: %s\n", dn?dn:"error");
   xfree (dn);
 
   dn = gpgsm_get_fingerprint_string (cert, GCRY_MD_MD5);
-  fprintf (fp, "      md5_fpr: %s\n", dn?dn:"error");
+  es_fprintf (fp, "      md5_fpr: %s\n", dn?dn:"error");
   xfree (dn);
 
   dn = gpgsm_get_certid (cert);
-  fprintf (fp, "       certid: %s\n", dn?dn:"error");
+  es_fprintf (fp, "       certid: %s\n", dn?dn:"error");
   xfree (dn);
 
   dn = gpgsm_get_keygrip_hexstring (cert);
-  fprintf (fp, "      keygrip: %s\n", dn?dn:"error");
+  es_fprintf (fp, "      keygrip: %s\n", dn?dn:"error");
   xfree (dn);
 
   ksba_cert_get_validity (cert, 0, t);
-  fputs ("    notBefore: ", fp);
+  es_fputs ("    notBefore: ", fp);
   gpgsm_print_time (fp, t);
-  putc ('\n', fp);
-  fputs ("     notAfter: ", fp);
+  es_putc ('\n', fp);
+  es_fputs ("     notAfter: ", fp);
   ksba_cert_get_validity (cert, 1, t);
   gpgsm_print_time (fp, t);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
 
   oid = ksba_cert_get_digest_algo (cert);
   s = get_oid_desc (oid, NULL);
-  fprintf (fp, "     hashAlgo: %s%s%s%s\n", oid, s?" (":"",s?s:"",s?")":"");
+  es_fprintf (fp, "     hashAlgo: %s%s%s%s\n", oid, s?" (":"",s?s:"",s?")":"");
 
   {
     const char *algoname;
     unsigned int nbits;
 
     algoname = gcry_pk_algo_name (gpgsm_get_key_algo_info (cert, &nbits));
-    fprintf (fp, "      keyType: %u bit %s\n",  nbits, algoname? algoname:"?");
+    es_fprintf (fp, "      keyType: %u bit %s\n",
+                nbits, algoname? algoname:"?");
   }
 
   /* subjectKeyIdentifier */
-  fputs ("    subjKeyId: ", fp);
+  es_fputs ("    subjKeyId: ", fp);
   err = ksba_cert_get_subj_key_id (cert, NULL, &keyid);
   if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA
       || gpg_err_code (err) == GPG_ERR_NO_VALUE)
     {
       if (gpg_err_code (err) == GPG_ERR_NO_DATA
           || gpg_err_code (err) == GPG_ERR_NO_VALUE)
-        fputs ("[none]\n", fp);
+        es_fputs ("[none]\n", fp);
       else
         {
           gpgsm_print_serial (fp, keyid);
           ksba_free (keyid);
-          putc ('\n', fp);
+          es_putc ('\n', fp);
         }
     }
   else
-    fputs ("[?]\n", fp);
+    es_fputs ("[?]\n", fp);
 
 
   /* authorityKeyIdentifier */
-  fputs ("    authKeyId: ", fp);
+  es_fputs ("    authKeyId: ", fp);
   err = ksba_cert_get_auth_key_id (cert, &keyid, &name, &sexp);
   if (!err || gpg_err_code (err) == GPG_ERR_NO_DATA
       || gpg_err_code (err) == GPG_ERR_NO_VALUE)
     {
       if (gpg_err_code (err) == GPG_ERR_NO_DATA || !name
           || gpg_err_code (err) == GPG_ERR_NO_VALUE)
-        fputs ("[none]\n", fp);
+        es_fputs ("[none]\n", fp);
       else
         {
           gpgsm_print_serial (fp, sexp);
           ksba_free (sexp);
-          putc ('\n', fp);
+          es_putc ('\n', fp);
           print_names_raw (fp, -15, name);
           ksba_name_release (name);
         }
       if (keyid)
         {
-          fputs (" authKeyId.ki: ", fp);
+          es_fputs (" authKeyId.ki: ", fp);
           gpgsm_print_serial (fp, keyid);
           ksba_free (keyid);
-          putc ('\n', fp);
+          es_putc ('\n', fp);
         }
     }
   else
-    fputs ("[?]\n", fp);
+    es_fputs ("[?]\n", fp);
 
-  fputs ("     keyUsage:", fp);
+  es_fputs ("     keyUsage:", fp);
   err = ksba_cert_get_key_usage (cert, &kusage);
   if (gpg_err_code (err) != GPG_ERR_NO_DATA
       && gpg_err_code (err) != GPG_ERR_NO_VALUE)
     {
       if (err)
-        fprintf (fp, " [error: %s]", gpg_strerror (err));
+        es_fprintf (fp, " [error: %s]", gpg_strerror (err));
       else
         {
           if ( (kusage & KSBA_KEYUSAGE_DIGITAL_SIGNATURE))
-            fputs (" digitalSignature", fp);
+            es_fputs (" digitalSignature", fp);
           if ( (kusage & KSBA_KEYUSAGE_NON_REPUDIATION))  
-            fputs (" nonRepudiation", fp);
+            es_fputs (" nonRepudiation", fp);
           if ( (kusage & KSBA_KEYUSAGE_KEY_ENCIPHERMENT)) 
-            fputs (" keyEncipherment", fp);
+            es_fputs (" keyEncipherment", fp);
           if ( (kusage & KSBA_KEYUSAGE_DATA_ENCIPHERMENT))
-            fputs (" dataEncipherment", fp);
+            es_fputs (" dataEncipherment", fp);
           if ( (kusage & KSBA_KEYUSAGE_KEY_AGREEMENT))    
-            fputs (" keyAgreement", fp);
+            es_fputs (" keyAgreement", fp);
           if ( (kusage & KSBA_KEYUSAGE_KEY_CERT_SIGN))
-            fputs (" certSign", fp);
+            es_fputs (" certSign", fp);
           if ( (kusage & KSBA_KEYUSAGE_CRL_SIGN))  
-            fputs (" crlSign", fp);
+            es_fputs (" crlSign", fp);
           if ( (kusage & KSBA_KEYUSAGE_ENCIPHER_ONLY))
-            fputs (" encipherOnly", fp);
+            es_fputs (" encipherOnly", fp);
           if ( (kusage & KSBA_KEYUSAGE_DECIPHER_ONLY))  
-            fputs (" decipherOnly", fp);
+            es_fputs (" decipherOnly", fp);
         }
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
   else
-    fputs (" [none]\n", fp);
+    es_fputs (" [none]\n", fp);
 
-  fputs ("  extKeyUsage: ", fp);
+  es_fputs ("  extKeyUsage: ", fp);
   err = ksba_cert_get_ext_key_usages (cert, &string);
   if (gpg_err_code (err) != GPG_ERR_NO_DATA
       && gpg_err_code (err) != GPG_ERR_NO_VALUE)
     { 
       if (err)
-        fprintf (fp, "[error: %s]", gpg_strerror (err));
+        es_fprintf (fp, "[error: %s]", gpg_strerror (err));
       else
         {
           p = string;
@@ -736,31 +740,31 @@
               for (i=0; key_purpose_map[i].oid; i++)
                 if ( !strcmp (key_purpose_map[i].oid, p) )
                   break;
-              fputs (key_purpose_map[i].oid?key_purpose_map[i].name:p, fp);
+              es_fputs (key_purpose_map[i].oid?key_purpose_map[i].name:p, fp);
               p = pend;
               if (*p != 'C')
-                fputs (" (suggested)", fp);
+                es_fputs (" (suggested)", fp);
               if ((p = strchr (p, '\n')))
                 {
                   p++;
-                  fputs ("\n               ", fp);
+                  es_fputs ("\n               ", fp);
                 }
             }
           xfree (string);
         }
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
   else
-    fputs ("[none]\n", fp);
+    es_fputs ("[none]\n", fp);
 
 
-  fputs ("     policies: ", fp);
+  es_fputs ("     policies: ", fp);
   err = ksba_cert_get_cert_policies (cert, &string);
   if (gpg_err_code (err) != GPG_ERR_NO_DATA
       && gpg_err_code (err) != GPG_ERR_NO_VALUE)
     {
       if (err)
-        fprintf (fp, "[error: %s]", gpg_strerror (err));
+        es_fprintf (fp, "[error: %s]", gpg_strerror (err));
       else
         {
           p = string;
@@ -770,111 +774,111 @@
               for (i=0; key_purpose_map[i].oid; i++)
                 if ( !strcmp (key_purpose_map[i].oid, p) )
                   break;
-              fputs (p, fp);
+              es_fputs (p, fp);
               p = pend;
               if (*p == 'C')
-                fputs (" (critical)", fp);
+                es_fputs (" (critical)", fp);
               if ((p = strchr (p, '\n')))
                 {
                   p++;
-                  fputs ("\n               ", fp);
+                  es_fputs ("\n               ", fp);
                 }
             }
           xfree (string);
         }
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
   else
-    fputs ("[none]\n", fp);
+    es_fputs ("[none]\n", fp);
 
-  fputs ("  chainLength: ", fp);
+  es_fputs ("  chainLength: ", fp);
   err = ksba_cert_is_ca (cert, &is_ca, &chainlen);
   if (err || is_ca)
     {
       if (gpg_err_code (err) == GPG_ERR_NO_VALUE )
-        fprintf (fp, "[none]");
+        es_fprintf (fp, "[none]");
       else if (err)
-        fprintf (fp, "[error: %s]", gpg_strerror (err));
+        es_fprintf (fp, "[error: %s]", gpg_strerror (err));
       else if (chainlen == -1)
-        fputs ("unlimited", fp);
+        es_fputs ("unlimited", fp);
       else
-        fprintf (fp, "%d", chainlen);
-      putc ('\n', fp);
+        es_fprintf (fp, "%d", chainlen);
+      es_putc ('\n', fp);
     }
   else
-    fputs ("not a CA\n", fp);
+    es_fputs ("not a CA\n", fp);
 
 
   /* CRL distribution point */
   for (idx=0; !(err=ksba_cert_get_crl_dist_point (cert, idx, &name, &name2,
                                                   &reason)) ;idx++)
     {
-      fputs ("        crlDP: ", fp);
+      es_fputs ("        crlDP: ", fp);
       print_names_raw (fp, 15, name);
       if (reason)
         {
-          fputs ("               reason: ", fp);
+          es_fputs ("               reason: ", fp);
           if ( (reason & KSBA_CRLREASON_UNSPECIFIED))
-            fputs (" unused", stdout);
+            es_fputs (" unused", fp);
           if ( (reason & KSBA_CRLREASON_KEY_COMPROMISE))
-            fputs (" keyCompromise", stdout);
+            es_fputs (" keyCompromise", fp);
           if ( (reason & KSBA_CRLREASON_CA_COMPROMISE))
-            fputs (" caCompromise", stdout);
+            es_fputs (" caCompromise", fp);
           if ( (reason & KSBA_CRLREASON_AFFILIATION_CHANGED))
-            fputs (" affiliationChanged", stdout);
+            es_fputs (" affiliationChanged", fp);
           if ( (reason & KSBA_CRLREASON_SUPERSEDED))
-            fputs (" superseded", stdout);
+            es_fputs (" superseded", fp);
           if ( (reason & KSBA_CRLREASON_CESSATION_OF_OPERATION))
-            fputs (" cessationOfOperation", stdout);
+            es_fputs (" cessationOfOperation", fp);
           if ( (reason & KSBA_CRLREASON_CERTIFICATE_HOLD))
-            fputs (" certificateHold", stdout);
-          putchar ('\n');
+            es_fputs (" certificateHold", fp);
+          es_putc ('\n', fp);
         }
-      fputs ("               issuer: ", fp);
+      es_fputs ("               issuer: ", fp);
       print_names_raw (fp, 23, name2);
       ksba_name_release (name);
       ksba_name_release (name2);
     }
   if (err && gpg_err_code (err) != GPG_ERR_EOF
       && gpg_err_code (err) != GPG_ERR_NO_VALUE)
-    fputs ("        crlDP: [error]\n", fp);
+    es_fputs ("        crlDP: [error]\n", fp);
   else if (!idx)
-    fputs ("        crlDP: [none]\n", fp);
+    es_fputs ("        crlDP: [none]\n", fp);
 
 
   /* authorityInfoAccess. */
   for (idx=0; !(err=ksba_cert_get_authority_info_access (cert, idx, &string,
                                                          &name)); idx++)
     {
-      fputs ("     authInfo: ", fp);
+      es_fputs ("     authInfo: ", fp);
       s = get_oid_desc (string, NULL);
-      fprintf (fp, "%s%s%s%s\n", string, s?" (":"", s?s:"", s?")":"");
+      es_fprintf (fp, "%s%s%s%s\n", string, s?" (":"", s?s:"", s?")":"");
       print_names_raw (fp, -15, name);
       ksba_name_release (name);
       ksba_free (string);
     }
   if (err && gpg_err_code (err) != GPG_ERR_EOF
       && gpg_err_code (err) != GPG_ERR_NO_VALUE)
-    fputs ("     authInfo: [error]\n", fp);
+    es_fputs ("     authInfo: [error]\n", fp);
   else if (!idx)
-    fputs ("     authInfo: [none]\n", fp);
+    es_fputs ("     authInfo: [none]\n", fp);
 
   /* subjectInfoAccess. */
   for (idx=0; !(err=ksba_cert_get_subject_info_access (cert, idx, &string,
                                                          &name)); idx++)
     {
-      fputs ("  subjectInfo: ", fp);
+      es_fputs ("  subjectInfo: ", fp);
       s = get_oid_desc (string, NULL);
-      fprintf (fp, "%s%s%s%s\n", string, s?" (":"", s?s:"", s?")":"");
+      es_fprintf (fp, "%s%s%s%s\n", string, s?" (":"", s?s:"", s?")":"");
       print_names_raw (fp, -15, name);
       ksba_name_release (name);
       ksba_free (string);
     }
   if (err && gpg_err_code (err) != GPG_ERR_EOF
       && gpg_err_code (err) != GPG_ERR_NO_VALUE)
-    fputs ("     subjInfo: [error]\n", fp);
+    es_fputs ("     subjInfo: [error]\n", fp);
   else if (!idx)
-    fputs ("     subjInfo: [none]\n", fp);
+    es_fputs ("     subjInfo: [none]\n", fp);
 
 
   for (idx=0; !(err=ksba_cert_get_extension (cert, idx,
@@ -885,7 +889,7 @@
       s = get_oid_desc (oid, &flag);
 
       if (!(flag & 1))
-        fprintf (fp, "     %s: %s%s%s%s  [%d octets]\n",
+        es_fprintf (fp, "     %s: %s%s%s%s  [%d octets]\n",
                  i? "critExtn":"    extn",
                  oid, s?" (":"", s?s:"", s?")":"", (int)len);
     }
@@ -895,9 +899,9 @@
     {
       err = gpgsm_validate_chain (ctrl, cert, NULL, 1, fp, 0);
       if (!err)
-        fprintf (fp, "  [certificate is good]\n");
+        es_fprintf (fp, "  [certificate is good]\n");
       else
-        fprintf (fp, "  [certificate is bad: %s]\n", gpg_strerror (err));
+        es_fprintf (fp, "  [certificate is bad: %s]\n", gpg_strerror (err));
     }
 
   if (opt.with_ephemeral_keys && hd)
@@ -906,9 +910,9 @@
 
       err = keydb_get_flags (hd, KEYBOX_FLAG_BLOB, 0, &blobflags);
       if (err)
-        fprintf (fp, "  [error getting keyflags: %s]\n", gpg_strerror (err));
+        es_fprintf (fp, "  [error getting keyflags: %s]\n",gpg_strerror (err));
       else if ((blobflags & 2))
-        fprintf (fp, "  [stored as ephemeral]\n");
+        es_fprintf (fp, "  [stored as ephemeral]\n");
     }
 
 }
@@ -918,7 +922,7 @@
 
 /* List one certificate in standard mode */
 static void
-list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
+list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret,
                int with_validation)
 {
   gpg_error_t err;
@@ -931,44 +935,44 @@
   char *string, *p, *pend;
 
   sexp = ksba_cert_get_serial (cert);
-  fputs ("Serial number: ", fp);
+  es_fputs ("Serial number: ", fp);
   gpgsm_print_serial (fp, sexp);
   ksba_free (sexp);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
 
   dn = ksba_cert_get_issuer (cert, 0);
-  fputs ("       Issuer: ", fp);
-  gpgsm_print_name (fp, dn);
+  es_fputs ("       Issuer: ", fp);
+  gpgsm_es_print_name (fp, dn);
   ksba_free (dn);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
   for (idx=1; (dn = ksba_cert_get_issuer (cert, idx)); idx++)
     {
-      fputs ("          aka: ", fp);
-      gpgsm_print_name (fp, dn);
+      es_fputs ("          aka: ", fp);
+      gpgsm_es_print_name (fp, dn);
       ksba_free (dn);
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
 
   dn = ksba_cert_get_subject (cert, 0);
-  fputs ("      Subject: ", fp);
-  gpgsm_print_name (fp, dn);
+  es_fputs ("      Subject: ", fp);
+  gpgsm_es_print_name (fp, dn);
   ksba_free (dn);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
   for (idx=1; (dn = ksba_cert_get_subject (cert, idx)); idx++)
     {
-      fputs ("          aka: ", fp);
-      gpgsm_print_name (fp, dn);
+      es_fputs ("          aka: ", fp);
+      gpgsm_es_print_name (fp, dn);
       ksba_free (dn);
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
 
   ksba_cert_get_validity (cert, 0, t);
-  fputs ("     validity: ", fp);
+  es_fputs ("     validity: ", fp);
   gpgsm_print_time (fp, t);
-  fputs (" through ", fp);
+  es_fputs (" through ", fp);
   ksba_cert_get_validity (cert, 1, t);
   gpgsm_print_time (fp, t);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
 
 
   {
@@ -976,7 +980,8 @@
     unsigned int nbits;
 
     algoname = gcry_pk_algo_name (gpgsm_get_key_algo_info (cert, &nbits));
-    fprintf (fp, "     key type: %u bit %s\n", nbits, algoname? algoname:"?");
+    es_fprintf (fp, "     key type: %u bit %s\n",
+                nbits, algoname? algoname:"?");
   }
 
 
@@ -984,40 +989,40 @@
   if (gpg_err_code (err) != GPG_ERR_NO_DATA
       && gpg_err_code (err) != GPG_ERR_NO_VALUE)
     {
-      fputs ("    key usage:", fp);
+      es_fputs ("    key usage:", fp);
       if (err)
-        fprintf (fp, " [error: %s]", gpg_strerror (err));
+        es_fprintf (fp, " [error: %s]", gpg_strerror (err));
       else
         {
           if ( (kusage & KSBA_KEYUSAGE_DIGITAL_SIGNATURE))
-            fputs (" digitalSignature", fp);
+            es_fputs (" digitalSignature", fp);
           if ( (kusage & KSBA_KEYUSAGE_NON_REPUDIATION))  
-            fputs (" nonRepudiation", fp);
+            es_fputs (" nonRepudiation", fp);
           if ( (kusage & KSBA_KEYUSAGE_KEY_ENCIPHERMENT)) 
-            fputs (" keyEncipherment", fp);
+            es_fputs (" keyEncipherment", fp);
           if ( (kusage & KSBA_KEYUSAGE_DATA_ENCIPHERMENT))
-            fputs (" dataEncipherment", fp);
+            es_fputs (" dataEncipherment", fp);
           if ( (kusage & KSBA_KEYUSAGE_KEY_AGREEMENT))    
-            fputs (" keyAgreement", fp);
+            es_fputs (" keyAgreement", fp);
           if ( (kusage & KSBA_KEYUSAGE_KEY_CERT_SIGN))
-            fputs (" certSign", fp);
+            es_fputs (" certSign", fp);
           if ( (kusage & KSBA_KEYUSAGE_CRL_SIGN))  
-            fputs (" crlSign", fp);
+            es_fputs (" crlSign", fp);
           if ( (kusage & KSBA_KEYUSAGE_ENCIPHER_ONLY))
-            fputs (" encipherOnly", fp);
+            es_fputs (" encipherOnly", fp);
           if ( (kusage & KSBA_KEYUSAGE_DECIPHER_ONLY))  
-            fputs (" decipherOnly", fp);
+            es_fputs (" decipherOnly", fp);
         }
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
 
   err = ksba_cert_get_ext_key_usages (cert, &string);
   if (gpg_err_code (err) != GPG_ERR_NO_DATA
       && gpg_err_code (err) != GPG_ERR_NO_VALUE)
     { 
-      fputs ("ext key usage: ", fp);
+      es_fputs ("ext key usage: ", fp);
       if (err)
-        fprintf (fp, "[error: %s]", gpg_strerror (err));
+        es_fprintf (fp, "[error: %s]", gpg_strerror (err));
       else
         {
           p = string;
@@ -1027,28 +1032,28 @@
               for (i=0; key_purpose_map[i].oid; i++)
                 if ( !strcmp (key_purpose_map[i].oid, p) )
                   break;
-              fputs (key_purpose_map[i].oid?key_purpose_map[i].name:p, fp);
+              es_fputs (key_purpose_map[i].oid?key_purpose_map[i].name:p, fp);
               p = pend;
               if (*p != 'C')
-                fputs (" (suggested)", fp);
+                es_fputs (" (suggested)", fp);
               if ((p = strchr (p, '\n')))
                 {
                   p++;
-                  fputs (", ", fp);
+                  es_fputs (", ", fp);
                 }
             }
           xfree (string);
         }
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
 
   err = ksba_cert_get_cert_policies (cert, &string);
   if (gpg_err_code (err) != GPG_ERR_NO_DATA
       && gpg_err_code (err) != GPG_ERR_NO_VALUE)
     {
-      fputs ("     policies: ", fp);
+      es_fputs ("     policies: ", fp);
       if (err)
-        fprintf (fp, "[error: %s]", gpg_strerror (err));
+        es_fprintf (fp, "[error: %s]", gpg_strerror (err));
       else
         {
           for (p=string; *p; p++)
@@ -1056,36 +1061,36 @@
               if (*p == '\n')
                 *p = ',';
             }
-          print_sanitized_string (fp, string, 0);
+          es_write_sanitized (fp, string, strlen (string), NULL, NULL);
           xfree (string);
         }
-      putc ('\n', fp);
+      es_putc ('\n', fp);
     }
 
   err = ksba_cert_is_ca (cert, &is_ca, &chainlen);
   if (err || is_ca)
     {
-      fputs (" chain length: ", fp);
+      es_fputs (" chain length: ", fp);
       if (gpg_err_code (err) == GPG_ERR_NO_VALUE )
-        fprintf (fp, "none");
+        es_fprintf (fp, "none");
       else if (err)
-        fprintf (fp, "[error: %s]", gpg_strerror (err));
+        es_fprintf (fp, "[error: %s]", gpg_strerror (err));
       else if (chainlen == -1)
-        fputs ("unlimited", fp);
+        es_fputs ("unlimited", fp);
       else
-        fprintf (fp, "%d", chainlen);
-      putc ('\n', fp);
+        es_fprintf (fp, "%d", chainlen);
+      es_putc ('\n', fp);
     }
 
   if (opt.with_md5_fingerprint)
     {
       dn = gpgsm_get_fingerprint_string (cert, GCRY_MD_MD5);
-      fprintf (fp, "      md5 fpr: %s\n", dn?dn:"error");
+      es_fprintf (fp, "      md5 fpr: %s\n", dn?dn:"error");
       xfree (dn);
     }
 
   dn = gpgsm_get_fingerprint_string (cert, 0);
-  fprintf (fp, "  fingerprint: %s\n", dn?dn:"error");
+  es_fprintf (fp, "  fingerprint: %s\n", dn?dn:"error");
   xfree (dn);
 
 
@@ -1102,7 +1107,7 @@
       if (!tmperr && buflen)
         {
           if (*buffer)
-            fputs ("  [qualified]\n", fp);
+            es_fputs ("  [qualified]\n", fp);
         }    
       else if (gpg_err_code (tmperr) == GPG_ERR_NOT_FOUND)
         ; /* Don't know - will not get marked as 'q' */
@@ -1111,9 +1116,9 @@
                    gpg_strerror (tmperr)); 
 
       if (!err)
-        fprintf (fp, "  [certificate is good]\n");
+        es_fprintf (fp, "  [certificate is good]\n");
       else
-        fprintf (fp, "  [certificate is bad: %s]\n", gpg_strerror (err));
+        es_fprintf (fp, "  [certificate is bad: %s]\n", gpg_strerror (err));
     }
 }
 
@@ -1122,7 +1127,7 @@
 static void
 list_cert_chain (ctrl_t ctrl, KEYDB_HANDLE hd,
                  ksba_cert_t cert, int raw_mode,
-                 FILE *fp, int with_validation)
+                 estream_t fp, int with_validation)
 {
   ksba_cert_t next = NULL;
 
@@ -1134,7 +1139,7 @@
   while (!gpgsm_walk_cert_chain (cert, &next))
     {
       ksba_cert_release (cert);
-      fputs ("Certified by\n", fp);
+      es_fputs ("Certified by\n", fp);
       if (raw_mode)
         list_cert_raw (ctrl, hd, next, fp, 0, with_validation);
       else
@@ -1142,7 +1147,7 @@
       cert = next;
     }
   ksba_cert_release (cert);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
 }
 
 
@@ -1153,7 +1158,7 @@
    output mode will be used instead of the standard beautified one.
  */
 static gpg_error_t
-list_internal_keys (ctrl_t ctrl, strlist_t names, FILE *fp,
+list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t fp,
                     unsigned int mode, int raw_mode)
 {
   KEYDB_HANDLE hd;
@@ -1247,10 +1252,10 @@
           
           if (ctrl->no_server)
             {
-              fprintf (fp, "%s\n", resname );
+              es_fprintf (fp, "%s\n", resname );
               for (i=strlen(resname); i; i-- )
-                putc ('-', fp);
-              putc ('\n', fp);
+                es_putc ('-', fp);
+              es_putc ('\n', fp);
               lastresname = resname;
             }
         }
@@ -1288,7 +1293,7 @@
               else
                 list_cert_std (ctrl, cert, fp, have_secret,
                                ctrl->with_validation);
-              putc ('\n', fp);
+              es_putc ('\n', fp);
             }
         }
       ksba_cert_release (cert); 
@@ -1321,10 +1326,10 @@
       const char *resname = "[external keys]";
       int i;
 
-      fprintf (parm->fp, "%s\n", resname );
+      es_fprintf (parm->fp, "%s\n", resname );
       for (i=strlen(resname); i; i-- )
-        putchar('-');
-      putc ('\n', parm->fp);
+        es_putc('-', parm->fp);
+      es_putc ('\n', parm->fp);
       parm->print_header = 0;
     }
 
@@ -1338,7 +1343,7 @@
         list_cert_raw (parm->ctrl, NULL, cert, parm->fp, 0, 0);
       else
         list_cert_std (parm->ctrl, cert, parm->fp, 0, 0);
-      putc ('\n', parm->fp);
+      es_putc ('\n', parm->fp);
     }
 }
 
@@ -1347,7 +1352,7 @@
    make sense here because it would be unwise to list external secret
    keys */
 static gpg_error_t
-list_external_keys (ctrl_t ctrl, strlist_t names, FILE *fp, int raw_mode)
+list_external_keys (ctrl_t ctrl, strlist_t names, estream_t fp, int raw_mode)
 {
   int rc;
   struct list_external_parm_s parm;
@@ -1377,7 +1382,8 @@
     Bit 8: Do a raw format dump.
  */
 gpg_error_t
-gpgsm_list_keys (ctrl_t ctrl, strlist_t names, FILE *fp, unsigned int mode)
+gpgsm_list_keys (ctrl_t ctrl, strlist_t names, estream_t fp,
+                 unsigned int mode)
 {
   gpg_error_t err = 0;
 

Modified: trunk/sm/server.c
===================================================================
--- trunk/sm/server.c	2007-03-14 13:26:18 UTC (rev 4462)
+++ trunk/sm/server.c	2007-03-19 14:35:04 UTC (rev 4463)
@@ -51,7 +51,21 @@
 };
 
 
+/* Cookie definition for assuan data line output.  */
+static ssize_t data_line_cookie_write (void *cookie,
+                                       const void *buffer, size_t size);
+static int data_line_cookie_close (void *cookie);
+static es_cookie_io_functions_t data_line_cookie_functions =
+  {
+    NULL,
+    data_line_cookie_write,
+    NULL,
+    data_line_cookie_close
+  };
 
+
+
+
 /* Note that it is sufficient to allocate the target string D as
    long as the source string S, i.e.: strlen(s)+1; */
 static void
@@ -106,6 +120,37 @@
 }
 
 
+/* A write handler used by es_fopencookie to write assuan data
+   lines.  */
+static ssize_t
+data_line_cookie_write (void *cookie, const void *buffer, size_t size)
+{
+  assuan_context_t ctx = cookie;
+
+  if (assuan_send_data (ctx, buffer, size))
+    {
+      errno = EIO;
+      return -1;
+    }
+
+  return size;
+}
+
+static int
+data_line_cookie_close (void *cookie)
+{
+  assuan_context_t ctx = cookie;
+
+  if (assuan_send_data (ctx, NULL, 0))
+    {
+      errno = EIO;
+      return -1;
+    }
+
+  return 0;
+}
+
+
 static void 
 close_message_fd (ctrl_t ctrl)
 {
@@ -706,7 +751,7 @@
 do_listkeys (assuan_context_t ctx, char *line, int mode)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
-  FILE *fp;
+  estream_t fp;
   char *p;
   strlist_t list, sl;
   unsigned int listmode;
@@ -737,17 +782,20 @@
 
   if (ctrl->server_local->list_to_output)
     {
-      if ( assuan_get_output_fd (ctx) == -1 )
+      int outfd = assuan_get_output_fd (ctx);
+
+      if ( outfd == -1 )
         return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
-      fp = fdopen (assuan_get_output_fd (ctx), "w");
+      fp = es_fdopen ( dup (outfd), "w");
       if (!fp)
-        return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
+        return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen() failed");
     }
   else
     {
-      fp = assuan_get_data_fp (ctx);
+      fp = es_fopencookie (ctx, "w", data_line_cookie_functions);
       if (!fp)
-        return set_error (GPG_ERR_ASS_GENERAL, "no data stream");
+        return set_error (GPG_ERR_ASS_GENERAL, 
+                          "error setting up a data stream");
     }
   
   ctrl->with_colons = 1;
@@ -758,11 +806,9 @@
     listmode |= (1<<7);
   err = gpgsm_list_keys (assuan_get_pointer (ctx), list, fp, listmode);
   free_strlist (list);
+  es_fclose (fp);
   if (ctrl->server_local->list_to_output)
-    {
-      fclose (fp);
-      assuan_close_output_fd (ctx);
-    }
+    assuan_close_output_fd (ctx);
   return err;
 }
 




More information about the Gnupg-commits mailing list