Changes to let the key listing use estream to help systems without
authorWerner Koch <wk@gnupg.org>
Mon, 19 Mar 2007 14:35:04 +0000 (14:35 +0000)
committerWerner Koch <wk@gnupg.org>
Mon, 19 Mar 2007 14:35:04 +0000 (14:35 +0000)
funopen.

15 files changed:
NEWS
TODO
common/ChangeLog
common/estream.c
common/estream.h
common/miscellaneous.c
common/util.h
doc/vuln-announce-2007-multiple-message.txt
sm/ChangeLog
sm/certchain.c
sm/certdump.c
sm/gpgsm.c
sm/gpgsm.h
sm/keylist.c
sm/server.c

diff --git a/NEWS b/NEWS
index d4eca64..d20a99b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,9 @@
 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 @@ Noteworthy changes in version 2.0.3 (2007-03-08)
    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. 
 
diff --git a/TODO b/TODO
index 2e0f545..4a8a482 100644 (file)
--- a/TODO
+++ b/TODO
@@ -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.
 * 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.
 
 * 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.
+
+
index 6e2ce2b..58695ed 100644 (file)
@@ -1,3 +1,10 @@
+2007-03-19  Werner Koch  <wk@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@jabberwocky.com>
 
        From STABLE-BRANCH-1-4
index e63bc89..31e91d5 100644 (file)
@@ -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.
  *
 # 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 @@ static estream_mutex_t estream_list_lock = ESTREAM_MUTEX_INITIALIZER;
 /* 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 es_cookie_io_functions_t estream_functions_file =
 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 @@ es_vfprintf (estream_t ES__RESTRICT stream, const char *ES__RESTRICT format,
 }
 
 
+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 @@ es_opaque_get (estream_t stream)
   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*/
+
+
+
index 123a65a..aede408 100644 (file)
@@ -172,6 +172,13 @@ int es_read (estream_t ES__RESTRICT stream,
 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 @@ estream_t es_tmpfile (void);
 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*/
 
index 364f134..948c8ef 100644 (file)
@@ -74,6 +74,7 @@ make_printable_string (const void *p, size_t n, int delim )
 }
 
 
+
 /*
  * Check if the file is compressed.
  */
index f932830..324fbb2 100644 (file)
@@ -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
index dcdb482..16c08d4 100644 (file)
@@ -1,6 +1,6 @@
              Multiple Messages Problem in GnuPG and GPGME
             ==============================================
-                              2007-03-05
+                     2007-03-05  [CVE-2007-1263]
 
 
 Summary
index c4d1eec..cb154fc 100644 (file)
@@ -1,3 +1,25 @@
+2007-03-19  Werner Koch  <wk@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@g10code.com>
 
        * gpgsm.c (main): Let --gen-key print a more informative error
