[g10]
[gnupg.git] / g10 / armor.c
index 89d2672..2dde9fe 100644 (file)
@@ -1,12 +1,12 @@
 /* armor.c - Armor flter
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
- *                                             Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+ *               2007 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,
@@ -15,8 +15,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>
@@ -27,9 +26,9 @@
 #include <assert.h>
 #include <ctype.h>
 
-#include "errors.h"
+#include "gpg.h"
+#include "status.h"
 #include "iobuf.h"
-#include "memory.h"
 #include "util.h"
 #include "filter.h"
 #include "packet.h"
 #include "status.h"
 #include "i18n.h"
 
-#ifdef HAVE_DOSISH_SYSTEM
-#define LF "\r\n"
-#else
-#define LF "\n"
-#endif
-
 #define MAX_LINELEN 20000
 
 #define CRCINIT 0xB704CE
@@ -119,6 +112,53 @@ static char *tail_strings[] = {
 };
 
 
+static int armor_filter ( void *opaque, int control,
+                          iobuf_t chain, byte *buf, size_t *ret_len);
+
+
+
+\f
+/* Create a new context for armor filters.  */
+armor_filter_context_t *
+new_armor_context (void)
+{
+  armor_filter_context_t *afx;
+
+  afx = xcalloc (1, sizeof *afx);
+  afx->refcount = 1;
+
+  return afx;
+}
+
+/* Release an armor filter context.  Passing NULL is explicitly
+   allowed and a no-op.  */
+void
+release_armor_context (armor_filter_context_t *afx)
+{
+  if (!afx)
+    return;
+  assert (afx->refcount);
+  if ( --afx->refcount )
+    return;
+  xfree (afx);
+}
+
+/* Push the armor filter onto the iobuf stream IOBUF.  */
+int
+push_armor_filter (armor_filter_context_t *afx, iobuf_t iobuf)
+{
+  int rc; 
+
+  afx->refcount++;
+  rc = iobuf_push_filter (iobuf, armor_filter, afx);
+  if (rc)
+    afx->refcount--;
+  return rc;
+}
+
+
+
+
 
 static void
 initialize(void)
@@ -248,12 +288,14 @@ parse_hash_header( const char *line )
            found |= 2;
        else if( !strncmp( s, "MD5", s2-s ) )
            found |= 4;
-       else if( !strncmp( s, "SHA256", s2-s ) )
+       else if( !strncmp( s, "SHA224", s2-s ) )
            found |= 8;
-       else if( !strncmp( s, "SHA384", s2-s ) )
+       else if( !strncmp( s, "SHA256", s2-s ) )
            found |= 16;
-       else if( !strncmp( s, "SHA512", s2-s ) )
+       else if( !strncmp( s, "SHA384", s2-s ) )
            found |= 32;
+       else if( !strncmp( s, "SHA512", s2-s ) )
+           found |= 64;
        else
            return 0;
        for(; *s2 && (*s2==' ' || *s2 == '\t'); s2++ )
@@ -266,7 +308,19 @@ parse_hash_header( const char *line )
     return found;
 }
 
+/* Returns true if this is a valid armor tag as per RFC-2440bis-21. */
+static int
+is_armor_tag(const char *line)
+{
+  if(strncmp(line,"Version",7)==0
+     || strncmp(line,"Comment",7)==0
+     || strncmp(line,"MessageID",9)==0
+     || strncmp(line,"Hash",4)==0
+     || strncmp(line,"Charset",7)==0)
+    return 1;
 
+  return 0;
+}
 
 /****************
  * Check whether this is a armor line.
@@ -291,17 +345,25 @@ is_armor_header( byte *line, unsigned len )
     save_p = p;
     p += 5;
 
-    /* Some mail programs on Windows seem to add spaces to the end of
-       the line.  This becomes strict if --openpgp is set. */
-
-    if(!RFC2440)
-      while(*p==' ')
+    /* Some Windows environments seem to add whitespace to the end of
+       the line, so we strip it here.  This becomes strict if
+       --rfc2440 is set since 2440 reads "The header lines, therefore,
+       MUST start at the beginning of a line, and MUST NOT have text
+       following them on the same line."  It is unclear whether "text"
+       refers to all text or just non-whitespace text.  4880 clarified
+       this was only non-whitespace text. */
+
+    if(RFC2440)
+      {
+       if( *p == '\r' )
+         p++;
+       if( *p == '\n' )
+         p++;
+      }
+    else
+      while(*p==' ' || *p=='\r' || *p=='\n' || *p=='\t')
        p++;
 
