import works
[gnupg.git] / g10 / armor.c
index 3a6fc41..03a393b 100644 (file)
@@ -32,8 +32,8 @@
 #include "filter.h"
 #include "packet.h"
 #include "options.h"
-
-
+#include "main.h"
+#include "status.h"
 
 
 #define CRCINIT 0xB704CE
@@ -65,8 +65,13 @@ typedef enum {
     fhdrEMPTYClearsig,
     fhdrCHECKClearsig,
     fhdrCHECKClearsig2,
+    fhdrCHECKDashEscaped,
+    fhdrCHECKDashEscaped2,
+    fhdrCHECKDashEscaped3,
     fhdrREADClearsigNext,
     fhdrENDClearsig,
+    fhdrENDClearsigHelp,
+    fhdrTESTSpaces,
     fhdrTEXT,
     fhdrERROR,
     fhdrERRORShow,
@@ -82,18 +87,21 @@ static char *head_strings[] = {
     "BEGIN PGP PUBLIC KEY BLOCK",
     "BEGIN PGP SIGNATURE",
     "BEGIN PGP SIGNED MESSAGE",
+    "BEGIN PGP ARMORED FILE",
     NULL
 };
 static char *tail_strings[] = {
     "END PGP MESSAGE",
     "END PGP PUBLIC KEY BLOCK",
     "END PGP SIGNATURE",
+    "END dummy",
+    "END PGP ARMORED FILE",
     NULL
 };
 
 
-static fhdr_state_t find_header( fhdr_state_t state,
-                         byte *buf, size_t *r_buflen, IOBUF a, size_t n);
+static fhdr_state_t find_header( fhdr_state_t state, byte *buf,
+                    size_t *r_buflen, IOBUF a, size_t n, unsigned *r_empty);
 
 
 static void
@@ -158,37 +166,120 @@ is_armored( byte *buf )
 
 
 /****************
+ * Try to check wether the iobuf is armored
+ * Returns true if this may be the case; the caller should use the
+ *        filter to do further processing.
+ */
+int
+use_armor_filter( IOBUF a )
+{
+    byte buf[1];
+    int n;
+
+    n = iobuf_peek(a, buf, 1 );
+    if( n == -1 )
+       return 0; /* EOF, doesn't matter wether armored or not */
+    if( !n )
+       return 1; /* can't check it: try armored */
+    return is_armored(buf);
+}
+
+
+
+
+static void
+invalid_armor(void)
+{
+    write_status(STATUS_BADARMOR);
+    g10_exit(1); /* stop here */
+}
+
+
+/****************
+ * check wether the armor header is valid on a signed message.
+ * this is for security reasons: the header lines are not included in the
+ * hash and by using some creative formatting rules, Mallory could fake
+ * any text at the beginning of a document; assuming it is read with
+ * a simple viewer. We do only allow the Hash Header.
+ */
+static int
+parse_hash_header( const char *line )
+{
+    const char *s, *s2;
+    unsigned found = 0;
+
+    if( strlen(line) < 6  || strlen(line) > 60 )
+       return 0; /* too short or too long */
+    if( memcmp( line, "Hash:", 5 ) )
+       return 0; /* invalid header */
+    s = line+5;
+    for(s=line+5;;s=s2) {
+       for(; *s && (*s==' ' || *s == '\t'); s++ )
+           ;
+       if( !*s )
+           break;
+       for(s2=s+1; *s2 && *s2!=' ' && *s2 != '\t' && *s2 != ','; s2++ )
+           ;
+       if( !strncmp( s, "RIPEMD160", s2-s ) )
+           found |= 1;
+       else if( !strncmp( s, "SHA1", s2-s ) )
+           found |= 2;
+       else if( !strncmp( s, "MD5", s2-s ) )
+           found |= 4;
+       else if( !strncmp( s, "MD2", s2-s ) )
+           found |= 8;
+       else
+           return 0;
+       for(; *s2 && (*s2==' ' || *s2 == '\t'); s2++ )
+           ;
+       if( *s2 && *s2 != ',' )
+           return 0;
+       if( *s2 )
+           s2++;
+    }
+    return found;
+}
+
+
+/****************
  * 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)
+find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
+                                       IOBUF a, size_t n, unsigned *r_empty)
 {
-    int c, i;
+    int c=0, i;
     const char *s;
-    char *p;
+    byte *p;
     size_t buflen;
     int cont;
     int clearsig=0;
     int hdr_line=0;
+    unsigned empty = 0;
 
     buflen = *r_buflen;
     assert(buflen >= 100 );
-    buflen--;
+    buflen -= 3; /* reserved room for CR,LF and one extra */
 
     do {
        switch( state ) {
          case fhdrHASArmor:
-           /* read 28 bytes, which is the bare minimum for a BEGIN...
-            * and check wether this has a Armor. */
+           /* read at least the first byte to check wether it is armored
+            * or not */
            c = 0;
-           for(n=0; n < 28 && (c=iobuf_get(a)) != -1 && c != '\n'; )
+           for(n=0; n < 28 && (c=iobuf_get2(a)) != -1 && c != '\n'; )
                buf[n++] = c;
-           if( n < 28 || c == -1 )
+           if( !n && c == '\n' )
+               state = fhdrCHECKBegin;
+           else if( !n  || c == -1 )
                state = fhdrNOArmor; /* too short */
            else if( !is_armored( buf ) )
                state = fhdrNOArmor;
+           else if( c == '\n' )
+               state = fhdrCHECKBegin;
            else
                state = fhdrINITCont;
            break;
@@ -197,53 +288,64 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, IOBUF a, size_t n)
            n = 0;
          case fhdrINITCont: /* read more stuff into buffer */
            c = 0;
-           for(; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
+           for(; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
                buf[n++] = c;
            state = c == '\n' ? fhdrCHECKBegin :
                     c == -1  ? fhdrEOF : fhdrINITSkip;
            break;
 
          case fhdrINITSkip:
-           while( (c=iobuf_get(a)) != -1 && c != '\n' )
-               ;
+           if( c == '\n' )
+               n = 0;
+           else {
+               while( (c=iobuf_get2(a)) != -1 && c != '\n' )
+                   ;
+           }
            state =  c == -1? fhdrEOF : fhdrINIT;
            break;
 
          case fhdrSKIPHeader:
-           while( (c=iobuf_get(a)) != -1 && c != '\n' )
+           while( (c=iobuf_get2(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'; )
+           for(n=0; n < buflen && (c=iobuf_get2(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 */
-                       log_debug("armor header: ");
-                       print_string( stderr, buf, n );
-                       putc('\n', stderr);
-                       state = fhdrWAITHeader;
+                       if( opt.verbose ) {
+                           log_info("armor header: ");
+                           print_string( stderr, buf, n );
+                           putc('\n', stderr);
+                       }
+                       if( clearsig && !parse_hash_header( buf ) ) {
+                           log_error("invalid clearsig header\n");
+                           state = fhdrERROR;
+                       }
+                       else
+                           state = fhdrWAITHeader;
                    }
                    else
-                       state = fhdrTEXT;
+                       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 not armor lines we are not allowed
+                        * a clearsig and no armor lines we are not allowed
                         * to have an empty line */
                        n = 0;
                        state = fhdrTEXT;
                    }
                }
                else {
-                   log_debug("invalid armor header: ");
+                   log_error("invalid armor header: ");
                    print_string( stderr, buf, n );
                    putc('\n', stderr);
                    state = fhdrERROR;
@@ -251,9 +353,11 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, IOBUF a, size_t n)
            }
            else if( c != -1 ) {
                if( strchr( buf, ':') ) { /* buffer to short, but this is okay*/
-                   log_debug("armor header: ");
-                   print_string( stderr, buf, n );
-                   fputs("[...]\n", stderr);  /* indicate it is truncated */
+                   if( opt.verbose ) {
+                       log_info("armor header: ");
+                       print_string( stderr, buf, n );
+                       fputs("[...]\n", stderr);  /* indicate it is truncated */
+                   }
                    state = fhdrSKIPHeader;  /* skip rest of line */
                }
                else /* line too long */
@@ -265,17 +369,21 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, IOBUF a, size_t n)
 
          case fhdrWAITClearsig: /* skip all empty lines (for clearsig) */
            c = 0;
-           for(n=0; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
+           for(n=0; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
                buf[n++] = c;
            if( n < buflen || c == '\n' ) {
                buf[n] = 0;
                if( !n || (buf[0]=='\r' && !buf[1]) ) /* empty line */
                    ;
                else
-                   state = fhdrTEXT;
+                   state = fhdrCHECKDashEscaped3;
            }
-           else
+           else {
+               /* fixme: we should check wether this line continues
+                *   it is poosible that we have only read ws until here
+                *   and more stuff is to come */
                state = fhdrEOF;
+           }
            break;
 
          case fhdrENDClearsig:
@@ -306,24 +414,25 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, IOBUF a, size_t n)
            state = fhdrWAITHeader;
            if( hdr_line == BEGIN_SIGNED_MSG_IDX )
                clearsig = 1;
-           log_debug("armor: %s\n", head_strings[hdr_line]);
+           if( opt.verbose > 1 )
+               log_info("armor: %s\n", head_strings[hdr_line]);
            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 the header line or dashd escaped text*/
+            * we have to look for a the header line or dashed escaped text*/
            n = 0;
            c = 0;
-           for(; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
+           while( n < buflen && (c=iobuf_get2(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;
-               /* FIXME: handle it */
+               empty++;
            }
            else if( c == '\n' )
                state = fhdrCHECKClearsig2;
@@ -331,43 +440,95 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, IOBUF a, size_t n)
                state = fhdrCHECKClearsig;
            break;
 
+         case fhdrCHECKDashEscaped3:
+           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 );
+               putc('\n', stderr);
+               state = fhdrERROR;
+           }
+           break;
+
          case fhdrCHECKClearsig:
-         case fhdrCHECKClearsig2:
            /* check the clearsig line */
            if( n > 15 && !memcmp(buf, "-----", 5 ) )
                state = fhdrENDClearsig;
-           else if( buf[0] == '-' && buf[1] == ' ' ) {
-               /* 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 == fhdrCHECKClearsig2 ?
-                                    fhdrREADClearsig : fhdrREADClearsigNext;
-                   /* FIXME: add the lf to the buffer */
-               }
-               else {
-                   log_debug("invalid dash escaped line: ");
-                   print_string( stderr, buf, n );
-                   putc('\n', stderr);
-                   state = fhdrERROR;
-               }
+           else if( buf[0] == '-' && buf[1] == ' ' )
+               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] == ' ' )
+               state = fhdrCHECKDashEscaped2;
            else {
-               state = state == fhdrCHECKClearsig2 ?
-                                 fhdrREADClearsig : fhdrREADClearsigNext;
-               /* FIXME: add the lf to the buffer */
+               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_get2(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 wether the rest of the line
+            * does only contain white spaces; this is problematic
+            * since we may have to restore the stuffs. simply
+            * counting spaces is not enough, because it may be a
+            * mix of different white space chacters */
+           IOBUF b = iobuf_temp();
+           while( (c=iobuf_get2(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_debug("invalid clear text header: ");
+           log_error("invalid clear text header: ");
            print_string( stderr, buf, n );
            putc('\n', stderr);
            state = fhdrERROR;
@@ -386,6 +547,10 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, IOBUF a, size_t n)
          case fhdrEMPTYClearsig:
          case fhdrCHECKClearsig:
          case fhdrCHECKClearsig2:
+         case fhdrCHECKDashEscaped:
+         case fhdrCHECKDashEscaped2:
+         case fhdrCHECKDashEscaped3:
+         case fhdrTESTSpaces:
          case fhdrERRORShow:
            cont = 1;
            break;
@@ -395,7 +560,28 @@ find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, IOBUF a, size_t n)
 
     if( clearsig && state == fhdrTEXT )
        state = fhdrCLEARSIG;
+
+    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;
+       }
+    }
+
+
     *r_buflen = n;
+    *r_empty = empty;
     return state;
 }
 
@@ -407,12 +593,13 @@ check_input( armor_filter_context_t *afx, IOBUF a )
     int rc = 0;
     size_t n;
     fhdr_state_t state = afx->parse_state;
+    unsigned emplines;
 
     if( state != fhdrENDClearsig )
        state = fhdrHASArmor;
 
     n = DIM(afx->helpbuf);
-    state = find_header( state, afx->helpbuf, &n, a, afx->helplen );
+    state = find_header( state, afx->helpbuf, &n, a, afx->helplen, &emplines);
     switch( state ) {
       case fhdrNOArmor:
        afx->inp_checked = 1;
@@ -421,6 +608,9 @@ check_input( armor_filter_context_t *afx, IOBUF a )
        break;
 
       case fhdrERROR:
+       invalid_armor();
+       break;
+
       case fhdrEOF:
        rc = -1;
        break;
@@ -458,12 +648,27 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
     size_t len = 0;
     size_t n, nn;
     fhdr_state_t state = afx->parse_state;
+    unsigned emplines = afx->empty;
 
     size = 100; /* FIXME: only used for testing (remove it)  */
 
     len = 2;   /* reserve 2 bytes for the length header */
-    size -= 2; /* and 2 for the term header */
+    size -= 3; /* and 1 for empline handling and 2 for the term header */
     while( !rc && len < size ) {
+       if( emplines ) {
+           while( emplines && len < size ) {
+               buf[len++] = '\r';
+               buf[len++] = '\n';
+               emplines--;
+           }
+           continue;
+       }
+       if( state == fhdrENDClearsigHelp ) {
+           state = fhdrENDClearsig;
+           afx->faked = 0;
+           rc = -1;
+           continue;
+       }
        if( afx->helpidx < afx->helplen ) { /* flush the last buffer */
            n = afx->helplen;
            for(nn=afx->helpidx; len < size && nn < n ; nn++ )
@@ -478,23 +683,29 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
        /* read a new one */
        n = DIM(afx->helpbuf);
        afx->helpidx = 0;
-       state = find_header( state, afx->helpbuf, &n, a, 0 );
+       state = find_header( state, afx->helpbuf, &n, a, 0, &emplines );
        switch( state) {
          case fhdrERROR:
+           invalid_armor();
+           break;
+
          case fhdrEOF:
            rc = -1;
            break;
 
          case fhdrCLEARSIG:
+           BUG();
+
          case fhdrREADClearsig:
          case fhdrREADClearsigNext:
            afx->helplen = n;
            break;
 
          case fhdrENDClearsig:
+           assert( emplines );
+           emplines--; /* don't count the last one */
+           state = fhdrENDClearsigHelp;
            afx->helplen = n;
-           afx->faked = 0;
-           rc = -1;
            break;
 
          default: BUG();
@@ -509,6 +720,7 @@ fake_packet( armor_filter_context_t *afx, IOBUF a,
     }
 
     afx->parse_state = state;
+    afx->empty = emplines;
     *retn = len;
     return rc;
 }
@@ -600,6 +812,8 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
                log_error("CRC error; %06lx - %06lx\n",
                                    (ulong)afx->crc, (ulong)mycrc);
            else {
+               rc = 0;
+             #if 0
                for(rc=0;!rc;) {
                    rc = 0 /*check_trailer( &fhdr, c )*/;
                    if( !rc ) {
@@ -615,6 +829,7 @@ radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
                    log_error("premature eof (in Trailer)\n");
                else
                    log_error("error in trailer line\n");
+             #endif
            }
        }
     }
@@ -641,6 +856,14 @@ armor_filter( void *opaque, int control,
     int  idx, idx2;
     size_t n=0;
     u32 crc;
+  #if 0
+    static FILE *fp ;
+
+    if( !fp ) {
+       fp = fopen("armor.out", "w");
+       assert(fp);
+    }
+  #endif
 
     if( DBG_FILTER )
        log_debug("armor-filter: control: %d\n", control );
@@ -655,8 +878,8 @@ armor_filter( void *opaque, int control,
        *ret_len = n;
     }
     else if( control == IOBUFCTRL_UNDERFLOW ) {
-       if( size < 20 )
-           BUG(); /* supplied buffer maybe too short */
+       if( size < 30 )
+           BUG(); /* supplied buffer too short */
 
        if( afx->inp_eof ) {
            *ret_len = 0;
@@ -669,25 +892,47 @@ armor_filter( void *opaque, int control,
            rc = fake_packet( afx, a, &n, buf, size );
        else if( !afx->inp_checked ) {
            rc = check_input( afx, a );
-           if( afx->inp_bypass )
-               ;
+           if( afx->inp_bypass ) {
+               for( n=0; n < size && n < afx->helplen; n++ )
+                   buf[n] = afx->helpbuf[n];
+               if( !n )
+                   rc = -1;
+               assert( n == afx->helplen );
+               afx->helplen = 0;
+           }
            else if( afx->faked ) {
-               /* the buffer is at least 20 bytes long, so it
-                * is easy to construct a packet */
-               buf[0] = 0xaf; /* old packet format, type 11, var length */
-               buf[1] = 0;    /* set the length header */
-               buf[2] = 6;
-               buf[3] = 't';  /* canonical text */
-               buf[4] = 0;    /* namelength */
-               buf[5] = buf[6] = buf[7] = buf[8] = 0; /* timestamp */
-               n = 9;
+               /* the buffer is at least 30 bytes long, so it
+                * is easy to construct the packets */
+
+               /* first a onepass signature packet */
+               buf[0] = 0x90; /* old packet forma, type 4, 1 length byte */
+               buf[1] = 13;   /* length */
+               buf[2] = 3;    /* version */
+               buf[3] = 0x01; /* sigclass 0x01 (data in canonical text mode)*/
+               buf[4] = 0;    /* digest algo (don't know) */
+               buf[5] = 0;    /* public key algo (don't know) */
+               memset(buf+6, 0, 8); /* don't know the keyid */
+               buf[14] = 1;   /* this is the last one */
+
+               /* followed by a plaintext packet */
+               buf[15] = 0xaf; /* old packet format, type 11, var length */
+               buf[16] = 0;    /* set the length header */
+               buf[17] = 6;
+               buf[18] = 't';  /* canonical text mode */
+               buf[19] = 0;    /* namelength */
+               memset(buf+20, 0, 4); /* timestamp */
+               n = 24;
            }
            else if( !rc )
                rc = radix64_read( afx, a, &n, buf, size );
        }
        else
            rc = radix64_read( afx, a, &n, buf, size );
-
+      #if 0
+       if( n )
+           if( fwrite(buf, n, 1, fp ) != 1 )
+               BUG();
+      #endif
        *ret_len = n;
     }
     else if( control == IOBUFCTRL_FLUSH ) {
@@ -697,8 +942,9 @@ armor_filter( void *opaque, int control,
            iobuf_writestr(a, "-----");
            iobuf_writestr(a, head_strings[afx->what] );
            iobuf_writestr(a, "-----\n");
-           iobuf_writestr(a, "Version: G10 pre-release "  VERSION "\n");
-           iobuf_writestr(a, "Comment: This is a alpha test version!\n\n");
+           iobuf_writestr(a, "Version: G10 v"  VERSION " ("
+                                           PRINTABLE_OS_NAME ")\n");
+           iobuf_writestr(a, "Comment: This is an alpha version!\n\n");
            afx->status++;
            afx->idx = 0;
            afx->idx2 = 0;
@@ -797,3 +1043,5 @@ armor_filter( void *opaque, int control,
     return rc;
 }
 
+
+