index d4147b3..d477c13 100644 (file)
@@ -93,7 +93,7 @@ set_already_asked_marktrusted (ksba_cert_t cert)
    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 @@ do_list (int is_error, int listmode, FILE *fp, const char *format, ...)
     {
       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 @@ compare_certs (ksba_cert_t a, ksba_cert_t b)
 
 
 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 @@ unknown_criticals (ksba_cert_t cert, int listmode, FILE *fp)
    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 @@ allowed_ca (ksba_cert_t cert, int *chainlen, int listmode, FILE *fp)
 
 
 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 @@ gpgsm_is_root_cert (ksba_cert_t cert)
 
 /* 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 @@ is_cert_still_valid (ctrl_t ctrl, int lm, FILE *fp,
 */
 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;
index 65c604e..d697733 100644 (file)
@@ -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 @@ struct dn_array_s {
 };
 
 
-/* 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 @@ gpgsm_format_serial (ksba_const_sexp_t sn)
 
 
 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 @@ parse_dn (const unsigned char *string)
 }
 
 
+/* 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 @@ print_dn_part (FILE *fp, struct dn_array_s *dn, const char *key, int translate)
         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_dn_part (FILE *fp, struct dn_array_s *dn, const char *key, int translate)
 /* 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 @@ print_dn_parts (FILE *fp, struct dn_array_s *dn, int translate)
   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,6 +583,53 @@ pretty_print_sexp (FILE *fp, const unsigned char *buf, size_t buflen)
   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 @@ gpgsm_print_name2 (FILE *fp, const char *name, int translate)
         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 @@ gpgsm_print_name (FILE *fp, const char *name)
 }
 
 
+/* 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 
 {
index 9557ffb..b71107a 100644 (file)
@@ -481,6 +481,7 @@ static void emergency_cleanup (void);
 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 @@ main ( int argc, char **argv)
     case aDumpSecretKeys:
       {
         unsigned int mode;
-        FILE *fp;
+        estream_t fp;
 
         switch (cmd)
           {
@@ -1585,13 +1586,12 @@ main ( int argc, char **argv)
           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_fwrite (const char *filename)
 }
 
 
+/* 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)
 {
index 2806530..a128374 100644 (file)
@@ -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 @@ void gpgsm_destroy_writer (Base64Context ctx);
 
 
 /*-- 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_walk_cert_chain (ksba_cert_t start, ksba_cert_t *r_next);
 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 @@ int gpgsm_find_cert (const char *name, ksba_sexp_t keyid, ksba_cert_t *r_cert);
 
 /*-- 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);
index 1c9323c..1b79695 100644 (file)
 #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 @@ struct list_external_parm_s {
 
 /* 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 @@ struct {
 
 
 /* A table mapping OIDs to a descriptive string. */
-static struct {
+static struct 
+{
   char *oid;
   char *name;
   unsigned int flag;
@@ -201,7 +204,7 @@ get_oid_desc (const char *oid, unsigned int *flag)
 
 
 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 @@ print_key_data (ksba_cert_t cert, FILE *fp)
 
   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 @@ print_key_data (ksba_cert_t cert, FILE *fp)
 }
 
 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 @@ print_capabilities (ksba_cert_t cert, FILE *fp)
   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 @@ print_capabilities (ksba_cert_t cert, FILE *fp)
   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 @@ print_capabilities (ksba_cert_t cert, FILE *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);
   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 @@ email_kludge (const char *name)
 /* 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 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
   }
 
 
-  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 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
     }
   
   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 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
             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 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
     {
       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 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
 
   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 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
           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 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity,
 
 
 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 @@ print_names_raw (FILE *fp, int indent, ksba_name_t name)
 
   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 @@ print_names_raw (FILE *fp, int indent, ksba_name_t name)
    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 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
   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 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
               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 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
               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 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
       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 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
     {
       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 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
 
       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_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd,
 
 /* 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 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
   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 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
     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 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
   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 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
               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 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
               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 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
       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 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
                    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 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, FILE *fp, int have_secret,
 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 @@ list_cert_chain (ctrl_t ctrl, KEYDB_HANDLE hd,
   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 @@ list_cert_chain (ctrl_t ctrl, KEYDB_HANDLE hd,
       cert = next;
     }
   ksba_cert_release (cert);
-  putc ('\n', fp);
+  es_putc ('\n', fp);
 }
 
 
@@ -1153,7 +1158,7 @@ list_cert_chain (ctrl_t ctrl, KEYDB_HANDLE hd,
    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 @@ list_internal_keys (ctrl_t ctrl, strlist_t names, FILE *fp,
           
           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 @@ list_internal_keys (ctrl_t ctrl, strlist_t names, FILE *fp,
               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 @@ list_external_cb (void *cb_value, ksba_cert_t cert)
       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_external_cb (void *cb_value, ksba_cert_t cert)
         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 @@ list_external_cb (void *cb_value, ksba_cert_t cert)
    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 @@ list_external_keys (ctrl_t ctrl, strlist_t names, FILE *fp, int raw_mode)
     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;
 
index f922a0f..dde097e 100644 (file)
@@ -51,7 +51,21 @@ struct server_local_s {
 };
 
 
+/* 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
+  };
+
 
+
+\f
 /* 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 @@ has_option (const char *line, const char *name)
 }
 
 
+/* 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 @@ static int
 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 @@ do_listkeys (assuan_context_t ctx, char *line, int mode)
 
   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 @@ do_listkeys (assuan_context_t ctx, char *line, int mode)
     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;
 }