-    if( *p == '\r' )
-       p++;
-    if( *p == '\n' )
-       p++;
     if( *p )
        return -1; /* garbage after dashes */
     save_c = *save_p; *save_p = 0;
@@ -333,21 +395,35 @@ parse_header_line( armor_filter_context_t *afx, byte *line, unsigned int len )
     int hashes=0;
     unsigned int len2;
 
-    len2 = check_trailing_ws( line, len );
+    len2 = length_sans_trailing_ws ( line, len );
     if( !len2 ) {
         afx->buffer_pos = len2;  /* (it is not the fine way to do it here) */
        return 0; /* WS only: same as empty line */
     }
-    len = len2;
-    line[len2] = 0;
+
+    /*
+      This is fussy.  The spec says that a header line is delimited
+      with a colon-space pair.  This means that a line such as
+      "Comment: " (with nothing else) is actually legal as an empty
+      string comment.  However, email and cut-and-paste being what it
+      is, that trailing space may go away.  Therefore, we accept empty
+      headers delimited with only a colon.  --rfc2440, as always,
+      makes this strict and enforces the colon-space pair. -dms
+    */
 
     p = strchr( line, ':');
-    if( !p || !p[1] ) {
+    if( !p || (RFC2440 && p[1]!=' ')
+       || (!RFC2440 && p[1]!=' ' && p[1]!='\n' && p[1]!='\r'))
+      {
        log_error(_("invalid armor header: "));
        print_string( stderr, line, len, 0 );
        putc('\n', stderr);
        return -1;
-    }
+      }
+
+    /* Chop off the whitespace we detected before */
+    len=len2;
+    line[len2]='\0';
 
     if( opt.verbose ) {
        log_info(_("armor header: "));
@@ -355,16 +431,32 @@ parse_header_line( armor_filter_context_t *afx, byte *line, unsigned int len )
        putc('\n', stderr);
     }
 
-    if( afx->in_cleartext ) {
+    if( afx->in_cleartext )
+      {
        if( (hashes=parse_hash_header( line )) )
-           afx->hashes |= hashes;
+         afx->hashes |= hashes;
        else if( strlen(line) > 15 && !memcmp( line, "NotDashEscaped:", 15 ) )
-           afx->not_dash_escaped = 1;
-       else {
+         afx->not_dash_escaped = 1;
+       else
+         {
            log_error(_("invalid clearsig header\n"));
            return -1;
-       }
-    }
+         }
+      }
+    else if(!is_armor_tag(line))
+      {
+       /* Section 6.2: "Unknown keys should be reported to the user,
+          but OpenPGP should continue to process the message."  Note
+          that in a clearsigned message this applies to the signature
+          part (i.e. "BEGIN PGP SIGNATURE") and not the signed data
+          ("BEGIN PGP SIGNED MESSAGE").  The only key allowed in the
+          signed data section is "Hash". */
+
+       log_info(_("unknown armor header: "));
+       print_string( stderr, line, len, 0 );
+       putc('\n', stderr);
+      }
+
     return 1;
 }
 
@@ -414,7 +506,7 @@ check_input( armor_filter_context_t *afx, IOBUF a )
            if( hdr_line == BEGIN_SIGNED_MSG_IDX ) {
                if( afx->in_cleartext ) {
                    log_error(_("nested clear text signatures\n"));
-                   rc = G10ERR_INVALID_ARMOR;
+                   rc = gpg_error (GPG_ERR_INV_ARMOR);
                }
                afx->in_cleartext = 1;
            }
