Add open card manufacturer 0x0008.
[gnupg.git] / sm / base64.c
index dca0fd1..4a67d61 100644 (file)
@@ -1,11 +1,11 @@
-/* base64.c 
- *     Copyright (C) 2001 Free Software Foundation, Inc.
+/* base64.c
+ * Copyright (C) 2001, 2003, 2010 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -14,8 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <unistd.h> 
+#include <unistd.h>
 #include <time.h>
 #include <assert.h>
 
+#include "gpgsm.h"
+
+
 #include <ksba.h>
 
-#include "gpgsm.h"
 #include "i18n.h"
 
 #ifdef HAVE_DOSISH_SYSTEM
   #define LF "\n"
 #endif
 
-/* data used by the reader callbacks */
-struct reader_cb_parm_s {
-  FILE *fp;
+/* Data used by the reader callbacks.  */
+struct reader_cb_parm_s
+{
+  estream_t fp;
+
   unsigned char line[1024];
   int linelen;
   int readpos;
   int have_lf;
   unsigned long line_counter;
 
-  int autodetect; /* try to detect the input encoding */
-  int assume_pem; /* assume input encoding is PEM */
-  int assume_base64; /* assume inpout is base64 encoded */
+  int allow_multi_pem;  /* Allow processing of multiple PEM objects. */
+  int autodetect;       /* Try to detect the input encoding. */
+  int assume_pem;       /* Assume input encoding is PEM. */
+  int assume_base64;    /* Assume input is base64 encoded. */
 
   int identified;
   int is_pem;
+  int is_base64;
   int stop_seen;
+  int might_be_smime;
+
+  int eof_seen;
 
   struct {
     int idx;
@@ -62,11 +70,14 @@ struct reader_cb_parm_s {
   } base64;
 };
 
-/* data used by the writer callbacks */
-struct writer_cb_parm_s {
-  FILE *fp;
+
+/* Data used by the writer callbacks.  */
+struct writer_cb_parm_s
+{
+  estream_t stream;    /* Output stream.  */
+
   const char *pem_name;
-  
+
   int wrote_begin;
   int did_finish;
 
@@ -85,41 +96,71 @@ struct base64_context_s {
     struct reader_cb_parm_s rparm;
     struct writer_cb_parm_s wparm;
   } u;
+
+  union {
+    ksba_reader_t reader;
+    ksba_writer_t writer;
+  } u2;
 };
 
 
 /* The base-64 character list */
-static unsigned char bintoasc[64] = 
-       "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 
-       "abcdefghijklmnopqrstuvwxyz" 
-       "0123456789+/"; 
+static char bintoasc[64] =
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+       "abcdefghijklmnopqrstuvwxyz"
+       "0123456789+/";
 /* The reverse base-64 list */
 static unsigned char asctobin[256] = {
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, 
-  0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, 
-  0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
-  0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 
-  0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 
-  0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 
-  0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 
-  0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
-  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+  0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+  0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
+  0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24,
+  0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+  0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   0xff, 0xff, 0xff, 0xff
 };
 
 
+static int
+has_only_base64 (const unsigned char *line, int linelen)
+{
+  if (linelen < 20)
+    return 0;
+  for (; linelen; line++, linelen--)
+    {
+      if (*line == '\n' || (linelen > 1 && *line == '\r' && line[1] == '\n'))
+          break;
+      if ( !strchr (bintoasc, *line) )
+        return 0;
+    }
+  return 1;  /* yes */
+}
+
+static int
+is_empty_line (const unsigned char *line, int linelen)
+{
+  if (linelen >= 2 && *line == '\r' && line[1] == '\n')
+    return 1;
+  if (linelen >= 1 && *line == '\n')
+    return 1;
+  return 0;
+}
+
 
 static int
 base64_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
@@ -140,18 +181,19 @@ base64_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
       parm->have_lf = 0;
       for (n=0; n < DIM(parm->line);)
         {
-          c = getc (parm->fp);
+          c = es_getc (parm->fp);
           if (c == EOF)
             {
-              if (ferror (parm->fp))
+              parm->eof_seen = 1;
+              if (es_ferror (parm->fp))
                 return -1;
-              break; 
+              break;
             }
           parm->line[n++] = c;
           if (c == '\n')
             {
               parm->have_lf = 1;
-              /* FIXME: we need to skip overlong lines while detecting
+              /* Fixme: we need to skip overlong lines while detecting
                  the dashed lines */
               break;
             }
@@ -164,24 +206,65 @@ base64_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
 
   if (!parm->identified)
     {
-      if (parm->line_counter == 1 && !parm->have_lf)
+      if (!parm->autodetect)
+        {
+          if (parm->assume_pem)
+            {
+              /* wait for the header line */
+              parm->linelen = parm->readpos = 0;
+              if (!parm->have_lf
+                  || strncmp ((char*)parm->line, "-----BEGIN ", 11)
+                  || !strncmp ((char*)parm->line+11, "PGP ", 4))
+                goto next;
+              parm->is_pem = 1;
+            }
+          else if (parm->assume_base64)
+            parm->is_base64 = 1;
+        }
+      else if (parm->line_counter == 1 && !parm->have_lf)
         {
           /* first line too long - assume DER encoding */
           parm->is_pem = 0;
         }
       else if (parm->line_counter == 1 && parm->linelen && *parm->line == 0x30)
         {
-          /* the very first bytes does pretty much look like a SEQUENCE tag*/
+          /* the very first byte does pretty much look like a SEQUENCE tag*/
           parm->is_pem = 0;
         }
-      else if ( parm->have_lf && !strncmp (parm->line, "-----BEGIN ", 11)
-                && strncmp (parm->line+11, "PGP ", 4) )
+      else if ( parm->have_lf
+                && !strncmp ((char*)parm->line, "-----BEGIN ", 11)
+                && strncmp ((char *)parm->line+11, "PGP ", 4) )
         {
           /* Fixme: we must only compare if the line really starts at
              the beginning */
           parm->is_pem = 1;
           parm->linelen = parm->readpos = 0;
         }
+      else if ( parm->have_lf && parm->line_counter == 1
+                && parm->linelen >= 13
+                && !ascii_memcasecmp (parm->line, "Content-Type:", 13))
+        { /* might be a S/MIME body */
+          parm->might_be_smime = 1;
+          parm->linelen = parm->readpos = 0;
+          goto next;
+        }
+      else if (parm->might_be_smime == 1
+               && is_empty_line (parm->line, parm->linelen))
+        {
+          parm->might_be_smime = 2;
+          parm->linelen = parm->readpos = 0;
+          goto next;
+        }
+      else if (parm->might_be_smime == 2)
+        {
+          parm->might_be_smime = 0;
+          if ( !has_only_base64 (parm->line, parm->linelen))
+            {
+              parm->linelen = parm->readpos = 0;
+              goto next;
+            }
+          parm->is_pem = 1;
+        }
       else
         {
           parm->linelen = parm->readpos = 0;
@@ -191,16 +274,32 @@ base64_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
       parm->base64.stop_seen = 0;
       parm->base64.idx = 0;
     }
-  
+
 
   n = 0;
-  if (parm->is_pem)
-    {  
-      if (parm->have_lf && !strncmp (parm->line, "-----END ", 9))
-        { 
+  if (parm->is_pem || parm->is_base64)
+    {
+      if (parm->is_pem && parm->have_lf
+          && !strncmp ((char*)parm->line, "-----END ", 9))
+        {
           parm->identified = 0;
           parm->linelen = parm->readpos = 0;
-          /* let us return 0 */
+
+          /* If the caller want to read multiple PEM objects from one
+             file, we have to reset our internal state and return a
+             EOF immediately. The caller is the expected to use
+             ksba_reader_clear to clear the EOF condition and continue
+             to read.  If we don't want to do that we just return 0
+             bytes which will force the ksba_reader to skip until
+             EOF. */
+          if (parm->allow_multi_pem)
+            {
+              parm->identified = 0;
+              parm->autodetect = 0;
+              parm->assume_pem = 1;
+              parm->stop_seen = 0;
+              return -1; /* Send EOF now. */
+            }
         }
       else if (parm->stop_seen)
         { /* skip the rest of the line */
@@ -219,32 +318,32 @@ base64_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
               if (c == '=')
                 { /* pad character: stop */
                   if (idx == 1)
-                    buffer[n++] = val; 
+                    buffer[n++] = val;
                   parm->stop_seen = 1;
                   break;
                 }
-              if( (c = asctobin[(c2=c)]) == 255 ) 
+              if( (c = asctobin[(c2=c)]) == 255 )
                 {
                   log_error (_("invalid radix64 character %02x skipped\n"),
                              c2);
                   continue;
                 }
-              switch (idx) 
+              switch (idx)
                 {
-                case 0: 
+                case 0:
                   val = c << 2;
                   break;
-                case 1: 
+                case 1:
                   val |= (c>>4)&3;
                   buffer[n++] = val;
                   val = (c<<4)&0xf0;
                   break;
-                case 2: 
+                case 2:
                   val |= (c>>2)&15;
                   buffer[n++] = val;
                   val = (c<<6)&0xc0;
                   break;
-                case 3: 
+                case 3:
                   val |= c&0x3f;
                   buffer[n++] = val;
                   break;
@@ -285,13 +384,14 @@ simple_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
 
   for (n=0; n < count; n++)
     {
-      c = getc (parm->fp);
+      c = es_getc (parm->fp);
       if (c == EOF)
         {
-          if ( ferror (parm->fp) )
+          parm->eof_seen = 1;
+          if (es_ferror (parm->fp))
             return -1;
           if (n)
-            break; /* return what we have before an EOF */
+            break; /* Return what we have before an EOF.  */
           return -1;
         }
       *(byte *)buffer++ = c;
@@ -311,7 +411,7 @@ base64_writer_cb (void *cb_value, const void *buffer, size_t count)
   unsigned char radbuf[4];
   int i, c, idx, quad_count;
   const unsigned char *p;
-  FILE *fp = parm->fp;
+  estream_t stream = parm->stream;
 
   if (!count)
     return 0;
@@ -320,9 +420,9 @@ base64_writer_cb (void *cb_value, const void *buffer, size_t count)
     {
       if (parm->pem_name)
         {
-          fputs ("-----BEGIN ", fp);
-          fputs (parm->pem_name, fp);
-          fputs ("-----\n", fp);
+          es_fputs ("-----BEGIN ", stream);
+          es_fputs (parm->pem_name, stream);
+          es_fputs ("-----\n", stream);
         }
       parm->wrote_begin = 1;
       parm->base64.idx = 0;
@@ -341,16 +441,16 @@ base64_writer_cb (void *cb_value, const void *buffer, size_t count)
         {
           idx = 0;
           c = bintoasc[(*radbuf >> 2) & 077];
-          putc (c, fp);
+          es_putc (c, stream);
           c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
-          putc (c, fp);
+          es_putc (c, stream);
           c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
-          putc (c, fp);
+          es_putc (c, stream);
           c = bintoasc[radbuf[2]&077];
-          putc (c, fp);
-          if (++quad_count >= (64/4)) 
+          es_putc (c, stream);
+          if (++quad_count >= (64/4))
             {
-              fputs (LF, fp);
+              es_fputs (LF, stream);
               quad_count = 0;
             }
         }
@@ -360,62 +460,80 @@ base64_writer_cb (void *cb_value, const void *buffer, size_t count)
   parm->base64.idx = idx;
   parm->base64.quad_count = quad_count;
 
-  return ferror (fp) ? KSBA_Write_Error:0;
+  return es_ferror (stream)? gpg_error_from_syserror () : 0;
 }
 
+
+/* This callback is only used in stream mode.  Hiowever, we don't
+   restrict it to this.  */
+static int
+plain_writer_cb (void *cb_value, const void *buffer, size_t count)
+{
+  struct writer_cb_parm_s *parm = cb_value;
+  estream_t stream = parm->stream;
+
+  if (!count)
+    return 0;
+
+  es_write (stream, buffer, count, NULL);
+
+  return es_ferror (stream)? gpg_error_from_syserror () : 0;
+}
+
+
 static int
 base64_finish_write (struct writer_cb_parm_s *parm)
 {
-  unsigned char radbuf[4];
-  int i, c, idx, quad_count;
-  FILE *fp = parm->fp;
+  unsigned char *radbuf;
+  int c, idx, quad_count;
+  estream_t stream = parm->stream;
 
   if (!parm->wrote_begin)
-    return 0; /* nothing written */
+    return 0; /* Nothing written or we are not called in base-64 mode. */
 
   /* flush the base64 encoding */
   idx = parm->base64.idx;
   quad_count = parm->base64.quad_count;
-  for (i=0; i < idx; i++)
-    radbuf[i] = parm->base64.radbuf[i];
-
   if (idx)
     {
+      radbuf = parm->base64.radbuf;
+
       c = bintoasc[(*radbuf>>2)&077];
-      putc (c, fp);
+      es_putc (c, stream);
       if (idx == 1)
         {
           c = bintoasc[((*radbuf << 4) & 060) & 077];
-          putc (c, fp);
-          putc ('=', fp);
-          putc ('=', fp);
+          es_putc (c, stream);
+          es_putc ('=', stream);
+          es_putc ('=', stream);
         }
-      else 
-        { 
+      else
+        {
           c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
-          putc (c, fp);
+          es_putc (c, stream);
           c = bintoasc[((radbuf[1] << 2) & 074) & 077];
-          putc (c, fp);
-          putc ('=', fp);
+          es_putc (c, stream);
+          es_putc ('=', stream);
 
         }
-      if (++quad_count >= (64/4)) 
+      if (++quad_count >= (64/4))
         {
-          fputs (LF, fp);
+          es_fputs (LF, stream);
           quad_count = 0;
         }
     }
 
   if (quad_count)
-    fputs (LF, fp);
+    es_fputs (LF, stream);
 
   if (parm->pem_name)
     {
-      fputs ("-----END ", fp);
-      fputs (parm->pem_name, fp);
-      fputs ("-----\n", fp);
+      es_fputs ("-----END ", stream);
+      es_fputs (parm->pem_name, stream);
+      es_fputs ("-----\n", stream);
     }
-  return ferror (fp)? GNUPG_Write_Error : 0;
+
+  return es_ferror (stream)? gpg_error_from_syserror () : 0;
 }
 
 
@@ -426,24 +544,28 @@ base64_finish_write (struct writer_cb_parm_s *parm)
    The function returns a Base64Context object which must be passed to
    the gpgme_destroy_reader function.  The created KsbaReader object
    is also returned, but the caller must not call the
-   ksba_reader_release function on. */
+   ksba_reader_release function on.  If ALLOW_MULTI_PEM is true, the
+   reader expects that the caller uses ksba_reader_clear after EOF
+   until no more objects were found. */
 int
 gpgsm_create_reader (Base64Context *ctx,
-                     CTRL ctrl, FILE *fp, KsbaReader *r_reader)
+                     ctrl_t ctrl, estream_t fp, int allow_multi_pem,
+                     ksba_reader_t *r_reader)
 {
   int rc;
-  KsbaReader r;
+  ksba_reader_t r;
 
   *r_reader = NULL;
   *ctx = xtrycalloc (1, sizeof **ctx);
   if (!*ctx)
-    return seterr (Out_Of_Core);
+    return out_of_core ();
+  (*ctx)->u.rparm.allow_multi_pem = allow_multi_pem;
 
-  r = ksba_reader_new ();
-  if (!r)
+  rc = ksba_reader_new (&r);
+  if (rc)
     {
       xfree (*ctx); *ctx = NULL;
-      return seterr (Out_Of_Core);
+      return rc;
     }
 
   (*ctx)->u.rparm.fp = fp;
@@ -470,63 +592,82 @@ gpgsm_create_reader (Base64Context *ctx,
     {
       ksba_reader_release (r);
       xfree (*ctx); *ctx = NULL;
-      return map_ksba_err (rc);
+      return rc;
     }
 
+  (*ctx)->u2.reader = r;
   *r_reader = r;
   return 0;
 }
 
 
+int
+gpgsm_reader_eof_seen (Base64Context ctx)
+{
+  return ctx && ctx->u.rparm.eof_seen;
+}
+
 void
 gpgsm_destroy_reader (Base64Context ctx)
 {
+  if (!ctx)
+    return;
+
+  ksba_reader_release (ctx->u2.reader);
   xfree (ctx);
 }
 
 
 \f
-/* Create a writer for the given stream.  Depending on the control
-   information an output encoding is automagically choosen.  The
-   function returns a Base64Context object which must be passed to the
-   gpgme_destroy_writer function.  The created KsbaWriter object is
-   also returned, but the caller must not call the ksba_reader_release
-   function on. */
+/* Create a writer for the given STREAM.  Depending on
+   the control information an output encoding is automagically
+   choosen.  The function returns a Base64Context object which must be
+   passed to the gpgme_destroy_writer function.  The created
+   KsbaWriter object is also returned, but the caller must not call
+   the ksba_reader_release function on it. */
 int
-gpgsm_create_writer (Base64Context *ctx,
-                     CTRL ctrl, FILE *fp, KsbaWriter *r_writer)
+gpgsm_create_writer (Base64Context *ctx, ctrl_t ctrl, estream_t stream,
+                     ksba_writer_t *r_writer)
 {
   int rc;
-  KsbaWriter w;
+  ksba_writer_t w;
 
   *r_writer = NULL;
   *ctx = xtrycalloc (1, sizeof **ctx);
   if (!*ctx)
-    return seterr (Out_Of_Core);
+    return out_of_core ();
 
-  w = ksba_writer_new ();
-  if (!w)
+  rc = ksba_writer_new (&w);
+  if (rc)
     {
       xfree (*ctx); *ctx = NULL;
-      return seterr (Out_Of_Core);
+      return rc;
     }
 
   if (ctrl->create_pem || ctrl->create_base64)
     {
-      (*ctx)->u.wparm.fp = fp;
-      (*ctx)->u.wparm.pem_name = "CMS OBJECT"; /* fixme */
+      (*ctx)->u.wparm.stream = stream;
+      if (ctrl->create_pem)
+        (*ctx)->u.wparm.pem_name = ctrl->pem_name? ctrl->pem_name
+                                                 : "CMS OBJECT";
       rc = ksba_writer_set_cb (w, base64_writer_cb, &(*ctx)->u.wparm);
     }
+  else if (stream)
+    {
+      (*ctx)->u.wparm.stream = stream;
+      rc = ksba_writer_set_cb (w, plain_writer_cb, &(*ctx)->u.wparm);
+    }
   else
-    rc = ksba_writer_set_file (w, fp);
+    rc = gpg_error (GPG_ERR_INV_ARG);
 
   if (rc)
     {
       ksba_writer_release (w);
       xfree (*ctx); *ctx = NULL;
-      return map_ksba_err (rc);
+      return rc;
     }
 
+  (*ctx)->u2.writer = w;
   *r_writer = w;
   return 0;
 }
@@ -536,20 +677,24 @@ int
 gpgsm_finish_writer (Base64Context ctx)
 {
   struct writer_cb_parm_s *parm;
-  
+
   if (!ctx)
-    return GNUPG_Invalid_Value;
+    return gpg_error (GPG_ERR_INV_VALUE);
   parm = &ctx->u.wparm;
   if (parm->did_finish)
-    return 0; /* already done */
+    return 0; /* Already done. */
   parm->did_finish = 1;
-  if (!parm->fp)
-    return 0; /* callback was not used */
+  if (!parm->stream)
+    return 0; /* Callback was not used.  */
   return base64_finish_write (parm);
 }
 
 void
 gpgsm_destroy_writer (Base64Context ctx)
 {
+  if (!ctx)
+    return;
+
+  ksba_writer_release (ctx->u2.writer);
   xfree (ctx);
 }