Fixed a few bugs
[gnupg.git] / g10 / armor.c
1 /* armor.c - Armor filter
2  *      Copyright (c) 1997 by Werner Koch (dd9jn)
3  *
4  * This file is part of G10.
5  *
6  * G10 is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * G10 is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <assert.h>
27
28 #include "errors.h"
29 #include "iobuf.h"
30 #include "memory.h"
31 #include "util.h"
32 #include "filter.h"
33 #include "packet.h"
34 #include "options.h"
35
36
37
38
39 #define CRCINIT 0xB704CE
40 #define CRCPOLY 0X864CFB
41 #define CRCUPDATE(a,c) do {                                                 \
42                         a = ((a) << 8) ^ crc_table[((a)&0xff >> 16) ^ (c)]; \
43                         a &= 0x00ffffff;                                    \
44                     } while(0)
45 static u32 crc_table[256];
46 static byte bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
47                          "abcdefghijklmnopqrstuvwxyz"
48                          "0123456789+/";
49 static byte asctobin[256]; /* runtime initialized */
50 static int is_initialized;
51
52
53 typedef enum {
54     fhdrHASArmor,
55     fhdrNOArmor,
56     fhdrINIT,
57     fhdrINITCont,
58     fhdrINITSkip,
59     fhdrCHECKBegin,
60     fhdrWAITHeader,
61     fhdrWAITClearsig,
62     fhdrSKIPHeader,
63     fhdrCLEARSIG,
64     fhdrREADClearsig,
65     fhdrEMPTYClearsig,
66     fhdrCHECKClearsig,
67     fhdrCHECKClearsig2,
68     fhdrREADClearsigNext,
69     fhdrENDClearsig,
70     fhdrTEXT,
71     fhdrERROR,
72     fhdrERRORShow,
73     fhdrEOF
74 } fhdr_state_t;
75
76
77 /* if we encounter this armor string with this index, go
78  * into a mode, which fakes packets and wait for the next armor */
79 #define BEGIN_SIGNED_MSG_IDX 3
80 static char *head_strings[] = {
81     "BEGIN PGP MESSAGE",
82     "BEGIN PGP PUBLIC KEY BLOCK",
83     "BEGIN PGP SIGNATURE",
84     "BEGIN PGP SIGNED MESSAGE",
85     NULL
86 };
87 static char *tail_strings[] = {
88     "END PGP MESSAGE",
89     "END PGP PUBLIC KEY BLOCK",
90     "END PGP SIGNATURE",
91     NULL
92 };
93
94
95 static fhdr_state_t find_header( fhdr_state_t state,
96                           byte *buf, size_t *r_buflen, IOBUF a, size_t n);
97
98
99 static void
100 initialize(void)
101 {
102     int i, j;
103     u32 t;
104     byte *s;
105
106     /* init the crc lookup table */
107     crc_table[0] = 0;
108     for(i=j=0; j < 128; j++ ) {
109         t = crc_table[j];
110         if( t & 0x00800000 ) {
111             t <<= 1;
112             crc_table[i++] = t ^ CRCPOLY;
113             crc_table[i++] = t;
114         }
115         else {
116             t <<= 1;
117             crc_table[i++] = t;
118             crc_table[i++] = t ^ CRCPOLY;
119         }
120     }
121     /* build the helptable for radix64 to bin conversion */
122     for(i=0; i < 256; i++ )
123         asctobin[i] = 255; /* used to detect invalid characters */
124     for(s=bintoasc,i=0; *s; s++,i++ )
125         asctobin[*s] = i;
126
127     is_initialized=1;
128 }
129
130 /****************
131  * Check wether this is a armored file or not
132  * See also parse-packet.c for details on this code
133  * Returns: True if it seems to be armored
134  */
135 static int
136 is_armored( byte *buf )
137 {
138     int ctb, pkttype;
139
140     ctb = *buf;
141     if( !(ctb & 0x80) )
142         return 1; /* invalid packet: assume it is armored */
143     pkttype =  ctb & 0x40 ? (ctb & 0x3f) : ((ctb>>2)&0xf);
144     switch( pkttype ) {
145       case PKT_PUBLIC_CERT:
146       case PKT_SECRET_CERT:
147       case PKT_PUBKEY_ENC:
148       case PKT_SIGNATURE:
149       case PKT_COMMENT:
150       case PKT_PLAINTEXT:
151       case PKT_COMPRESSED:
152       case PKT_ENCRYPTED:
153         return 0; /* seems to be a regular packet: not armored */
154     }
155
156     return 1;
157 }
158
159
160 /****************
161  * parse an ascii armor.
162  * Returns: the state,
163  *          the remaining bytes in BUF are returned in RBUFLEN.
164  */
165 static fhdr_state_t
166 find_header( fhdr_state_t state, byte *buf, size_t *r_buflen, IOBUF a, size_t n)
167 {
168     int c, i;
169     const char *s;
170     char *p;
171     size_t buflen;
172     int cont;
173     int clearsig=0;
174     int hdr_line=0;
175
176     buflen = *r_buflen;
177     assert(buflen >= 100 );
178     buflen--;
179
180     do {
181         switch( state ) {
182           case fhdrHASArmor:
183             /* read 28 bytes, which is the bare minimum for a BEGIN...
184              * and check wether this has a Armor. */
185             c = 0;
186             for(n=0; n < 28 && (c=iobuf_get(a)) != -1 && c != '\n'; )
187                 buf[n++] = c;
188             if( n < 28 || c == -1 )
189                 state = fhdrNOArmor; /* too short */
190             else if( !is_armored( buf ) )
191                 state = fhdrNOArmor;
192             else
193                 state = fhdrINITCont;
194             break;
195
196           case fhdrINIT: /* read some stuff into buffer */
197             n = 0;
198           case fhdrINITCont: /* read more stuff into buffer */
199             c = 0;
200             for(; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
201                 buf[n++] = c;
202             state = c == '\n' ? fhdrCHECKBegin :
203                      c == -1  ? fhdrEOF : fhdrINITSkip;
204             break;
205
206           case fhdrINITSkip:
207             while( (c=iobuf_get(a)) != -1 && c != '\n' )
208                 ;
209             state =  c == -1? fhdrEOF : fhdrINIT;
210             break;
211
212           case fhdrSKIPHeader:
213             while( (c=iobuf_get(a)) != -1 && c != '\n' )
214                 ;
215             state =  c == -1? fhdrEOF : fhdrWAITHeader;
216             break;
217
218           case fhdrWAITHeader: /* wait for Header lines */
219             c = 0;
220             for(n=0; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
221                 buf[n++] = c;
222             buf[n] = 0;
223             if( n < buflen || c == '\n' ) {
224                 if( n && buf[0] != '\r') { /* maybe a header */
225                     if( strchr( buf, ':') ) { /* yes */
226                         log_debug("armor header: ");
227                         print_string( stderr, buf, n );
228                         putc('\n', stderr);
229                         state = fhdrWAITHeader;
230                     }
231                     else
232                         state = fhdrTEXT;
233                 }
234                 else if( !n || (buf[0] == '\r' && !buf[1]) ) { /* empty line */
235                     if( clearsig )
236                         state = fhdrWAITClearsig;
237                     else {
238                         /* this is not really correct: if we do not have
239                          * a clearsig and not armor lines we are not allowed
240                          * to have an empty line */
241                         n = 0;
242                         state = fhdrTEXT;
243                     }
244                 }
245                 else {
246                     log_debug("invalid armor header: ");
247                     print_string( stderr, buf, n );
248                     putc('\n', stderr);
249                     state = fhdrERROR;
250                 }
251             }
252             else if( c != -1 ) {
253                 if( strchr( buf, ':') ) { /* buffer to short, but this is okay*/
254                     log_debug("armor header: ");
255                     print_string( stderr, buf, n );
256                     fputs("[...]\n", stderr);  /* indicate it is truncated */
257                     state = fhdrSKIPHeader;  /* skip rest of line */
258                 }
259                 else /* line too long */
260                     state = fhdrERROR;
261             }
262             else
263                 state = fhdrEOF;
264             break;
265
266           case fhdrWAITClearsig: /* skip all empty lines (for clearsig) */
267             c = 0;
268             for(n=0; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
269                 buf[n++] = c;
270             if( n < buflen || c == '\n' ) {
271                 buf[n] = 0;
272                 if( !n || (buf[0]=='\r' && !buf[1]) ) /* empty line */
273                     ;
274                 else
275                     state = fhdrTEXT;
276             }
277             else
278                 state = fhdrEOF;
279             break;
280
281           case fhdrENDClearsig:
282           case fhdrCHECKBegin:
283             state = state == fhdrCHECKBegin ? fhdrINITSkip : fhdrERRORShow;
284             if( n < 15 )
285                 break;  /* too short */
286             if( memcmp( buf, "-----", 5 ) )
287                 break;
288             buf[n] = 0;
289             p = strstr(buf+5, "-----");
290             if( !p )
291                 break;
292             *p = 0;
293             p += 5;
294             if( *p == '\r' )
295                 p++;
296             if( *p )
297                 break; /* garbage after dashes */
298             p = buf+5;
299             for(i=0; (s=head_strings[i]); i++ )
300                 if( !strcmp(s, p) )
301                     break;
302             if( !s )
303                 break; /* unknown begin line */
304             /* found the begin line */
305             hdr_line = i;
306             state = fhdrWAITHeader;
307             if( hdr_line == BEGIN_SIGNED_MSG_IDX )
308                 clearsig = 1;
309             log_debug("armor: %s\n", head_strings[hdr_line]);
310             break;
311
312           case fhdrCLEARSIG:
313           case fhdrEMPTYClearsig:
314           case fhdrREADClearsig:
315             /* we are at the start of a line: read a clearsig into the buffer
316              * we have to look for a the header line or dashd escaped text*/
317             n = 0;
318             c = 0;
319             for(; n < buflen && (c=iobuf_get(a)) != -1 && c != '\n'; )
320                 buf[n++] = c;
321             buf[n] = 0;
322             if( c == -1 )
323                 state = fhdrEOF;
324             else if( !n || ( buf[0]=='\r' && !buf[1] ) ) {
325                 state = fhdrEMPTYClearsig;
326                 /* FIXME: handle it */
327             }
328             else if( c == '\n' )
329                 state = fhdrCHECKClearsig2;
330             else
331                 state = fhdrCHECKClearsig;
332             break;
333
334           case fhdrCHECKClearsig:
335           case fhdrCHECKClearsig2:
336             /* check the clearsig line */
337             if( n > 15 && !memcmp(buf, "-----", 5 ) )
338                 state = fhdrENDClearsig;
339             else if( buf[0] == '-' && buf[1] == ' ' ) {
340                 /* dash escaped line */
341                 if( buf[2] == '-' || ( n > 6 && !memcmp(buf+2, "From ", 5))) {
342                     for(i=2; i < n; i++ )
343                         buf[i-2] = buf[i];
344                     n -= 2;
345                     buf[n] = 0; /* not really needed */
346                     state = state == fhdrCHECKClearsig2 ?
347                                      fhdrREADClearsig : fhdrREADClearsigNext;
348                     /* FIXME: add the lf to the buffer */
349                 }
350                 else {
351                     log_debug("invalid dash escaped line: ");
352                     print_string( stderr, buf, n );
353                     putc('\n', stderr);
354                     state = fhdrERROR;
355                 }
356             }
357             else {
358                 state = state == fhdrCHECKClearsig2 ?
359                                   fhdrREADClearsig : fhdrREADClearsigNext;
360                 /* FIXME: add the lf to the buffer */
361             }
362             break;
363
364           case fhdrREADClearsigNext:
365             /* Read to the end of the line, do not care about checking
366              * for dashed escaped text of headers */
367             break;
368
369           case fhdrERRORShow:
370             log_debug("invalid clear text header: ");
371             print_string( stderr, buf, n );
372             putc('\n', stderr);
373             state = fhdrERROR;
374             break;
375
376           default: BUG();
377         }
378         switch( state ) {
379           case fhdrINIT:
380           case fhdrINITCont:
381           case fhdrINITSkip:
382           case fhdrCHECKBegin:
383           case fhdrWAITHeader:
384           case fhdrWAITClearsig:
385           case fhdrSKIPHeader:
386           case fhdrEMPTYClearsig:
387           case fhdrCHECKClearsig:
388           case fhdrCHECKClearsig2:
389           case fhdrERRORShow:
390             cont = 1;
391             break;
392           default: cont = 0;
393         }
394     } while( cont );
395
396     if( clearsig && state == fhdrTEXT )
397         state = fhdrCLEARSIG;
398     *r_buflen = n;
399     return state;
400 }
401
402
403 /* figure out wether the data is armored or not */
404 static int
405 check_input( armor_filter_context_t *afx, IOBUF a )
406 {
407     int rc = 0;
408     size_t n;
409     fhdr_state_t state = afx->parse_state;
410
411     if( state != fhdrENDClearsig )
412         state = fhdrHASArmor;
413
414     n = DIM(afx->helpbuf);
415     state = find_header( state, afx->helpbuf, &n, a, afx->helplen );
416     switch( state ) {
417       case fhdrNOArmor:
418         afx->inp_checked = 1;
419         afx->inp_bypass = 1;
420         afx->helplen = n;
421         break;
422
423       case fhdrERROR:
424       case fhdrEOF:
425         rc = -1;
426         break;
427
428       case fhdrCLEARSIG: /* start fake package mode (for clear signatures) */
429         afx->helplen = n;
430         afx->helpidx = 0;
431         afx->faked = 1;
432         break;
433
434       case fhdrTEXT:
435         afx->helplen = n;
436         afx->helpidx = 0;
437         afx->inp_checked = 1;
438         afx->crc = CRCINIT;
439         afx->idx = 0;
440         afx->radbuf[0] = 0;
441         break;
442
443       default: BUG();
444     }
445
446     afx->parse_state = state;
447     return rc;
448 }
449
450
451
452 /* fake a literal data packet and wait for an armor line */
453 static int
454 fake_packet( armor_filter_context_t *afx, IOBUF a,
455              size_t *retn, byte *buf, size_t size  )
456 {
457     int rc = 0;
458     size_t len = 0;
459     size_t n, nn;
460     fhdr_state_t state = afx->parse_state;
461
462     size = 100; /* FIXME: only used for testing (remove it)  */
463
464     len = 2;    /* reserve 2 bytes for the length header */
465     size -= 2;  /* and 2 for the term header */
466     while( !rc && len < size ) {
467         if( afx->helpidx < afx->helplen ) { /* flush the last buffer */
468             n = afx->helplen;
469             for(nn=afx->helpidx; len < size && nn < n ; nn++ )
470                 buf[len++] = afx->helpbuf[nn];
471             afx->helpidx = nn;
472             continue;
473         }
474         if( state == fhdrEOF ) {
475             rc = -1;
476             continue;
477         }
478         /* read a new one */
479         n = DIM(afx->helpbuf);
480         afx->helpidx = 0;
481         state = find_header( state, afx->helpbuf, &n, a, 0 );
482         switch( state) {
483           case fhdrERROR:
484           case fhdrEOF:
485             rc = -1;
486             break;
487
488           case fhdrCLEARSIG:
489           case fhdrREADClearsig:
490           case fhdrREADClearsigNext:
491             afx->helplen = n;
492             break;
493
494           case fhdrENDClearsig:
495             afx->helplen = n;
496             afx->faked = 0;
497             rc = -1;
498             break;
499
500           default: BUG();
501         }
502     }
503     buf[0] = (len-2) >> 8;
504     buf[1] = (len-2);
505     if( state == fhdrENDClearsig ) { /* write last (ending) length header */
506         buf[len++] = 0;
507         buf[len++] = 0;
508         rc = 0;
509     }
510
511     afx->parse_state = state;
512     *retn = len;
513     return rc;
514 }
515
516
517
518 static int
519 radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
520               byte *buf, size_t size )
521 {
522     byte val;
523     int c, c2;
524     int checkcrc=0;
525     int rc = 0;
526     size_t n = 0;
527     int  idx, i;
528     u32 crc;
529
530     crc = afx->crc;
531     idx = afx->idx;
532     val = afx->radbuf[0];
533     for( n=0; n < size; ) {
534         if( afx->helpidx < afx->helplen )
535             c = afx->helpbuf[afx->helpidx++];
536         else if( (c=iobuf_get(a)) == -1 )
537             break;
538         if( c == '\n' || c == ' ' || c == '\r' || c == '\t' )
539             continue;
540         else if( c == '=' ) { /* pad character: stop */
541             if( idx == 1 )
542                 buf[n++] = val;
543             checkcrc++;
544             break;
545         }
546         else if( (c = asctobin[(c2=c)]) == 255 ) {
547             log_error("invalid radix64 character %02x skipped\n", c2);
548             continue;
549         }
550         switch(idx) {
551           case 0: val =  c << 2; break;
552           case 1: val |= (c>>4)&3; buf[n++]=val;val=(c<<4)&0xf0;break;
553           case 2: val |= (c>>2)&15; buf[n++]=val;val=(c<<6)&0xc0;break;
554           case 3: val |= c&0x3f; buf[n++] = val; break;
555         }
556         idx = (idx+1) % 4;
557     }
558     for(i=0; i < n; i++ )
559         crc = (crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ buf[i]];
560     crc &= 0x00ffffff;
561     afx->crc = crc;
562     afx->idx = idx;
563     afx->radbuf[0] = val;
564     if( checkcrc ) {
565         afx->inp_eof = 1; /*assume eof */
566         for(;;) { /* skip lf and pad characters */
567             if( afx->helpidx < afx->helplen )
568                 c = afx->helpbuf[afx->helpidx++];
569             else if( (c=iobuf_get(a)) == -1 )
570                 break;
571             if( c == '\n' || c == ' ' || c == '\r'
572                 || c == '\t' || c == '=' )
573                 continue;
574             break;
575         }
576         if( c == -1 )
577             log_error("premature eof (no CRC)\n");
578         else {
579             u32 mycrc = 0;
580             idx = 0;
581             do {
582                 if( (c = asctobin[c]) == 255 )
583                     break;
584                 switch(idx) {
585                   case 0: val =  c << 2; break;
586                   case 1: val |= (c>>4)&3; mycrc |= val << 16;val=(c<<4)&0xf0;break;
587                   case 2: val |= (c>>2)&15; mycrc |= val << 8;val=(c<<6)&0xc0;break;
588                   case 3: val |= c&0x3f; mycrc |= val; break;
589                 }
590                 if( afx->helpidx < afx->helplen )
591                     c = afx->helpbuf[afx->helpidx++];
592                 else if( (c=iobuf_get(a)) == -1 )
593                     break;
594             } while( ++idx < 4 );
595             if( c == -1 )
596                 log_error("premature eof (in CRC)\n");
597             else if( idx != 4 )
598                 log_error("malformed CRC\n");
599             else if( mycrc != afx->crc )
600                 log_error("CRC error; %06lx - %06lx\n",
601                                     (ulong)afx->crc, (ulong)mycrc);
602             else {
603                 for(rc=0;!rc;) {
604                     rc = 0 /*check_trailer( &fhdr, c )*/;
605                     if( !rc ) {
606                         if( afx->helpidx < afx->helplen )
607                             c = afx->helpbuf[afx->helpidx++];
608                         else if( (c=iobuf_get(a)) == -1 )
609                             rc = 2;
610                     }
611                 }
612                 if( rc == -1 )
613                     rc = 0;
614                 else if( rc == 2 )
615                     log_error("premature eof (in Trailer)\n");
616                 else
617                     log_error("error in trailer line\n");
618             }
619         }
620     }
621
622     if( !n )
623         rc = -1;
624
625     *retn = n;
626     return rc;
627 }
628
629
630 /****************
631  * The filter is used to handle the armor stuff
632  */
633 int
634 armor_filter( void *opaque, int control,
635              IOBUF a, byte *buf, size_t *ret_len)
636 {
637     size_t size = *ret_len;
638     armor_filter_context_t *afx = opaque;
639     int rc=0, i, c;
640     byte radbuf[3];
641     int  idx, idx2;
642     size_t n=0;
643     u32 crc;
644
645     if( DBG_FILTER )
646         log_debug("armor-filter: control: %d\n", control );
647     if( control == IOBUFCTRL_UNDERFLOW && afx->inp_bypass ) {
648         for( n=0; n < size; n++ ) {
649             if( (c=iobuf_get(a)) == -1 )
650                 break;
651             buf[n] = c & 0xff;
652         }
653         if( !n )
654             rc = -1;
655         *ret_len = n;
656     }
657     else if( control == IOBUFCTRL_UNDERFLOW ) {
658         if( size < 20 )
659             BUG(); /* supplied buffer maybe too short */
660
661         if( afx->inp_eof ) {
662             *ret_len = 0;
663             if( DBG_FILTER )
664                 log_debug("armor-filter: eof due to inp_eof flag\n" );
665             return -1;
666         }
667
668         if( afx->faked )
669             rc = fake_packet( afx, a, &n, buf, size );
670         else if( !afx->inp_checked ) {
671             rc = check_input( afx, a );
672             if( afx->inp_bypass )
673                 ;
674             else if( afx->faked ) {
675                 /* the buffer is at least 20 bytes long, so it
676                  * is easy to construct a packet */
677                 buf[0] = 0xaf; /* old packet format, type 11, var length */
678                 buf[1] = 0;    /* set the length header */
679                 buf[2] = 6;
680                 buf[3] = 't';  /* canonical text */
681                 buf[4] = 0;    /* namelength */
682                 buf[5] = buf[6] = buf[7] = buf[8] = 0; /* timestamp */
683                 n = 9;
684             }
685             else if( !rc )
686                 rc = radix64_read( afx, a, &n, buf, size );
687         }
688         else
689             rc = radix64_read( afx, a, &n, buf, size );
690
691         *ret_len = n;
692     }
693     else if( control == IOBUFCTRL_FLUSH ) {
694         if( !afx->status ) { /* write the header line */
695             if( afx->what >= DIM(head_strings) )
696                 log_bug("afx->what=%d", afx->what);
697             iobuf_writestr(a, "-----");
698             iobuf_writestr(a, head_strings[afx->what] );
699             iobuf_writestr(a, "-----\n");
700             iobuf_writestr(a, "Version: G10 pre-release "  VERSION "\n");
701             iobuf_writestr(a, "Comment: This is an alpha test version!\n\n");
702             afx->status++;
703             afx->idx = 0;
704             afx->idx2 = 0;
705             afx->crc = CRCINIT;
706         }
707         crc = afx->crc;
708         idx = afx->idx;
709         idx2 = afx->idx2;
710         for(i=0; i < idx; i++ )
711             radbuf[i] = afx->radbuf[i];
712
713         for(i=0; i < size; i++ )
714             crc = (crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ buf[i]];
715         crc &= 0x00ffffff;
716
717         for( ; size; buf++, size-- ) {
718             radbuf[idx++] = *buf;
719             if( idx > 2 ) {
720                 idx = 0;
721                 c = bintoasc[(*radbuf >> 2) & 077];
722                 iobuf_put(a, c);
723                 c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
724                 iobuf_put(a, c);
725                 c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
726                 iobuf_put(a, c);
727                 c = bintoasc[radbuf[2]&077];
728                 iobuf_put(a, c);
729                 if( ++idx2 > (72/4) ) {
730                     iobuf_put(a, '\n');
731                     idx2=0;
732                 }
733             }
734         }
735         for(i=0; i < idx; i++ )
736             afx->radbuf[i] = radbuf[i];
737         afx->idx = idx;
738         afx->idx2 = idx2;
739         afx->crc  = crc;
740     }
741     else if( control == IOBUFCTRL_INIT ) {
742         if( !is_initialized )
743             initialize();
744     }
745     else if( control == IOBUFCTRL_FREE ) {
746         if( afx->status ) { /* pad, write cecksum, and bottom line */
747             crc = afx->crc;
748             idx = afx->idx;
749             idx2 = afx->idx2;
750             for(i=0; i < idx; i++ )
751                 radbuf[i] = afx->radbuf[i];
752             if( idx ) {
753                 c = bintoasc[(*radbuf>>2)&077];
754                 iobuf_put(a, c);
755                 if( idx == 1 ) {
756                     c = bintoasc[((*radbuf << 4) & 060) & 077];
757                     iobuf_put(a, c);
758                     iobuf_put(a, '=');
759                     iobuf_put(a, '=');
760                 }
761                 else { /* 2 */
762                     c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
763                     iobuf_put(a, c);
764                     c = bintoasc[((radbuf[1] << 2) & 074) & 077];
765                     iobuf_put(a, c);
766                     iobuf_put(a, '=');
767                 }
768                 ++idx2;
769             }
770             /* may need a linefeed */
771             if( idx2 < (72/4) )
772                 iobuf_put(a, '\n');
773             /* write the CRC */
774             iobuf_put(a, '=');
775             radbuf[0] = crc >>16;
776             radbuf[1] = crc >> 8;
777             radbuf[2] = crc;
778             c = bintoasc[(*radbuf >> 2) & 077];
779             iobuf_put(a, c);
780             c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
781             iobuf_put(a, c);
782             c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
783             iobuf_put(a, c);
784             c = bintoasc[radbuf[2]&077];
785             iobuf_put(a, c);
786             iobuf_put(a, '\n');
787             /* and the the trailer */
788             if( afx->what >= DIM(tail_strings) )
789                 log_bug("afx->what=%d", afx->what);
790             iobuf_writestr(a, "-----");
791             iobuf_writestr(a, tail_strings[afx->what] );
792             iobuf_writestr(a, "-----\n");
793         }
794     }
795     else if( control == IOBUFCTRL_DESC )
796         *(char**)buf = "armor_filter";
797     return rc;
798 }
799