@@ -430,9 +522,9 @@ check_input( armor_filter_context_t *afx, IOBUF a )
        } while( !maxlen );
     }
 
-    /* parse the header lines */
+    /* Parse the header lines.  */
     while(len) {
-       /* read the next line (skip all truncated lines) */
+       /* Read the next line (skip all truncated lines). */
        do {
            maxlen = MAX_LINELEN;
            afx->buffer_len = iobuf_read_line( a, &afx->buffer,
@@ -443,7 +535,7 @@ check_input( armor_filter_context_t *afx, IOBUF a )
 
        i = parse_header_line( afx, line, len );
        if( i <= 0 ) {
-           if( i )
+           if (i && RFC2440)
                rc = G10ERR_INVALID_ARMOR;
            break;
        }
@@ -464,7 +556,8 @@ check_input( armor_filter_context_t *afx, IOBUF a )
     return rc;
 }
 
-
+#define PARTIAL_CHUNK 512
+#define PARTIAL_POW   9
 
 /****************
  * Fake a literal data packet and wait for the next armor line
@@ -480,19 +573,31 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
     int lastline = 0;
     unsigned maxlen, n;
     byte *p;
+    byte tempbuf[PARTIAL_CHUNK];
+    size_t tempbuf_len=0;
 
-    len = 2;   /* reserve 2 bytes for the length header */
-    size -= 2; /* and 2 for the terminating header */
-    while( !rc && len < size ) {
+    while( !rc && size-len>=(PARTIAL_CHUNK+1)) {
        /* copy what we have in the line buffer */
        if( afx->faked == 1 )
            afx->faked++; /* skip the first (empty) line */
-       else {
-           while( len < size && afx->buffer_pos < afx->buffer_len )
-               buf[len++] = afx->buffer[afx->buffer_pos++];
-           if( len >= size )
+       else
+         {
+           /* It's full, so write this partial chunk */
+           if(tempbuf_len==PARTIAL_CHUNK)
+             {
+               buf[len++]=0xE0+PARTIAL_POW;
+               memcpy(&buf[len],tempbuf,PARTIAL_CHUNK);
+               len+=PARTIAL_CHUNK;
+               tempbuf_len=0;
                continue;
-       }
+             }
+
+           while( tempbuf_len < PARTIAL_CHUNK
+                  && afx->buffer_pos < afx->buffer_len )
+             tempbuf[tempbuf_len++] = afx->buffer[afx->buffer_pos++];
+           if( tempbuf_len==PARTIAL_CHUNK )
+             continue;
+         }
 
        /* read the next line */
        maxlen = MAX_LINELEN;
@@ -505,15 +610,64 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
        }
        if( !maxlen )
            afx->truncated++;
-       if( !afx->not_dash_escaped ) {
-           int crlf;
-           p = afx->buffer;
-           n = afx->buffer_len;
-           crlf = n > 1 && p[n-2] == '\r' && p[n-1]=='\n';
+
+       p = afx->buffer;
+       n = afx->buffer_len;
+
+       /* Armor header or dash-escaped line? */
+       if(p[0]=='-')
+         {
+           /* 2440bis-10: When reversing dash-escaping, an
+              implementation MUST strip the string "- " if it occurs
+              at the beginning of a line, and SHOULD warn on "-" and
+              any character other than a space at the beginning of a
+              line.  */
+
+           if(p[1]==' ' && !afx->not_dash_escaped)
+             {
+               /* It's a dash-escaped line, so skip over the
+                  escape. */
+               afx->buffer_pos = 2;
+             }
+           else if(p[1]=='-' && p[2]=='-' && p[3]=='-' && p[4]=='-')
+             {
+               /* Five dashes in a row mean it's probably armor
+                  header. */
+               int type = is_armor_header( p, n );
+               if( afx->not_dash_escaped && type != BEGIN_SIGNATURE )
+                 ; /* this is okay */
+               else
+                 {
+                   if( type != BEGIN_SIGNATURE )
+                     {
+                       log_info(_("unexpected armor: "));
+                       print_string( stderr, p, n, 0 );
+                       putc('\n', stderr);
+                     }
+
+                   lastline = 1;
+                   rc = -1;
+                 }
+             }
+           else if(!afx->not_dash_escaped)
+             {
+               /* Bad dash-escaping. */
+               log_info(_("invalid dash escaped line: "));
+               print_string( stderr, p, n, 0 );
+               putc('\n', stderr);
+             }
+         }
+
+       /* Now handle the end-of-line canonicalization */
+       if( !afx->not_dash_escaped )
+         {
+           int crlf = n > 1 && p[n-2] == '\r' && p[n-1]=='\n';
 
            /* PGP2 does not treat a tab as white space character */
-           afx->buffer_len = trim_trailing_chars( p, n,
-                                        afx->pgp2mode ? " \r\n" : " \t\r\n");
+           afx->buffer_len=
+             trim_trailing_chars( &p[afx->buffer_pos], n-afx->buffer_pos,
+                                  afx->pgp2mode ? " \r\n" : " \t\r\n");
+           afx->buffer_len+=afx->buffer_pos;
            /* the buffer is always allocated with enough space to append
             * the removed [CR], LF and a Nul
             * The reason for this complicated procedure is to keep at least
@@ -525,48 +679,23 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
             * faked packet could do the job).
             */
            if( crlf )
-               afx->buffer[afx->buffer_len++] = '\r';
+             afx->buffer[afx->buffer_len++] = '\r';
            afx->buffer[afx->buffer_len++] = '\n';
-           afx->buffer[afx->buffer_len] = 0;
-       }
-       p = afx->buffer;
-       n = afx->buffer_len;
-
-       if( n > 2 && *p == '-' ) {
-           /* check for dash escaped or armor header */
-           if( p[1] == ' ' && !afx->not_dash_escaped ) {
-               /* issue a warning if it is not regular encoded */
-               if( p[2] != '-' && !( n > 6 && !memcmp(p+2, "From ", 5))) {
-                   log_info(_("invalid dash escaped line: "));
-                   print_string( stderr, p, n, 0 );
-                   putc('\n', stderr);
-               }
-               afx->buffer_pos = 2; /* skip */
-           }
-           else if( n >= 15 &&  p[1] == '-' && p[2] == '-' && p[3] == '-' ) {
-               int type = is_armor_header( p, n );
-               if( afx->not_dash_escaped && type != BEGIN_SIGNATURE )
-                   ; /* this is okay */
-               else {
-                   if( type != BEGIN_SIGNATURE ) {
-                       log_info(_("unexpected armor:"));
-                       print_string( stderr, p, n, 0 );
-                       putc('\n', stderr);
-                   }
-                   lastline = 1;
-                   rc = -1;
-               }
-           }
-       }
+           afx->buffer[afx->buffer_len] = '\0';
+         }
     }
 
-    buf[0] = (len-2) >> 8;
-    buf[1] = (len-2);
     if( lastline ) { /* write last (ending) length header */
-       if( buf[0] || buf[1] ) { /* only if we have some text */
-           buf[len++] = 0;
-           buf[len++] = 0;
-       }
+        if(tempbuf_len<192)
+         buf[len++]=tempbuf_len;
+       else
+         {
+           buf[len++]=((tempbuf_len-192)/256) + 192;
+           buf[len++]=(tempbuf_len-192) % 256;
+         }
+       memcpy(&buf[len],tempbuf,tempbuf_len);
+       len+=tempbuf_len;
+
        rc = 0;
        afx->faked = 0;
        afx->in_cleartext = 0;
@@ -608,10 +737,10 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
 static int
 invalid_crc(void)
 {
-    if ( opt.ignore_crc_error )
-        return 0;
-    log_inc_errorcount();
-    return G10ERR_INVALID_ARMOR;
+  if ( opt.ignore_crc_error )
+    return 0;
+  log_inc_errorcount();
+  return gpg_error (GPG_ERR_INV_ARMOR);
 }
 
 
@@ -624,7 +753,7 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
     int checkcrc=0;
     int rc = 0;
     size_t n = 0;
-    int  idx, i;
+    int  idx, i, onlypad=0;
     u32 crc;
 
     crc = afx->crc;
@@ -668,6 +797,8 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
                    goto again;
                }
            }
+           else if(n==0)
+             onlypad=1;
 
            if( idx == 1 )
                buf[n++] = val;
@@ -675,7 +806,7 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
            break;
        }
        else if( (c = asctobin[(c2=c)]) == 255 ) {
-           log_error(_("invalid radix64 character %02x skipped\n"), c2);
+           log_error(_("invalid radix64 character %02X skipped\n"), c2);
            continue;
        }
        switch(idx) {
@@ -754,13 +885,17 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
            if( c == -1 ) {
                log_info(_("premature eof (in CRC)\n"));
                rc = invalid_crc();
-                           }
+           }
+           else if( idx == 0 ) {
+               /* No CRC at all is legal ("MAY") */
+               rc=0;
+           }
            else if( idx != 4 ) {
                log_info(_("malformed CRC\n"));
                rc = invalid_crc();
            }
            else if( mycrc != afx->crc ) {
-                log_info (_("CRC error; %06lx - %06lx\n"),
+                log_info (_("CRC error; %06lX - %06lX\n"),
                                    (ulong)afx->crc, (ulong)mycrc);
                 rc = invalid_crc();
            }
@@ -780,7 +915,7 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
                if( rc == -1 )
                    rc = 0;
                else if( rc == 2 ) {
-                   log_error(_("premature eof (in Trailer)\n"));
+                   log_error(_("premature eof (in trailer)\n"));
                    rc = G10ERR_INVALID_ARMOR;
                }
                else {
@@ -792,7 +927,7 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
        }
     }
 
