* pcsc-wrapper.c: New.
[gnupg.git] / g10 / armor.c
index be0e69b..121ec3a 100644 (file)
@@ -1,5 +1,6 @@
 /* armor.c - Armor flter
- *     Copyright (C) 1998,1999 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
+ *                                             Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -24,7 +25,9 @@
 #include <string.h>
 #include <errno.h>
 #include <assert.h>
+#include <ctype.h>
 
+#include "gpg.h"
 #include "errors.h"
 #include "iobuf.h"
 #include "memory.h"
 #include "status.h"
 #include "i18n.h"
 
+#ifdef HAVE_DOSISH_SYSTEM
+#define LF "\r\n"
+#else
+#define LF "\n"
+#endif
 
 #define MAX_LINELEN 20000
 
@@ -145,8 +153,9 @@ initialize(void)
 }
 
 /****************
- * Check whether this is an armored file or not
- * See also parse-packet.c for details on this code
+ * Check whether this is an armored file or not See also
+ * parse-packet.c for details on this code For unknown historic
+ * reasons we use a string here but only the first byte will be used.
  * Returns: True if it seems to be armored
  */
 static int
@@ -161,6 +170,7 @@ is_armored( const byte *buf )
     switch( pkttype ) {
       case PKT_MARKER:
       case PKT_SYMKEY_ENC:
+      case PKT_ONEPASS_SIG:
       case PKT_PUBLIC_KEY:
       case PKT_SECRET_KEY:
       case PKT_PUBKEY_ENC:
@@ -183,11 +193,12 @@ is_armored( const byte *buf )
  *        filter to do further processing.
  */
 int
-use_armor_filter( IOBUF a )
+use_armor_filter( iobuf_t a )
 {
     byte buf[1];
     int n;
 
+    /* fixme: there might be a problem with iobuf_peek */
     n = iobuf_peek(a, buf, 1 );
     if( n == -1 )
        return 0; /* EOF, doesn't matter whether armored or not */
@@ -238,8 +249,12 @@ parse_hash_header( const char *line )
            found |= 2;
        else if( !strncmp( s, "MD5", s2-s ) )
            found |= 4;
-       else if( !strncmp( s, "TIGER", s2-s ) )
+       else if( !strncmp( s, "SHA256", s2-s ) )
            found |= 8;
+       else if( !strncmp( s, "SHA384", s2-s ) )
+           found |= 16;
+       else if( !strncmp( s, "SHA512", s2-s ) )
+           found |= 32;
        else
            return 0;
        for(; *s2 && (*s2==' ' || *s2 == '\t'); s2++ )
@@ -254,440 +269,6 @@ parse_hash_header( const char *line )
 
 
 
-#if 0 /* old code */
-/****************
- * parse an ascii armor.
- * Returns: the state,
- *         the remaining bytes in BUF are returned in RBUFLEN.
- *         r_empty return the # of empty lines before the buffer
- */
-static fhdr_state_t
-find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
-            IOBUF a, size_t n, unsigned *r_empty, int *r_hashes,
-            int only_keyblocks, int *not_dashed )
-{
-    int c=0, i;
-    const char *s;
-    byte *p;
-    size_t buflen;
-    int cont;
-    int clearsig=0;
-    int hdr_line=0;
-    unsigned empty = 0;
-
-    buflen = *r_buflen;
-    assert(buflen >= 100 );
-    buflen -= 4; /* reserved room for CR,LF, and two extra */
-    do {
-       switch( state ) {
-         case fhdrHASArmor:
-           /* read at least the first byte to check whether it is armored
-            * or not */
-           c = 0;
-           for(n=0; n < 28 && (c=iobuf_get(a)) != -1 && c != '\n'; )
-               buf[n++] = c;
-           if( !n && c == '\n' )
-               state = fhdrCHECKBegin;
-           else if( !n  || c == -1 )
-               state = fhdrNOArmor; /* too short */
-           else if( !is_armored( buf ) ) {
-               state = fhdrNOArmor;
-               if( c == '\n' )
-                   buf[n++] = c;
-           }
-           else if( c == '\n' )
-               state = fhdrCHECKBegin;
-           else
-               state = fhdrINITCont;
-           break;
-
-         case fhdrINIT: /* read some stuff into buffer */
-           n = 0;
-         case fhdrINITCont: /* read more stuff into buffer */
-           c = 0;
-           for(; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
-               buf[n++] = c;
-           state = c == '\n' ? fhdrCHECKBegin :
-                    c == -1  ? fhdrEOF : fhdrINITSkip;
-           break;
-
-         case fhdrINITSkip:
-           if( c == '\n' )
-               n = 0;
-           else {
-               while( (c=iobuf_get(a)) != -1 && c != '\n' )
-                   ;
-           }
-           state =  c == -1? fhdrEOF : fhdrINIT;
-           break;
-
-         case fhdrSKIPHeader:
-           while( (c=iobuf_get(a)) != -1 && c != '\n' )
-               ;
-           state =  c == -1? fhdrEOF : fhdrWAITHeader;
-           break;
-
-         case fhdrWAITHeader: /* wait for Header lines */
-           c = 0;
-           for(n=0; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
-               buf[n++] = c;
-           buf[n] = 0;
-           if( n < buflen || c == '\n' ) {
-               if( n && buf[0] != '\r') { /* maybe a header */
-                   if( strchr( buf, ':') ) { /* yes */
-                       int hashes=0;
-                       if( buf[n-1] == '\r' )
-                           buf[--n] = 0;
-                       if( opt.verbose ) {
-                           log_info(_("armor header: "));
-                           print_string( stderr, buf, n, 0 );
-                           putc('\n', stderr);
-                       }
-                       if( clearsig && !(hashes=parse_hash_header( buf )) ) {
-                           if( strlen(buf) > 15
-                               && !memcmp( buf, "NotDashEscaped:", 15 ) ) {
-                               *not_dashed = 1;
-                               state = fhdrWAITHeader;
-                           }
-                           else {
-                               log_error(_("invalid clearsig header\n"));
-                               state = fhdrERROR;
-                           }
-                       }
-                       else {
-                           state = fhdrWAITHeader;
-                           if( r_hashes )
-                               *r_hashes |= hashes;
-                       }
-                   }
-                   else if( clearsig && n > 15 && !memcmp(buf, "-----", 5 ) )
-                       state = fhdrNullClearsig;
-                   else
-                       state = fhdrCHECKDashEscaped3;
-               }
-               else if( !n || (buf[0] == '\r' && !buf[1]) ) { /* empty line */
-                   if( clearsig )
-                       state = fhdrWAITClearsig;
-                   else {
-                       /* this is not really correct: if we do not have
-                        * a clearsig and no armor lines we are not allowed
-                        * to have an empty line */
-                       n = 0;
-                       state = fhdrTEXT;
-                   }
-               }
-               else {
-                   log_error(_("invalid armor header: "));
-                   print_string( stderr, buf, n, 0 );
-                   putc('\n', stderr);
-                   state = fhdrERROR;
-               }
-           }
-           else if( c != -1 ) {
-               if( strchr( buf, ':') ) { /* buffer to short, but this is okay*/
-                   if( opt.verbose ) {
-                       log_info(_("armor header: "));
-                       print_string( stderr, buf, n, 0 );
-                       fputs("[...]\n", stderr);  /* indicate it is truncated */
-                   }
-                   state = fhdrSKIPHeader;  /* skip rest of line */
-               }
-               else /* line too long */
-                   state = fhdrERROR;
-           }
-           else
-               state = fhdrEOF;
-           break;
-
-         case fhdrWAITClearsig: /* skip the empty line (for clearsig) */
-           c = 0;
-           for(n=0; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
-               buf[n++] = c;
-           if( c != -1 ) {
-               if( n > 15 && !memcmp(buf, "-----", 5 ) )
-                   state = fhdrNullClearsig;
-               else if( c != '\n' )
-                   state = fhdrREADClearsigNext;
-               else
-                   state = fhdrCHECKDashEscaped3;
-           }
-           else {
-               /* fixme: we should check whether this line continues
-                *   it is possible that we have only read ws until here
-                *   and more stuff is to come */
-               state = fhdrEOF;
-           }
-           break;
-
-         case fhdrNullClearsig: /* zero length cleartext */
-           state = fhdrENDClearsig;
-           break;
-
-         case fhdrENDClearsig:
-         case fhdrCHECKBegin:
-           state = state == fhdrCHECKBegin ? fhdrINITSkip : fhdrERRORShow;
-           if( n < 15 )
-               break;  /* too short */
-           if( memcmp( buf, "-----", 5 ) )
-               break;
-           buf[n] = 0;
-           p = strstr(buf+5, "-----");
-           if( !p )
-               break;
-           *p = 0;
-           p += 5;
-           if( *p == '\r' )
-               p++;
-           if( *p )
-               break; /* garbage after dashes */
-           p = buf+5;
-           for(i=0; (s=head_strings[i]); i++ )
-               if( !strcmp(s, p) )
-                   break;
-           if( !s )
-               break; /* unknown begin line */
-           if( only_keyblocks && i != 1 && i != 5 && i != 6 )
-               break; /* not a keyblock armor */
-
-           /* found the begin line */
-           hdr_line = i;
-           state = fhdrWAITHeader;
-           *not_dashed = 0;
-           if( hdr_line == BEGIN_SIGNED_MSG_IDX )
-               clearsig = 1;
-           if( opt.verbose > 1 )
-               log_info(_("armor: %s\n"), head_strings[hdr_line]);
-           break;
-
-         case fhdrCLEARSIGSimple:
-           /* we are at the begin of a new line */
-         case fhdrCLEARSIGSimpleNext:
-           n = 0;
-           c = 0;
-           while( n < buflen && (c=iobuf_get(a)) != -1 ) {
-               buf[n++] = c;
-               if( c == '\n' )
-                   break;
-           }
-           buf[n] = 0;
-           if( c == -1 )
-               state = fhdrEOF;
-           else if( state == fhdrCLEARSIGSimple
-                    && n > 15 && !memcmp(buf, "-----", 5 ) ) {
-               if( c == '\n' )
-                   buf[n-1] = 0;
-               state = fhdrENDClearsig;
-           }
-           else if( c == '\n' )
-               state = fhdrCLEARSIGSimple;
-           else
-               state = fhdrCLEARSIGSimpleNext;
-           break;
-
-         case fhdrCLEARSIG:
-         case fhdrEMPTYClearsig:
-         case fhdrREADClearsig:
-           /* we are at the start of a line: read a clearsig into the buffer
-            * we have to look for a header line or dashed escaped text*/
-           n = 0;
-           c = 0;
-           while( n < buflen && (c=iobuf_get(a)) != -1 && c != '\n' )
-               buf[n++] = c;
-           buf[n] = 0;
-           if( c == -1 )
-               state = fhdrEOF;
-           else if( !n || ( buf[0]=='\r' && !buf[1] ) ) {
-               state = fhdrEMPTYClearsig;
-               empty++;
-           }
-           else if( c == '\n' )
-               state = fhdrCHECKClearsig2;
-           else
-               state = fhdrCHECKClearsig;
-           break;
-
-         case fhdrCHECKDashEscaped3:
-           if( *not_dashed ) {
-               state = fhdrTEXTSimple;
-               break;
-           }
-           if( !(n > 1 && buf[0] == '-' && buf[1] == ' ' ) ) {
-               state = fhdrTEXT;
-               break;
-           }
-           /* fall through */
-         case fhdrCHECKDashEscaped2:
-         case fhdrCHECKDashEscaped:
-           /* check dash escaped line */
-           if( buf[2] == '-' || ( n > 6 && !memcmp(buf+2, "From ", 5))) {
-               for(i=2; i < n; i++ )
-                   buf[i-2] = buf[i];
-               n -= 2;
-               buf[n] = 0; /* not really needed */
-               state = state == fhdrCHECKDashEscaped3 ? fhdrTEXT :
-                       state == fhdrCHECKDashEscaped2 ?
-                                fhdrREADClearsig : fhdrTESTSpaces;
-           }
-           else {
-               log_error(_("invalid dash escaped line: "));
-               print_string( stderr, buf, n, 0 );
-               putc('\n', stderr);
-               state = fhdrERROR;
-           }
-           break;
-
-         case fhdrCHECKClearsig:
-           /* check the clearsig line */
-           if( n > 15 && !memcmp(buf, "-----", 5 ) )
-               state = fhdrENDClearsig;
-           else if( buf[0] == '-' && buf[1] == ' ' && !*not_dashed )
-               state = fhdrCHECKDashEscaped;
-           else {
-               state = fhdrTESTSpaces;
-           }
-           break;
-
-         case fhdrCHECKClearsig2:
-           /* check the clearsig line */
-           if( n > 15 && !memcmp(buf, "-----", 5 ) )
-               state = fhdrENDClearsig;
-           else if( buf[0] == '-' && buf[1] == ' ' && !*not_dashed )
-               state = fhdrCHECKDashEscaped2;
-           else {
-               state = fhdrREADClearsig;
-           }
-           break;
-
-         case fhdrREADClearsigNext:
-           /* Read to the end of the line, do not care about checking
-            * for dashed escaped text of headers */
-           c = 0;
-           n = 0;
-           while( n < buflen && (c=iobuf_get(a)) != -1 && c != '\n' )
-               buf[n++] = c;
-           buf[n] = 0;
-           if( c == -1 )
-               state = fhdrEOF;
-           else if( c == '\n' )
-               state = fhdrREADClearsig;
-           else
-               state = fhdrTESTSpaces;
-           break;
-
-         case fhdrTESTSpaces: {
-           /* but must check whether the rest of the line
-            * only contains white spaces; this is problematic
-            * since we may have to restore the stuff.  simply
-            * counting spaces is not enough, because it may be a
-            * mix of different white space characters */
-           IOBUF b = iobuf_temp();
-           while( (c=iobuf_get(a)) != -1 && c != '\n' ) {
-               iobuf_put(b,c);
-               if( c != ' ' && c != '\t' && c != '\r' )
-                   break;
-           }
-           if( c == '\n' ) {
-               /* okay we can skip the rest of the line */
-               iobuf_close(b);
-               state = fhdrREADClearsig;
-           }
-           else {
-               iobuf_unget_and_close_temp(a,b);
-               state = fhdrREADClearsigNext;
-           }
-         } break;
-
-         case fhdrERRORShow:
-           log_error(_("invalid clear text header: "));
-           print_string( stderr, buf, n, 0 );
-           putc('\n', stderr);
-           state = fhdrERROR;
-           break;
-
-         default: BUG();
-       }
-       switch( state ) {
-         case fhdrINIT:
-         case fhdrINITCont:
-         case fhdrINITSkip:
-         case fhdrCHECKBegin:
-         case fhdrWAITHeader:
-         case fhdrWAITClearsig:
-         case fhdrSKIPHeader:
-         case fhdrEMPTYClearsig:
-         case fhdrCHECKClearsig:
-         case fhdrCHECKClearsig2:
-         case fhdrCHECKDashEscaped:
-         case fhdrCHECKDashEscaped2:
-         case fhdrCHECKDashEscaped3:
-         case fhdrTESTSpaces:
-         case fhdrERRORShow:
-           cont = 1;
-           break;
-         default: cont = 0;
-       }
-    } while( cont );
-
-    if( clearsig && state == fhdrTEXT ) {
-       state = fhdrCLEARSIG;
-    }
-    else if( clearsig && state == fhdrTEXTSimple ) {
-       state = fhdrCLEARSIGSimple;
-       buf[n] = '\n';
-       n++;
-    }
-
-    if( state == fhdrCLEARSIG || state == fhdrREADClearsig ) {
-       /* append CR,LF after removing trailing wspaces */
-       for(p=buf+n-1; n; n--, p-- ) {
-           assert( *p != '\n' );
-           if( *p != ' ' && *p != '\t' && *p != '\r' ) {
-               p[1] = '\r';
-               p[2] = '\n';
-               n += 2;
-               break;
-           }
-       }
-       if( !n ) {
-           buf[0] = '\r';
-           buf[1] = '\n';
-           n = 2;
-       }
-    }
-
-  fprintf(stderr,"ARMOR READ (state=%d): %.*s", state, n, buf );
-
-    *r_buflen = n;
-    *r_empty = empty;
-    return state;
-}
-#endif
-
-
-static unsigned
-trim_trailing_spaces( byte *line, unsigned len )
-{
-    byte *p, *mark;
-    unsigned n;
-
-    for(mark=NULL, p=line, n=0; n < len; n++, p++ ) {
-       if( strchr(" \t\r\n", *p ) ) {
-           if( !mark )
-               mark = p;
-       }
-       else
-           mark = NULL;
-    }
-
-    if( mark ) {
-       *mark = 0;
-       return mark - line;
-    }
-    return len;
-}
-
-
-
 /****************
  * Check whether this is a armor line.
  * returns: -1 if it is not a armor header or the index number of the
@@ -710,6 +291,14 @@ is_armor_header( byte *line, unsigned len )
        return -1;
     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==' ')
+       p++;
+
     if( *p == '\r' )
        p++;
     if( *p == '\n' )
@@ -739,14 +328,20 @@ is_armor_header( byte *line, unsigned len )
  *      >0: Good header line
  */
 static int
-parse_header_line( armor_filter_context_t *afx, byte *line, unsigned len )
+parse_header_line( armor_filter_context_t *afx, byte *line, unsigned int len )
 {
     byte *p;
     int hashes=0;
+    unsigned int len2;
+
+    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;
 
-    if( *line == '\n' || ( len && (*line == '\r' && line[1]=='\n') ) )
-       return 0; /* empty line */
-    len = trim_trailing_spaces( line, len );
     p = strchr( line, ':');
     if( !p || !p[1] ) {
        log_error(_("invalid armor header: "));
@@ -778,12 +373,10 @@ parse_header_line( armor_filter_context_t *afx, byte *line, unsigned len )
 
 /* figure out whether the data is armored or not */
 static int
-check_input( armor_filter_context_t *afx, IOBUF a )
+check_input( armor_filter_context_t *afx, iobuf_t a )
 {
     int rc = 0;
     int i;
-    size_t n;
-    unsigned emplines;
     byte *line;
     unsigned len;
     unsigned maxlen;
@@ -822,7 +415,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_ERR_INV_ARMOR;
                }
                afx->in_cleartext = 1;
            }
@@ -852,7 +445,7 @@ check_input( armor_filter_context_t *afx, IOBUF a )
        i = parse_header_line( afx, line, len );
        if( i <= 0 ) {
            if( i )
-               rc = G10ERR_INVALID_ARMOR;
+               rc = GPG_ERR_INV_ARMOR;
            break;
        }
     }
@@ -860,9 +453,8 @@ check_input( armor_filter_context_t *afx, IOBUF a )
 
     if( rc )
        invalid_armor();
-    else if( afx->in_cleartext ) {
+    else if( afx->in_cleartext )
        afx->faked = 1;
-    }
     else {
        afx->inp_checked = 1;
        afx->crc = CRCINIT;
@@ -881,36 +473,24 @@ check_input( armor_filter_context_t *afx, IOBUF a )
  *       not implemented/checked.
  */
 static int
-fake_packet( armor_filter_context_t *afx, IOBUF a,
+fake_packet( armor_filter_context_t *afx, iobuf_t a,
             size_t *retn, byte *buf, size_t size  )
 {
     int rc = 0;
     size_t len = 0;
-    unsigned emplines = afx->empty;
     int lastline = 0;
     unsigned maxlen, n;
     byte *p;
 
     len = 2;   /* reserve 2 bytes for the length header */
-    size -= 3; /* and 1 for empline handling and 2 for the term header */
-               /* or the appended CR,LF */
+    size -= 2; /* and 2 for the terminating header */
     while( !rc && len < size ) {
-       if( emplines ) {
-           while( emplines && len < size ) {
-               buf[len++] = '\r';
-               buf[len++] = '\n';
-               emplines--;
-           }
-           continue;
-       }
-
+       /* copy what we have in the line buffer */
        if( afx->faked == 1 )
-           afx->faked++;  /* skip the first (empty) line */
+           afx->faked++; /* skip the first (empty) line */
        else {
            while( len < size && afx->buffer_pos < afx->buffer_len )
                buf[len++] = afx->buffer[afx->buffer_pos++];
-           buf[len++] = '\r';
-           buf[len++] = '\n';
            if( len >= size )
                continue;
        }
@@ -921,12 +501,35 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
        afx->buffer_len = iobuf_read_line( a, &afx->buffer,
                                           &afx->buffer_size, &maxlen );
        if( !afx->buffer_len ) {
-           rc = -1; /* eof */
+           rc = -1; /* eof (should not happen) */
            continue;
        }
        if( !maxlen )
            afx->truncated++;
-       afx->buffer_len = trim_trailing_spaces( afx->buffer, afx->buffer_len );
+       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';
+
+           /* 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");
+           /* 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
+            * the original type of lineending - handling of the removed
+            * trailing spaces seems to be impossible in our method
+            * of faking a packet; either we have to use a temporary file
+            * or calculate the hash here in this module and somehow find
+            * a way to send the hash down the processing line (well, a special
+            * faked packet could do the job).
+            */
+           if( crlf )
+               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;
 
@@ -942,15 +545,18 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
                afx->buffer_pos = 2; /* skip */
            }
            else if( n >= 15 &&  p[1] == '-' && p[2] == '-' && p[3] == '-' ) {
-               if( is_armor_header( p, n ) != BEGIN_SIGNATURE ) {
-                   log_info(_("unexpected armor:"));
-                   print_string( stderr, p, n, 0 );
-                   putc('\n', stderr);
+               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;
                }
-               lastline = 1;
-               assert( len >= 4 );
-               len -= 2; /* remove the last CR,LF */
-               rc = -1;
            }
        }
     }
@@ -958,7 +564,7 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
     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 */
+       if( buf[0] || buf[1] ) { /* only if we have some text */
            buf[len++] = 0;
            buf[len++] = 0;
        }
@@ -995,19 +601,27 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
        afx->radbuf[0] = 0;
     }
 
-    afx->empty = emplines;
     *retn = len;
     return rc;
 }
 
 
+static int
+invalid_crc(void)
+{
+    if ( opt.ignore_crc_error )
+        return 0;
+    log_inc_errorcount();
+    return GPG_ERR_INV_ARMOR;
+}
+
 
 static int
-radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
+radix64_read( armor_filter_context_t *afx, iobuf_t a, size_t *retn,
              byte *buf, size_t size )
 {
     byte val;
-    int c, c2;
+    int c=0, c2; /*init c because gcc is not clever enough for the continue*/
     int checkcrc=0;
     int rc = 0;
     size_t n = 0;
@@ -1018,11 +632,44 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
     idx = afx->idx;
     val = afx->radbuf[0];
     for( n=0; n < size; ) {
-       if( (c=iobuf_get(a)) == -1 )
-           break;
+
+       if( afx->buffer_pos < afx->buffer_len )
+           c = afx->buffer[afx->buffer_pos++];
+       else { /* read the next line */
+           unsigned maxlen = MAX_LINELEN;
+           afx->buffer_pos = 0;
+           afx->buffer_len = iobuf_read_line( a, &afx->buffer,
+                                              &afx->buffer_size, &maxlen );
+           if( !maxlen )
+               afx->truncated++;
+           if( !afx->buffer_len )
+               break; /* eof */
+           continue;
+       }
+
+      again:
        if( c == '\n' || c == ' ' || c == '\r' || c == '\t' )
            continue;
        else if( c == '=' ) { /* pad character: stop */
+           /* some mailers leave quoted-printable encoded characters
+            * so we try to workaround this */
+           if( afx->buffer_pos+2 < afx->buffer_len ) {
+               int cc1, cc2, cc3;
+               cc1 = afx->buffer[afx->buffer_pos];
+               cc2 = afx->buffer[afx->buffer_pos+1];
+               cc3 = afx->buffer[afx->buffer_pos+2];
+               if( isxdigit(cc1) && isxdigit(cc2)
+                                 && strchr( "=\n\r\t ", cc3 )) {
+                   /* well it seems to be the case - adjust */
+                   c = isdigit(cc1)? (cc1 - '0'): (ascii_toupper(cc1)-'A'+10);
+                   c <<= 4;
+                   c |= isdigit(cc2)? (cc2 - '0'): (ascii_toupper(cc2)-'A'+10);
+                   afx->buffer_pos += 2;
+                   afx->qp_detected = 1;
+                   goto again;
+               }
+           }
+
            if( idx == 1 )
                buf[n++] = val;
            checkcrc++;
@@ -1040,19 +687,32 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
        }
        idx = (idx+1) % 4;
     }
+
     for(i=0; i < n; i++ )
        crc = (crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ buf[i]];
     crc &= 0x00ffffff;
     afx->crc = crc;
     afx->idx = idx;
     afx->radbuf[0] = val;
+
     if( checkcrc ) {
        afx->any_data = 1;
        afx->inp_checked=0;
        afx->faked = 0;
        for(;;) { /* skip lf and pad characters */
-           if( (c=iobuf_get(a)) == -1 )
-               break;
+           if( afx->buffer_pos < afx->buffer_len )
+               c = afx->buffer[afx->buffer_pos++];
+           else { /* read the next line */
+               unsigned maxlen = MAX_LINELEN;
+               afx->buffer_pos = 0;
+               afx->buffer_len = iobuf_read_line( a, &afx->buffer,
+                                                  &afx->buffer_size, &maxlen );
+               if( !maxlen )
+                   afx->truncated++;
+               if( !afx->buffer_len )
+                   break; /* eof */
+               continue;
+           }
            if( c == '\n' || c == ' ' || c == '\r'
                || c == '\t' || c == '=' )
                continue;
@@ -1072,25 +732,45 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
                  case 2: val |= (c>>2)&15; mycrc |= val << 8;val=(c<<6)&0xc0;break;
                  case 3: val |= c&0x3f; mycrc |= val; break;
                }
-               if( (c=iobuf_get(a)) == -1 )
+               for(;;) {
+                   if( afx->buffer_pos < afx->buffer_len )
+                       c = afx->buffer[afx->buffer_pos++];
+                   else { /* read the next line */
+                       unsigned maxlen = MAX_LINELEN;
+                       afx->buffer_pos = 0;
+                       afx->buffer_len = iobuf_read_line( a, &afx->buffer,
+                                                          &afx->buffer_size,
+                                                               &maxlen );
+                       if( !maxlen )
+                           afx->truncated++;
+                       if( !afx->buffer_len )
+                           break; /* eof */
+                       continue;
+                   }
                    break;
+               }
+               if( !afx->buffer_len )
+                   break; /* eof */
            } while( ++idx < 4 );
            if( c == -1 ) {
-               log_error(_("premature eof (in CRC)\n"));
-               rc = G10ERR_INVALID_ARMOR;
-           }
+               log_info(_("premature eof (in CRC)\n"));
+               rc = invalid_crc();
+                           }
            else if( idx != 4 ) {
-               log_error(_("malformed CRC\n"));
-               rc = G10ERR_INVALID_ARMOR;
+               log_info(_("malformed CRC\n"));
+               rc = invalid_crc();
            }
            else if( mycrc != afx->crc ) {
-               log_error(_("CRC error; %06lx - %06lx\n"),
+                log_info (_("CRC error; %06lx - %06lx\n"),
                                    (ulong)afx->crc, (ulong)mycrc);
-               rc = G10ERR_INVALID_ARMOR;
+                rc = invalid_crc();
            }
            else {
                rc = 0;
-             #if 0
+                /* FIXME: Here we should emit another control packet,
+                 * so that we know in mainproc that we are processing
+                 * a clearsign message */
+#if 0
                for(rc=0;!rc;) {
                    rc = 0 /*check_trailer( &fhdr, c )*/;
                    if( !rc ) {
@@ -1102,13 +782,13 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
                    rc = 0;
                else if( rc == 2 ) {
                    log_error(_("premature eof (in Trailer)\n"));
-                   rc = G10ERR_INVALID_ARMOR;
+                   rc = GPG_ERR_INV_ARMOR;
                }
                else {
                    log_error(_("error in trailer line\n"));
-                   rc = G10ERR_INVALID_ARMOR;
+                   rc = GPG_ERR_INV_ARMOR;
                }
-             #endif
+#endif
            }
        }
     }
@@ -1125,7 +805,7 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
  */
 int
 armor_filter( void *opaque, int control,
-            IOBUF a, byte *buf, size_t *ret_len)
+            iobuf_t a, byte *buf, size_t *ret_len)
 {
     size_t size = *ret_len;
     armor_filter_context_t *afx = opaque;
@@ -1134,14 +814,14 @@ armor_filter( void *opaque, int control,
     int  idx, idx2;
     size_t n=0;
     u32 crc;
-  #if 1
+#if 0
     static FILE *fp ;
 
     if( !fp ) {
        fp = fopen("armor.out", "w");
        assert(fp);
     }
-  #endif
+#endif
 
     if( DBG_FILTER )
        log_debug("armor-filter: control: %d\n", control );
@@ -1163,7 +843,9 @@ armor_filter( void *opaque, int control,
        *ret_len = n;
     }
     else if( control == IOBUFCTRL_UNDERFLOW ) {
-       if( size < 15+(4*15) )  /* need space for up to 4 onepass_sigs */
+        /* We need some space for the faked packet.  The minmum required
+         * size is ~18 + length of the session marker */
+       if( size < 50 ) 
            BUG(); /* supplied buffer too short */
 
        if( afx->faked )
@@ -1171,7 +853,7 @@ armor_filter( void *opaque, int control,
        else if( !afx->inp_checked ) {
            rc = check_input( afx, a );
            if( afx->inp_bypass ) {
-               for(n=0; n < size && afx->buffer_pos < afx->buffer_len; n++ )
+               for(n=0; n < size && afx->buffer_pos < afx->buffer_len; )
                    buf[n++] = afx->buffer[afx->buffer_pos++];
                if( afx->buffer_pos >= afx->buffer_len )
                    afx->buffer_len = 0;
@@ -1179,44 +861,46 @@ armor_filter( void *opaque, int control,
                    rc = -1;
            }
            else if( afx->faked ) {
-               unsigned hashes = afx->hashes;
+               unsigned int hashes = afx->hashes;
+                const byte *sesmark;
+                size_t sesmarklen;
+                
+                sesmark = get_session_marker( &sesmarklen );
+                if ( sesmarklen > 20 )
+                    BUG();
+
                /* the buffer is at least 15+n*15 bytes long, so it
                 * is easy to construct the packets */
 
-               hashes &= 1|2|4|8;
-               if( !hashes )
+               hashes &= 1|2|4|8|16|32|64;
+               if( !hashes ) {
                    hashes |= 4;  /* default to MD 5 */
+                   /* This is non-ideal since PGP 5-8 have the same
+                      end-of-line bugs as PGP 2. However, we only
+                      enable pgp2mode if there is no Hash: header. */
+                   if( opt.pgp2_workarounds )
+                       afx->pgp2mode = 1;
+               }
                n=0;
-               do {
-                   /* first some onepass signature packets */
-                   buf[n++] = 0x90; /* old format, type 4, 1 length byte */
-                   buf[n++] = 13;   /* length */
-                   buf[n++] = 3;    /* version */
-                   buf[n++] = 0x01; /* sigclass 0x01 (canonical text mode)*/
-                   if( hashes & 1 ) {
-                       hashes &= ~1;
-                       buf[n++] = DIGEST_ALGO_RMD160;
-                   }
-                   else if( hashes & 2 ) {
-                       hashes &= ~2;
-                       buf[n++] = DIGEST_ALGO_SHA1;
-                   }
-                   else if( hashes & 4 ) {
-                       hashes &= ~4;
-                       buf[n++] = DIGEST_ALGO_MD5;
-                   }
-                   else if( hashes & 8 ) {
-                       hashes &= ~8;
-                       buf[n++] = DIGEST_ALGO_TIGER;
-                   }
-                   else
-                       buf[n++] = 0;    /* (don't know) */
-
-                   buf[n++] = 0;    /* public key algo (don't know) */
-                   memset(buf+n, 0, 8); /* don't know the keyid */
-                   n += 8;
-                   buf[n++] = !hashes;   /* last one */
-               } while( hashes );
+                /* 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;
+                buf[n++] = CTRLPKT_CLEARSIGN_START; 
+                buf[n++] = afx->not_dash_escaped? 0:1; /* sigclass */
+                if( hashes & 1 )
+                    buf[n++] = DIGEST_ALGO_RMD160;
+                if( hashes & 2 )
+                    buf[n++] = DIGEST_ALGO_SHA1;
+                if( hashes & 4 )
+                    buf[n++] = DIGEST_ALGO_MD5;
+                if( hashes & 8 )
+                    buf[n++] = DIGEST_ALGO_SHA256;
+                if( hashes & 16 )
+                    buf[n++] = DIGEST_ALGO_SHA384;
+                if( hashes & 32 )
+                    buf[n++] = DIGEST_ALGO_SHA512;
+                buf[1] = n - 2;
 
                /* followed by a plaintext packet */
                buf[n++] = 0xaf; /* old packet format, type 11, var length */
@@ -1232,48 +916,61 @@ armor_filter( void *opaque, int control,
        }
        else
            rc = radix64_read( afx, a, &n, buf, size );
-      #if 1
+#if 0
        if( n )
            if( fwrite(buf, n, 1, fp ) != 1 )
                BUG();
-      #endif
+#endif
        *ret_len = n;
     }
-    else if( control == IOBUFCTRL_FLUSH ) {
+    else if( control == IOBUFCTRL_FLUSH && !afx->cancel ) {
        if( !afx->status ) { /* write the header line */
+           const char *s;
+            STRLIST 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, "-----\n");
-           iobuf_writestr(a, "Version: GnuPG v"  VERSION " ("
-                                           PRINTABLE_OS_NAME ")\n");
-
-           if( opt.comment_string ) {
-               const char *s = opt.comment_string;
+           iobuf_writestr(a, "-----" LF );
+           if( !opt.no_version )
+               iobuf_writestr(a, "Version: GnuPG v"  VERSION " ("
+                                             PRINTABLE_OS_NAME ")" LF );
+
+           /* Write the comment string. */
+           for(s=comment? comment->d:NULL; 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_put(a, '\n' );
-           }
-           else
-               iobuf_writestr(a,
-                   "Comment: For info see http://www.gnupg.org\n");
-           if( afx->hdrlines )
-               iobuf_writestr(a, afx->hdrlines);
-           iobuf_put(a, '\n');
+                      iobuf_put(a, *s );
+                  }
+               iobuf_writestr(a, LF );
+              }
+
+           if ( afx->hdrlines ) {
+                for ( s = afx->hdrlines; *s; s++ ) {
+#ifdef HAVE_DOSISH_SYSTEM
+                    if ( *s == '\n' )
+                        iobuf_put( a, '\r');
+#endif
+                    iobuf_put(a, *s );
+                }
+            }
+           iobuf_writestr(a, LF );
            afx->status++;
            afx->idx = 0;
            afx->idx2 = 0;
            afx->crc = CRCINIT;
+
        }
        crc = afx->crc;
        idx = afx->idx;
@@ -1298,7 +995,7 @@ armor_filter( void *opaque, int control,
                c = bintoasc[radbuf[2]&077];
                iobuf_put(a, c);
                if( ++idx2 >= (64/4) ) { /* pgp doesn't like 72 here */
-                   iobuf_put(a, '\n');
+                   iobuf_writestr(a, LF );
                    idx2=0;
                }
            }
@@ -1313,8 +1010,13 @@ armor_filter( void *opaque, int control,
        if( !is_initialized )
            initialize();
     }
+    else if( control == IOBUFCTRL_CANCEL ) {
+       afx->cancel = 1;
+    }
     else if( control == IOBUFCTRL_FREE ) {
-       if( afx->status ) { /* pad, write cecksum, and bottom line */
+       if( afx->cancel )
+           ;
+       else if( afx->status ) { /* pad, write cecksum, and bottom line */
            crc = afx->crc;
            idx = afx->idx;
            idx2 = afx->idx2;
@@ -1337,13 +1039,13 @@ armor_filter( void *opaque, int control,
                    iobuf_put(a, '=');
                }
                if( ++idx2 >= (64/4) ) { /* pgp doesn't like 72 here */
-                   iobuf_put(a, '\n');
+                   iobuf_writestr(a, LF );
                    idx2=0;
                }
            }
            /* may need a linefeed */
            if( idx2 )
-               iobuf_put(a, '\n');
+               iobuf_writestr(a, LF );
            /* write the CRC */
            iobuf_put(a, '=');
            radbuf[0] = crc >>16;
@@ -1357,17 +1059,27 @@ armor_filter( void *opaque, int control,
            iobuf_put(a, c);
            c = bintoasc[radbuf[2]&077];
            iobuf_put(a, c);
-           iobuf_put(a, '\n');
+           iobuf_writestr(a, LF );
            /* 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, "-----\n");
+           iobuf_writestr(a, "-----" LF );
        }
-       else if( !afx->any_data && !afx->inp_bypass )
+       else if( !afx->any_data && !afx->inp_bypass ) {
            log_error(_("no valid OpenPGP data found.\n"));
-       m_free( afx->buffer );
+           afx->no_openpgp_data = 1;
+           write_status_text( STATUS_NODATA, "1" );
+       }
+       if( afx->truncated )
+           log_info(_("invalid armor: line longer than %d characters\n"),
+                     MAX_LINELEN );
+       /* issue an error to enforce dissemination of correct software */
+       if( afx->qp_detected )
+           log_error(_("quoted printable character in armor - "
+                       "probably a buggy MTA has been used\n") );
+       xfree ( afx->buffer );
        afx->buffer = NULL;
     }
     else if( control == IOBUFCTRL_DESC )
@@ -1376,4 +1088,247 @@ armor_filter( void *opaque, int control,
 }
 
 
+/****************
+ * create a radix64 encoded string.
+ */
+char *
+make_radix64_string( const byte *data, size_t len )
+{
+    char *buffer, *p;
+
+    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];
+       *p++ = bintoasc[(((data[1]<<2)&074)|((data[2]>>6)&03))&077];
+       *p++ = bintoasc[data[2]&077];
+    }
+    if( len == 2 ) {
+       *p++ = bintoasc[(data[0] >> 2) & 077];
+       *p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
+       *p++ = bintoasc[((data[1]<<2)&074)];
+    }
+    else if( len == 1 ) {
+       *p++ = bintoasc[(data[0] >> 2) & 077];
+       *p++ = bintoasc[(data[0] <<4)&060];
+    }
+    *p = 0;
+    return buffer;
+}
+
+
+/***********************************************
+ *  For the pipemode command we can't use the armor filter for various
+ *  reasons, so we use this new unarmor_pump stuff to remove the armor 
+ */
+
+enum unarmor_state_e {
+    STA_init = 0,
+    STA_bypass,
+    STA_wait_newline,
+    STA_wait_dash,
+    STA_first_dash, 
+    STA_compare_header,
+    STA_found_header_wait_newline,
+    STA_skip_header_lines,
+    STA_skip_header_lines_non_ws,
+    STA_read_data,
+    STA_wait_crc,
+    STA_read_crc,
+    STA_ready
+};
+
+struct unarmor_pump_s {
+    enum unarmor_state_e state;
+    byte val;
+    int checkcrc;
+    int pos;   /* counts from 0..3 */
+    u32 crc;
+    u32 mycrc; /* the one store in the data */
+};
+
+
+
+UnarmorPump
+unarmor_pump_new (void)
+{
+    UnarmorPump x;
+
+    if( !is_initialized )
+        initialize();
+    x = xcalloc (1,sizeof *x);
+    return x;
+}
+
+void
+unarmor_pump_release (UnarmorPump x)
+{
+    xfree (x);
+}
+
+/* 
+ * Get the next character from the ascii armor taken from the IOBUF
+ * created earlier by unarmor_pump_new().
+ * Return:  c = Character
+ *        256 = ignore this value
+ *         -1 = End of current armor 
+ *         -2 = Premature EOF (not used)
+ *         -3 = Invalid armor
+ */
+int
+unarmor_pump (UnarmorPump x, int c)
+{
+    int rval = 256; /* default is to ignore the return value */
+
+    switch (x->state) {
+      case STA_init:
+        { 
+            byte tmp[1];
+            tmp[0] = c; 
+            if ( is_armored (tmp) )
+                x->state = c == '-'? STA_first_dash : STA_wait_newline;
+            else {
+                x->state = STA_bypass;
+                return c;
+            }
+        }
+        break;
+      case STA_bypass:
+        return c; /* return here to avoid crc calculation */
+      case STA_wait_newline:
+        if (c == '\n')
+            x->state = STA_wait_dash;
+        break;
+      case STA_wait_dash:
+        x->state = c == '-'? STA_first_dash : STA_wait_newline;
+        break;
+      case STA_first_dash: /* just need for initalization */
+        x->pos = 0;
+        x->state = STA_compare_header;
+      case STA_compare_header:
+        if ( "-----BEGIN PGP SIGNATURE-----"[++x->pos] == c ) {
+            if ( x->pos == 28 ) 
+                x->state = STA_found_header_wait_newline;
+        }
+        else 
+            x->state = c == '\n'? STA_wait_dash : STA_wait_newline;
+        break;
+      case STA_found_header_wait_newline:
+        /* to make CR,LF issues easier we simply allow for white space
+           behind the 5 dashes */
+        if ( c == '\n' )
+            x->state = STA_skip_header_lines;
+        else if ( c != '\r' && c != ' ' && c != '\t' )
+            x->state = STA_wait_dash; /* garbage after the header line */
+        break;
+      case STA_skip_header_lines:
+        /* i.e. wait for one empty line */
+        if ( c == '\n' ) {
+            x->state = STA_read_data;
+            x->crc = CRCINIT;
+            x->val = 0;
+            x->pos = 0;
+        }
+        else if ( c != '\r' && c != ' ' && c != '\t' )
+            x->state = STA_skip_header_lines_non_ws;
+        break;
+      case STA_skip_header_lines_non_ws:
+        /* like above but we already encountered non white space */
+        if ( c == '\n' )
+            x->state = STA_skip_header_lines;
+        break;
+      case STA_read_data:
+        /* fixme: we don't check for the trailing dash lines but rely
+         * on the armor stop characters */
+        if( c == '\n' || c == ' ' || c == '\r' || c == '\t' )
+            break; /* skip all kind of white space */
+
+        if( c == '=' ) { /* pad character: stop */
+            if( x->pos == 1 ) /* in this case val has some value */
+                rval = x->val;
+            x->state = STA_wait_crc;
+            break;
+        }
+
+        {
+            int c2;
+            if( (c = asctobin[(c2=c)]) == 255 ) {
+                log_error(_("invalid radix64 character %02x skipped\n"), c2);
+                break;
+            }
+        }
+        
+        switch(x->pos) {
+          case 0:
+            x->val = c << 2;
+            break;
+          case 1:
+            x->val |= (c>>4)&3;
+            rval = x->val;
+            x->val = (c<<4)&0xf0;
+            break;
+          case 2:
+            x->val |= (c>>2)&15;
+            rval = x->val;
+            x->val = (c<<6)&0xc0;
+            break;
+          case 3:
+            x->val |= c&0x3f;
+            rval = x->val;
+            break;
+        }
+        x->pos = (x->pos+1) % 4;
+        break;
+      case STA_wait_crc:
+        if( c == '\n' || c == ' ' || c == '\r' || c == '\t' || c == '=' )
+            break; /* skip ws and pad characters */
+        /* assume that we are at the next line */
+        x->state = STA_read_crc;
+        x->pos = 0;
+        x->mycrc = 0;
+      case STA_read_crc:
+        if( (c = asctobin[c]) == 255 ) {
+            rval = -1; /* ready */
+            if( x->crc != x->mycrc ) {
+                log_info (_("CRC error; %06lx - %06lx\n"),
+                          (ulong)x->crc, (ulong)x->mycrc);
+                if ( invalid_crc() )
+                    rval = -3;
+            }
+            x->state = STA_ready; /* not sure whether this is correct */
+            break;
+        }
+        
+        switch(x->pos) {
+          case 0:
+            x->val = c << 2;
+            break;
+          case 1:
+            x->val |= (c>>4)&3;
+            x->mycrc |= x->val << 16;
+            x->val = (c<<4)&0xf0;
+            break;
+          case 2:
+            x->val |= (c>>2)&15;
+            x->mycrc |= x->val << 8;
+            x->val = (c<<6)&0xc0;
+            break;
+          case 3:
+            x->val |= c&0x3f;
+            x->mycrc |= x->val;
+            break;
+        }
+        x->pos = (x->pos+1) % 4;
+        break;
+      case STA_ready:
+        rval = -1;
+        break;
+    }
 
+    if ( !(rval & ~255) ) { /* compute the CRC */
+        x->crc = (x->crc << 8) ^ crc_table[((x->crc >> 16)&0xff) ^ rval];
+        x->crc &= 0x00ffffff;
+    }
+
+    return rval;
+}