-    if( !n )
+    if( !n && !onlypad )
        rc = -1;
 
     *retn = n;
@@ -802,7 +937,7 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
 /****************
  * This filter is used to handle the armor stuff
  */
-int
+static int
 armor_filter( void *opaque, int control,
             IOBUF a, byte *buf, size_t *ret_len)
 {
@@ -842,9 +977,10 @@ armor_filter( void *opaque, int control,
        *ret_len = n;
     }
     else if( control == IOBUFCTRL_UNDERFLOW ) {
-        /* We need some space for the faked packet.  The minmum required
-         * size is ~18 + length of the session marker */
-       if( size < 50 ) 
+        /* We need some space for the faked packet.  The minmum
+         * required size is the PARTIAL_CHUNK size plus a byte for the
+         * length itself */
+       if( size < PARTIAL_CHUNK+1 ) 
            BUG(); /* supplied buffer too short */
 
        if( afx->faked )
@@ -881,7 +1017,7 @@ armor_filter( void *opaque, int control,
                        afx->pgp2mode = 1;
                }
                n=0;
-                /* first a gpg control packet */
+                /* First a gpg control packet... */
                 buf[n++] = 0xff; /* new format, type 63, 1 length byte */
                 n++;   /* see below */
                 memcpy(buf+n, sesmark, sesmarklen ); n+= sesmarklen;
@@ -894,19 +1030,25 @@ armor_filter( void *opaque, int control,
                 if( hashes & 4 )
                     buf[n++] = DIGEST_ALGO_MD5;
                 if( hashes & 8 )
-                    buf[n++] = DIGEST_ALGO_SHA256;
+                    buf[n++] = DIGEST_ALGO_SHA224;
                 if( hashes & 16 )
-                    buf[n++] = DIGEST_ALGO_SHA384;
+                    buf[n++] = DIGEST_ALGO_SHA256;
                 if( hashes & 32 )
+                    buf[n++] = DIGEST_ALGO_SHA384;
+                if( hashes & 64 )
                     buf[n++] = DIGEST_ALGO_SHA512;
                 buf[1] = n - 2;
 
-               /* followed by a plaintext packet */
-               buf[n++] = 0xaf; /* old packet format, type 11, var length */
-               buf[n++] = 0;    /* set the length header */
-               buf[n++] = 6;
+               /* ...followed by an invented plaintext packet.
+                  Amusingly enough, this packet is not compliant with
+                  2440 as the initial partial length is less than 512
+                  bytes.  Of course, we'll accept it anyway ;) */
+
+               buf[n++] = 0xCB; /* new packet format, type 11 */
+               buf[n++] = 0xE1; /* 2^1 == 2 bytes */
                buf[n++] = 't';  /* canonical text mode */
                buf[n++] = 0;    /* namelength */
+               buf[n++] = 0xE2; /* 2^2 == 4 more bytes */
                memset(buf+n, 0, 4); /* timestamp */
                n += 4;
            }
@@ -925,32 +1067,39 @@ armor_filter( void *opaque, int control,
     else if( control == IOBUFCTRL_FLUSH && !afx->cancel ) {
        if( !afx->status ) { /* write the header line */
            const char *s;
+           strlist_t comment=opt.comments;
 
            if( afx->what >= DIM(head_strings) )
                log_bug("afx->what=%d", afx->what);
            iobuf_writestr(a, "-----");
            iobuf_writestr(a, head_strings[afx->what] );
-           iobuf_writestr(a, "-----" LF );
+           iobuf_writestr(a, "-----" );
+           iobuf_writestr(a,afx->eol);
            if( !opt.no_version )
+             {
                iobuf_writestr(a, "Version: GnuPG v"  VERSION " ("
-                                             PRINTABLE_OS_NAME ")" LF );
+                              PRINTABLE_OS_NAME ")" );
+               iobuf_writestr(a,afx->eol);
+             }
 
-           /* write the comment string or a default one */
-           s = opt.comment_string;
-           if( s && *s ) {
+           /* write the comment strings */
+           for(s=comment->d;comment;comment=comment->next,s=comment->d)
+             {
                iobuf_writestr(a, "Comment: " );
-               for( ; *s; s++ ) {
+               for( ; *s; s++ )
+                 {
                    if( *s == '\n' )
-                       iobuf_writestr(a, "\\n" );
+                     iobuf_writestr(a, "\\n" );
                    else if( *s == '\r' )
-                       iobuf_writestr(a, "\\r" );
+                     iobuf_writestr(a, "\\r" );
                    else if( *s == '\v' )
-                       iobuf_writestr(a, "\\v" );
+                     iobuf_writestr(a, "\\v" );
                    else
-                       iobuf_put(a, *s );
-               }
-               iobuf_writestr(a, LF );
-           }
+                     iobuf_put(a, *s );
+                 }
+
+               iobuf_writestr(a,afx->eol);
+             }
 
            if ( afx->hdrlines ) {
                 for ( s = afx->hdrlines; *s; s++ ) {
@@ -961,7 +1110,8 @@ armor_filter( void *opaque, int control,
                     iobuf_put(a, *s );
                 }
             }
-           iobuf_writestr(a, LF );
+
+           iobuf_writestr(a,afx->eol);
            afx->status++;
            afx->idx = 0;
            afx->idx2 = 0;
@@ -990,10 +1140,11 @@ armor_filter( void *opaque, int control,
                iobuf_put(a, c);
                c = bintoasc[radbuf[2]&077];
                iobuf_put(a, c);
-               if( ++idx2 >= (64/4) ) { /* pgp doesn't like 72 here */
-                   iobuf_writestr(a, LF );
+               if( ++idx2 >= (64/4) )
+                 { /* pgp doesn't like 72 here */
+                   iobuf_writestr(a,afx->eol);
                    idx2=0;
-               }
+                 }
            }
        }
        for(i=0; i < idx; i++ )
@@ -1002,10 +1153,23 @@ armor_filter( void *opaque, int control,
        afx->idx2 = idx2;
        afx->crc  = crc;
     }
-    else if( control == IOBUFCTRL_INIT ) {
+    else if( control == IOBUFCTRL_INIT )
+      {
        if( !is_initialized )
-           initialize();
-    }
+         initialize();
+
+       /* Figure out what we're using for line endings if the caller
+          didn't specify. */
+       if(afx->eol[0]==0)
+         {
+#ifdef HAVE_DOSISH_SYSTEM
+           afx->eol[0]='\r';
+           afx->eol[1]='\n';
+#else
+           afx->eol[0]='\n';
+#endif
+         }
+      }
     else if( control == IOBUFCTRL_CANCEL ) {
        afx->cancel = 1;
     }
@@ -1034,14 +1198,15 @@ armor_filter( void *opaque, int control,
                    iobuf_put(a, c);
                    iobuf_put(a, '=');
                }
-               if( ++idx2 >= (64/4) ) { /* pgp doesn't like 72 here */
-                   iobuf_writestr(a, LF );
+               if( ++idx2 >= (64/4) )
+                 { /* pgp doesn't like 72 here */
+                   iobuf_writestr(a,afx->eol);
                    idx2=0;
-               }
+                 }
            }
            /* may need a linefeed */
            if( idx2 )
-               iobuf_writestr(a, LF );
+             iobuf_writestr(a,afx->eol);
            /* write the CRC */
            iobuf_put(a, '=');
            radbuf[0] = crc >>16;
@@ -1055,13 +1220,14 @@ armor_filter( void *opaque, int control,
            iobuf_put(a, c);
            c = bintoasc[radbuf[2]&077];
            iobuf_put(a, c);
-           iobuf_writestr(a, LF );
+           iobuf_writestr(a,afx->eol);
            /* and the the trailer */
            if( afx->what >= DIM(tail_strings) )
                log_bug("afx->what=%d", afx->what);
            iobuf_writestr(a, "-----");
            iobuf_writestr(a, tail_strings[afx->what] );
-           iobuf_writestr(a, "-----" LF );
+           iobuf_writestr(a, "-----" );
+           iobuf_writestr(a,afx->eol);
        }
        else if( !afx->any_data && !afx->inp_bypass ) {
            log_error(_("no valid OpenPGP data found.\n"));
@@ -1075,8 +1241,9 @@ armor_filter( void *opaque, int control,
        if( afx->qp_detected )
            log_error(_("quoted printable character in armor - "
                        "probably a buggy MTA has been used\n") );
-       m_free( afx->buffer );
+       xfree( afx->buffer );
        afx->buffer = NULL;
+        release_armor_context (afx);
     }
     else if( control == IOBUFCTRL_DESC )
        *(char**)buf = "armor_filter";
@@ -1092,7 +1259,7 @@ make_radix64_string( const byte *data, size_t len )
 {
     char *buffer, *p;
 
-    buffer = p = m_alloc( (len+2)/3*4 + 1 );
+    buffer = p = xmalloc( (len+2)/3*4 + 1 );
     for( ; len >= 3 ; len -= 3, data += 3 ) {
        *p++ = bintoasc[(data[0] >> 2) & 077];
        *p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
@@ -1152,14 +1319,14 @@ unarmor_pump_new (void)
 
     if( !is_initialized )
         initialize();
-    x = m_alloc_clear (sizeof *x);
+    x = xmalloc_clear (sizeof *x);
     return x;
 }
 
 void
 unarmor_pump_release (UnarmorPump x)
 {
-    m_free (x);
+    xfree (x);
 }
 
 /* 
@@ -1249,7 +1416,7 @@ unarmor_pump (UnarmorPump x, int c)
         {
             int c2;
             if( (c = asctobin[(c2=c)]) == 255 ) {
-                log_error(_("invalid radix64 character %02x skipped\n"), c2);
+                log_error(_("invalid radix64 character %02X skipped\n"), c2);
                 break;
             }
         }
@@ -1286,7 +1453,7 @@ unarmor_pump (UnarmorPump x, int c)
         if( (c = asctobin[c]) == 255 ) {
             rval = -1; /* ready */
             if( x->crc != x->mycrc ) {
-                log_info (_("CRC error; %06lx - %06lx\n"),
+                log_info (_("CRC error; %06lX - %06lX\n"),
                           (ulong)x->crc, (ulong)x->mycrc);
                 if ( invalid_crc() )
                     rval = -3;