armor rewritten, but still buggy
[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 #include "main.h"
36 #include "status.h"
37
38
39
40
41 #define CRCINIT 0xB704CE
42 #define CRCPOLY 0X864CFB
43 #define CRCUPDATE(a,c) do {                                                 \
44                         a = ((a) << 8) ^ crc_table[((a)&0xff >> 16) ^ (c)]; \
45                         a &= 0x00ffffff;                                    \
46                     } while(0)
47 static u32 crc_table[256];
48 static byte bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
49                          "abcdefghijklmnopqrstuvwxyz"
50                          "0123456789+/";
51 static byte asctobin[256]; /* runtime initialized */
52 static int is_initialized;
53
54
55 typedef enum {
56     fhdrHASArmor,
57     fhdrNOArmor,
58     fhdrINIT,
59     fhdrINITCont,
60     fhdrINITSkip,
61     fhdrCHECKBegin,
62     fhdrWAITHeader,
63     fhdrWAITClearsig,
64     fhdrSKIPHeader,
65     fhdrCLEARSIG,
66     fhdrREADClearsig,
67     fhdrEMPTYClearsig,
68     fhdrCHECKClearsig,
69     fhdrCHECKClearsig2,
70     fhdrCHECKDashEscaped,
71     fhdrCHECKDashEscaped2,
72     fhdrCHECKDashEscaped3,
73     fhdrREADClearsigNext,
74     fhdrENDClearsig,
75     fhdrTESTSpaces,
76     fhdrTEXT,
77     fhdrERROR,
78     fhdrERRORShow,
79     fhdrEOF
80 } fhdr_state_t;
81
82
83 /* if we encounter this armor string with this index, go
84  * into a mode, which fakes packets and wait for the next armor */
85 #define BEGIN_SIGNED_MSG_IDX 3
86 static char *head_strings[] = {
87     "BEGIN PGP MESSAGE",
88     "BEGIN PGP PUBLIC KEY BLOCK",
89     "BEGIN PGP SIGNATURE",
90     "BEGIN PGP SIGNED MESSAGE",
91     NULL
92 };
93 static char *tail_strings[] = {
94     "END PGP MESSAGE",
95     "END PGP PUBLIC KEY BLOCK",
96     "END PGP SIGNATURE",
97     NULL
98 };
99
100
101 static fhdr_state_t find_header( fhdr_state_t state, byte *buf,
102                      size_t *r_buflen, IOBUF a, size_t n, unsigned *r_empty);
103
104
105 static void
106 initialize(void)
107 {
108     int i, j;
109     u32 t;
110     byte *s;
111
112     /* init the crc lookup table */
113     crc_table[0] = 0;
114     for(i=j=0; j < 128; j++ ) {
115         t = crc_table[j];
116         if( t & 0x00800000 ) {
117             t <<= 1;
118             crc_table[i++] = t ^ CRCPOLY;
119             crc_table[i++] = t;
120         }
121         else {
122             t <<= 1;
123             crc_table[i++] = t;
124             crc_table[i++] = t ^ CRCPOLY;
125         }
126     }
127     /* build the helptable for radix64 to bin conversion */
128     for(i=0; i < 256; i++ )
129         asctobin[i] = 255; /* used to detect invalid characters */
130     for(s=bintoasc,i=0; *s; s++,i++ )
131         asctobin[*s] = i;
132
133     is_initialized=1;
134 }
135
136 /****************
137  * Check wether this is a armored file or not
138  * See also parse-packet.c for details on this code
139  * Returns: True if it seems to be armored
140  */
141 static int
142 is_armored( byte *buf )
143 {
144     int ctb, pkttype;
145
146     ctb = *buf;
147     if( !(ctb & 0x80) )
148         return 1; /* invalid packet: assume it is armored */
149     pkttype =  ctb & 0x40 ? (ctb & 0x3f) : ((ctb>>2)&0xf);
150     switch( pkttype ) {
151       case PKT_PUBLIC_CERT:
152       case PKT_SECRET_CERT:
153       case PKT_PUBKEY_ENC:
154       case PKT_SIGNATURE:
155       case PKT_COMMENT:
156       case PKT_PLAINTEXT:
157       case PKT_COMPRESSED:
158       case PKT_ENCRYPTED:
159         return 0; /* seems to be a regular packet: not armored */
160     }
161
162     return 1;
163 }
164
165 static void
166 invalid_armor(void)
167 {
168     write_status(STATUS_BADARMOR);
169     g10_exit(1); /* stop here */
170 }
171
172
173 /****************
174  * parse an ascii armor.
175  * Returns: the state,
176  *          the remaining bytes in BUF are returned in RBUFLEN.
177  *          r_empty return the # of empty lines before the buffer
178  */
179 static fhdr_state_t
180 find_header( fhdr_state_t state, byte *buf, size_t *r_buflen,
181                                         IOBUF a, size_t n, unsigned *r_empty)
182 {
183     int c, i;
184     const char *s;
185     byte *p;
186     size_t buflen;
187     int cont;
188     int clearsig=0;
189     int hdr_line=0;
190     unsigned empty = 0;
191
192     buflen = *r_buflen;
193     assert(buflen >= 100 );
194     buflen -= 3; /* reserved room for CR,LF and one extra */
195
196     do {
197         switch( state ) {
198           case fhdrHASArmor:
199             /* read 28 bytes, which is the bare minimum for a BEGIN...
200              * and check wether this has a Armor. */
201             c = 0;
202             for(n=0; n < 28 && (c=iobuf_get2(a)) != -1 && c != '\n'; )
203                 buf[n++] = c;
204             if( n < 28 || c == -1 )
205                 state = fhdrNOArmor; /* too short */
206             else if( !is_armored( buf ) )
207                 state = fhdrNOArmor;
208             else
209                 state = fhdrINITCont;
210             break;
211
212           case fhdrINIT: /* read some stuff into buffer */
213             n = 0;
214           case fhdrINITCont: /* read more stuff into buffer */
215             c = 0;
216             for(; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
217                 buf[n++] = c;
218             state = c == '\n' ? fhdrCHECKBegin :
219                      c == -1  ? fhdrEOF : fhdrINITSkip;
220             break;
221
222           case fhdrINITSkip:
223             while( (c=iobuf_get2(a)) != -1 && c != '\n' )
224                 ;
225             state =  c == -1? fhdrEOF : fhdrINIT;
226             break;
227
228           case fhdrSKIPHeader:
229             while( (c=iobuf_get2(a)) != -1 && c != '\n' )
230                 ;
231             state =  c == -1? fhdrEOF : fhdrWAITHeader;
232             break;
233
234           case fhdrWAITHeader: /* wait for Header lines */
235             c = 0;
236             for(n=0; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
237                 buf[n++] = c;
238             buf[n] = 0;
239             if( n < buflen || c == '\n' ) {
240                 if( n && buf[0] != '\r') { /* maybe a header */
241                     if( strchr( buf, ':') ) { /* yes */
242                         log_debug("armor header: ");
243                         print_string( stderr, buf, n );
244                         putc('\n', stderr);
245                         state = fhdrWAITHeader;
246                     }
247                     else
248                         state = fhdrCHECKDashEscaped3;
249                 }
250                 else if( !n || (buf[0] == '\r' && !buf[1]) ) { /* empty line */
251                     if( clearsig )
252                         state = fhdrWAITClearsig;
253                     else {
254                         /* this is not really correct: if we do not have
255                          * a clearsig and no armor lines we are not allowed
256                          * to have an empty line */
257                         n = 0;
258                         state = fhdrTEXT;
259                     }
260                 }
261                 else {
262                     log_error("invalid armor header: ");
263                     print_string( stderr, buf, n );
264                     putc('\n', stderr);
265                     state = fhdrERROR;
266                 }
267             }
268             else if( c != -1 ) {
269                 if( strchr( buf, ':') ) { /* buffer to short, but this is okay*/
270                     log_debug("armor header: ");
271                     print_string( stderr, buf, n );
272                     fputs("[...]\n", stderr);  /* indicate it is truncated */
273                     state = fhdrSKIPHeader;  /* skip rest of line */
274                 }
275                 else /* line too long */
276                     state = fhdrERROR;
277             }
278             else
279                 state = fhdrEOF;
280             break;
281
282           case fhdrWAITClearsig: /* skip all empty lines (for clearsig) */
283             c = 0;
284             for(n=0; n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n'; )
285                 buf[n++] = c;
286             if( n < buflen || c == '\n' ) {
287                 buf[n] = 0;
288                 if( !n || (buf[0]=='\r' && !buf[1]) ) /* empty line */
289                     ;
290                 else
291                     state = fhdrCHECKDashEscaped3;
292             }
293             else {
294                 /* fixme: we should check wether this linee continues
295                  *   it is poosible that we have only read ws until here
296                  *   and more stuff is to come */
297                 state = fhdrEOF;
298             }
299             break;
300
301           case fhdrENDClearsig:
302           case fhdrCHECKBegin:
303             state = state == fhdrCHECKBegin ? fhdrINITSkip : fhdrERRORShow;
304             if( n < 15 )
305                 break;  /* too short */
306             if( memcmp( buf, "-----", 5 ) )
307                 break;
308             buf[n] = 0;
309             p = strstr(buf+5, "-----");
310             if( !p )
311                 break;
312             *p = 0;
313             p += 5;
314             if( *p == '\r' )
315                 p++;
316             if( *p )
317                 break; /* garbage after dashes */
318             p = buf+5;
319             for(i=0; (s=head_strings[i]); i++ )
320                 if( !strcmp(s, p) )
321                     break;
322             if( !s )
323                 break; /* unknown begin line */
324             /* found the begin line */
325             hdr_line = i;
326             state = fhdrWAITHeader;
327             if( hdr_line == BEGIN_SIGNED_MSG_IDX )
328                 clearsig = 1;
329             log_debug("armor: %s\n", head_strings[hdr_line]);
330             break;
331
332           case fhdrCLEARSIG:
333           case fhdrEMPTYClearsig:
334           case fhdrREADClearsig:
335             /* we are at the start of a line: read a clearsig into the buffer
336              * we have to look for a the header line or dashed escaped text*/
337             n = 0;
338             c = 0;
339             while( n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n' )
340                 buf[n++] = c;
341             buf[n] = 0;
342             if( c == -1 )
343                 state = fhdrEOF;
344             else if( !n || ( buf[0]=='\r' && !buf[1] ) ) {
345                 state = fhdrEMPTYClearsig;
346                 empty++;
347             }
348             else if( c == '\n' )
349                 state = fhdrCHECKClearsig2;
350             else
351                 state = fhdrCHECKClearsig;
352             break;
353
354           case fhdrCHECKDashEscaped3:
355             if( !(n > 1 && buf[0] == '-' && buf[1] == ' ' ) ) {
356                 state = fhdrTEXT;
357                 break;
358             }
359             /* fall through */
360           case fhdrCHECKDashEscaped2:
361           case fhdrCHECKDashEscaped:
362             /* check dash escaped line */
363             if( buf[2] == '-' || ( n > 6 && !memcmp(buf+2, "From ", 5))) {
364                 for(i=2; i < n; i++ )
365                     buf[i-2] = buf[i];
366                 n -= 2;
367                 buf[n] = 0; /* not really needed */
368                 state = state == fhdrCHECKDashEscaped3 ? fhdrTEXT :
369                         state == fhdrCHECKDashEscaped2 ?
370                                  fhdrREADClearsig : fhdrTESTSpaces;
371             }
372             else {
373                 log_error("invalid dash escaped line: ");
374                 print_string( stderr, buf, n );
375                 putc('\n', stderr);
376                 state = fhdrERROR;
377             }
378             break;
379
380           case fhdrCHECKClearsig:
381           case fhdrCHECKClearsig2:
382             /* check the clearsig line */
383             if( n > 15 && !memcmp(buf, "-----", 5 ) )
384                 state = fhdrENDClearsig;
385             else if( buf[0] == '-' && buf[1] == ' ' )
386                 state = fhdrCHECKDashEscaped;
387             else {
388                 state = state == fhdrCHECKClearsig2 ?
389                                   fhdrREADClearsig : fhdrTESTSpaces;
390             }
391             break;
392
393           case fhdrREADClearsigNext:
394             /* Read to the end of the line, do not care about checking
395              * for dashed escaped text of headers */
396             c = 0;
397             n = 0;
398             while( n < buflen && (c=iobuf_get2(a)) != -1 && c != '\n' )
399                 buf[n++] = c;
400             buf[n] = 0;
401             if( c == -1 )
402                 state = fhdrEOF;
403             else if( c == '\n' )
404                 state = fhdrREADClearsig;
405             else
406                 state = fhdrTESTSpaces;
407             break;
408
409           case fhdrTESTSpaces: {
410             /* but must check wether the rest of the line
411              * does only contain white spaces; this is problematic
412              * since we may have to restore the stuffs. simply
413              * counting spaces is not enough, because it may be a
414              * mix of different white space chacters */
415             IOBUF b = iobuf_temp();
416             while( (c=iobuf_get2(a)) != -1 && c != '\n' ) {
417                 iobuf_put(b,c);
418                 if( c != ' ' && c != '\t' && c != '\r' )
419                     break;
420             }
421             if( c == '\n' ) {
422                 /* okay we can skip the rest of the line */
423                 iobuf_close(b);
424                 state = fhdrREADClearsig;
425             }
426             else {
427                 iobuf_unget_and_close_temp(a,b);
428                 state = fhdrREADClearsigNext;
429             }
430           } break;
431
432           case fhdrERRORShow:
433             log_error("invalid clear text header: ");
434             print_string( stderr, buf, n );
435             putc('\n', stderr);
436             state = fhdrERROR;
437             break;
438
439           default: BUG();
440         }
441         switch( state ) {
442           case fhdrINIT:
443           case fhdrINITCont:
444           case fhdrINITSkip:
445           case fhdrCHECKBegin:
446           case fhdrWAITHeader:
447           case fhdrWAITClearsig:
448           case fhdrSKIPHeader:
449           case fhdrEMPTYClearsig:
450           case fhdrCHECKClearsig:
451           case fhdrCHECKClearsig2:
452           case fhdrCHECKDashEscaped:
453           case fhdrCHECKDashEscaped2:
454           case fhdrCHECKDashEscaped3:
455           case fhdrTESTSpaces:
456           case fhdrERRORShow:
457             cont = 1;
458             break;
459           default: cont = 0;
460         }
461     } while( cont );
462
463     if( clearsig && state == fhdrTEXT )
464         state = fhdrCLEARSIG;
465
466     if( state == fhdrCLEARSIG || state == fhdrREADClearsig ) {
467         /* append CR,LF after removing trailing wspaces */
468         for(p=buf+n-1; n; n--, p-- ) {
469             assert( *p != '\n' );
470             if( *p != ' ' && *p != '\t' && *p != '\r' ) {
471                 p[1] = '\r';
472                 p[2] = '\n';
473                 n += 2;
474                 break;
475             }
476         }
477         if( !n ) {
478             buf[0] = '\r';
479             buf[1] = '\n';
480             n = 2;
481         }
482     }
483
484
485     *r_buflen = n;
486     *r_empty = empty;
487     return state;
488 }
489
490
491 /* figure out wether the data is armored or not */
492 static int
493 check_input( armor_filter_context_t *afx, IOBUF a )
494 {
495     int rc = 0;
496     size_t n;
497     fhdr_state_t state = afx->parse_state;
498     unsigned emplines;
499
500     if( state != fhdrENDClearsig )
501         state = fhdrHASArmor;
502
503     n = DIM(afx->helpbuf);
504     state = find_header( state, afx->helpbuf, &n, a, afx->helplen, &emplines);
505     switch( state ) {
506       case fhdrNOArmor:
507         afx->inp_checked = 1;
508         afx->inp_bypass = 1;
509         afx->helplen = n;
510         break;
511
512       case fhdrERROR:
513         invalid_armor();
514         break;
515
516       case fhdrEOF:
517         rc = -1;
518         break;
519
520       case fhdrCLEARSIG: /* start fake package mode (for clear signatures) */
521         afx->helplen = n;
522         afx->helpidx = 0;
523         afx->faked = 1;
524         break;
525
526       case fhdrTEXT:
527         afx->helplen = n;
528         afx->helpidx = 0;
529         afx->inp_checked = 1;
530         afx->crc = CRCINIT;
531         afx->idx = 0;
532         afx->radbuf[0] = 0;
533         break;
534
535       default: BUG();
536     }
537
538     afx->parse_state = state;
539     return rc;
540 }
541
542
543
544 /* fake a literal data packet and wait for an armor line */
545 static int
546 fake_packet( armor_filter_context_t *afx, IOBUF a,
547              size_t *retn, byte *buf, size_t size  )
548 {
549     int rc = 0;
550     size_t len = 0;
551     size_t n, nn;
552     fhdr_state_t state = afx->parse_state;
553     unsigned emplines = afx->empty;
554
555     size = 100; /* FIXME: only used for testing (remove it)  */
556
557     len = 2;    /* reserve 2 bytes for the length header */
558     size -= 3;  /* and 1 for empline handling and 2 for the term header */
559     while( !rc && len < size ) {
560         if( emplines ) {
561             while( emplines && len < size ) {
562                 buf[len++] = '\r';
563                 buf[len++] = '\n';
564                 emplines--;
565             }
566             continue;
567         }
568         if( afx->helpidx < afx->helplen ) { /* flush the last buffer */
569             n = afx->helplen;
570             for(nn=afx->helpidx; len < size && nn < n ; nn++ )
571                 buf[len++] = afx->helpbuf[nn];
572             afx->helpidx = nn;
573             continue;
574         }
575         if( state == fhdrEOF ) {
576             rc = -1;
577             continue;
578         }
579         /* read a new one */
580         n = DIM(afx->helpbuf);
581         afx->helpidx = 0;
582         state = find_header( state, afx->helpbuf, &n, a, 0, &emplines );
583         switch( state) {
584           case fhdrERROR:
585             invalid_armor();
586             break;
587
588           case fhdrEOF:
589             rc = -1;
590             break;
591
592           case fhdrCLEARSIG:
593             BUG();
594
595           case fhdrREADClearsig:
596           case fhdrREADClearsigNext:
597             afx->helplen = n;
598             break;
599
600           case fhdrENDClearsig:
601             afx->helplen = n;
602             afx->faked = 0;
603             rc = -1;
604             break;
605
606           default: BUG();
607         }
608     }
609     buf[0] = (len-2) >> 8;
610     buf[1] = (len-2);
611     if( state == fhdrENDClearsig ) { /* write last (ending) length header */
612         buf[len++] = 0;
613         buf[len++] = 0;
614         rc = 0;
615     }
616
617     afx->parse_state = state;
618     afx->empty = emplines;
619     *retn = len;
620     return rc;
621 }
622
623
624
625 static int
626 radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
627               byte *buf, size_t size )
628 {
629     byte val;
630     int c, c2;
631     int checkcrc=0;
632     int rc = 0;
633     size_t n = 0;
634     int  idx, i;
635     u32 crc;
636
637     crc = afx->crc;
638     idx = afx->idx;
639     val = afx->radbuf[0];
640     for( n=0; n < size; ) {
641         if( afx->helpidx < afx->helplen )
642             c = afx->helpbuf[afx->helpidx++];
643         else if( (c=iobuf_get(a)) == -1 )
644             break;
645         if( c == '\n' || c == ' ' || c == '\r' || c == '\t' )
646             continue;
647         else if( c == '=' ) { /* pad character: stop */
648             if( idx == 1 )
649                 buf[n++] = val;
650             checkcrc++;
651             break;
652         }
653         else if( (c = asctobin[(c2=c)]) == 255 ) {
654             log_error("invalid radix64 character %02x skipped\n", c2);
655             continue;
656         }
657         switch(idx) {
658           case 0: val =  c << 2; break;
659           case 1: val |= (c>>4)&3; buf[n++]=val;val=(c<<4)&0xf0;break;
660           case 2: val |= (c>>2)&15; buf[n++]=val;val=(c<<6)&0xc0;break;
661           case 3: val |= c&0x3f; buf[n++] = val; break;
662         }
663         idx = (idx+1) % 4;
664     }
665     for(i=0; i < n; i++ )
666         crc = (crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ buf[i]];
667     crc &= 0x00ffffff;
668     afx->crc = crc;
669     afx->idx = idx;
670     afx->radbuf[0] = val;
671     if( checkcrc ) {
672         afx->inp_eof = 1; /*assume eof */
673         for(;;) { /* skip lf and pad characters */
674             if( afx->helpidx < afx->helplen )
675                 c = afx->helpbuf[afx->helpidx++];
676             else if( (c=iobuf_get(a)) == -1 )
677                 break;
678             if( c == '\n' || c == ' ' || c == '\r'
679                 || c == '\t' || c == '=' )
680                 continue;
681             break;
682         }
683         if( c == -1 )
684             log_error("premature eof (no CRC)\n");
685         else {
686             u32 mycrc = 0;
687             idx = 0;
688             do {
689                 if( (c = asctobin[c]) == 255 )
690                     break;
691                 switch(idx) {
692                   case 0: val =  c << 2; break;
693                   case 1: val |= (c>>4)&3; mycrc |= val << 16;val=(c<<4)&0xf0;break;
694                   case 2: val |= (c>>2)&15; mycrc |= val << 8;val=(c<<6)&0xc0;break;
695                   case 3: val |= c&0x3f; mycrc |= val; break;
696                 }
697                 if( afx->helpidx < afx->helplen )
698                     c = afx->helpbuf[afx->helpidx++];
699                 else if( (c=iobuf_get(a)) == -1 )
700                     break;
701             } while( ++idx < 4 );
702             if( c == -1 )
703                 log_error("premature eof (in CRC)\n");
704             else if( idx != 4 )
705                 log_error("malformed CRC\n");
706             else if( mycrc != afx->crc )
707                 log_error("CRC error; %06lx - %06lx\n",
708                                     (ulong)afx->crc, (ulong)mycrc);
709             else {
710                 rc = 0;
711               #if 0
712                 for(rc=0;!rc;) {
713                     rc = 0 /*check_trailer( &fhdr, c )*/;
714                     if( !rc ) {
715                         if( afx->helpidx < afx->helplen )
716                             c = afx->helpbuf[afx->helpidx++];
717                         else if( (c=iobuf_get(a)) == -1 )
718                             rc = 2;
719                     }
720                 }
721                 if( rc == -1 )
722                     rc = 0;
723                 else if( rc == 2 )
724                     log_error("premature eof (in Trailer)\n");
725                 else
726                     log_error("error in trailer line\n");
727               #endif
728             }
729         }
730     }
731
732     if( !n )
733         rc = -1;
734
735     *retn = n;
736     return rc;
737 }
738
739
740 /****************
741  * The filter is used to handle the armor stuff
742  */
743 int
744 armor_filter( void *opaque, int control,
745              IOBUF a, byte *buf, size_t *ret_len)
746 {
747     size_t size = *ret_len;
748     armor_filter_context_t *afx = opaque;
749     int rc=0, i, c;
750     byte radbuf[3];
751     int  idx, idx2;
752     size_t n=0;
753     u32 crc;
754     static FILE *fp ;
755
756     if( !fp ) {
757         fp = fopen("armor.out", "w");
758         assert(fp);
759     }
760
761     if( DBG_FILTER )
762         log_debug("armor-filter: control: %d\n", control );
763     if( control == IOBUFCTRL_UNDERFLOW && afx->inp_bypass ) {
764         for( n=0; n < size; n++ ) {
765             if( (c=iobuf_get(a)) == -1 )
766                 break;
767             buf[n] = c & 0xff;
768         }
769         if( !n )
770             rc = -1;
771         *ret_len = n;
772     }
773     else if( control == IOBUFCTRL_UNDERFLOW ) {
774         if( size < 30 )
775             BUG(); /* supplied buffer too short */
776
777         if( afx->inp_eof ) {
778             *ret_len = 0;
779             if( DBG_FILTER )
780                 log_debug("armor-filter: eof due to inp_eof flag\n" );
781             return -1;
782         }
783
784         if( afx->faked )
785             rc = fake_packet( afx, a, &n, buf, size );
786         else if( !afx->inp_checked ) {
787             rc = check_input( afx, a );
788             if( afx->inp_bypass )
789                 ;
790             else if( afx->faked ) {
791                 /* the buffer is at least 30 bytes long, so it
792                  * is easy to construct the packets */
793
794                 /* first a onepass signature packet */
795                 buf[0] = 0x90; /* old packet forma, type 4, 1 length byte */
796                 buf[1] = 13;   /* length */
797                 buf[2] = 3;    /* version */
798                 buf[3] = 0x01; /* sigclass 0x01 (data in canonical text mode)*/
799                 buf[4] = 0;    /* digest algo (don't know) */
800                 buf[5] = 0;    /* public key algo (don't know) */
801                 memset(buf+6, 0, 8); /* don't know the keyid */
802                 buf[14] = 1;   /* this is the last one */
803
804                 /* followed by a plaintext packet */
805                 buf[15] = 0xaf; /* old packet format, type 11, var length */
806                 buf[16] = 0;    /* set the length header */
807                 buf[17] = 6;
808                 buf[18] = 't';  /* canonical text mode */
809                 buf[19] = 0;    /* namelength */
810                 memset(buf+20, 0, 4); /* timestamp */
811                 n = 24;
812             }
813             else if( !rc )
814                 rc = radix64_read( afx, a, &n, buf, size );
815         }
816         else
817             rc = radix64_read( afx, a, &n, buf, size );
818         if( n )
819             if( fwrite(buf, n, 1, fp ) != 1 )
820                 BUG();
821         *ret_len = n;
822     }
823     else if( control == IOBUFCTRL_FLUSH ) {
824         if( !afx->status ) { /* write the header line */
825             if( afx->what >= DIM(head_strings) )
826                 log_bug("afx->what=%d", afx->what);
827             iobuf_writestr(a, "-----");
828             iobuf_writestr(a, head_strings[afx->what] );
829             iobuf_writestr(a, "-----\n");
830             iobuf_writestr(a, "Version: G10 pre-release "  VERSION "\n");
831             iobuf_writestr(a, "Comment: This is an alpha test version!\n\n");
832             afx->status++;
833             afx->idx = 0;
834             afx->idx2 = 0;
835             afx->crc = CRCINIT;
836         }
837         crc = afx->crc;
838         idx = afx->idx;
839         idx2 = afx->idx2;
840         for(i=0; i < idx; i++ )
841             radbuf[i] = afx->radbuf[i];
842
843         for(i=0; i < size; i++ )
844             crc = (crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ buf[i]];
845         crc &= 0x00ffffff;
846
847         for( ; size; buf++, size-- ) {
848             radbuf[idx++] = *buf;
849             if( idx > 2 ) {
850                 idx = 0;
851                 c = bintoasc[(*radbuf >> 2) & 077];
852                 iobuf_put(a, c);
853                 c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
854                 iobuf_put(a, c);
855                 c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
856                 iobuf_put(a, c);
857                 c = bintoasc[radbuf[2]&077];
858                 iobuf_put(a, c);
859                 if( ++idx2 > (72/4) ) {
860                     iobuf_put(a, '\n');
861                     idx2=0;
862                 }
863             }
864         }
865         for(i=0; i < idx; i++ )
866             afx->radbuf[i] = radbuf[i];
867         afx->idx = idx;
868         afx->idx2 = idx2;
869         afx->crc  = crc;
870     }
871     else if( control == IOBUFCTRL_INIT ) {
872         if( !is_initialized )
873             initialize();
874     }
875     else if( control == IOBUFCTRL_FREE ) {
876         if( afx->status ) { /* pad, write cecksum, and bottom line */
877             crc = afx->crc;
878             idx = afx->idx;
879             idx2 = afx->idx2;
880             for(i=0; i < idx; i++ )
881                 radbuf[i] = afx->radbuf[i];
882             if( idx ) {
883                 c = bintoasc[(*radbuf>>2)&077];
884                 iobuf_put(a, c);
885                 if( idx == 1 ) {
886                     c = bintoasc[((*radbuf << 4) & 060) & 077];
887                     iobuf_put(a, c);
888                     iobuf_put(a, '=');
889                     iobuf_put(a, '=');
890                 }
891                 else { /* 2 */
892                     c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
893                     iobuf_put(a, c);
894                     c = bintoasc[((radbuf[1] << 2) & 074) & 077];
895                     iobuf_put(a, c);
896                     iobuf_put(a, '=');
897                 }
898                 ++idx2;
899             }
900             /* may need a linefeed */
901             if( idx2 < (72/4) )
902                 iobuf_put(a, '\n');
903             /* write the CRC */
904             iobuf_put(a, '=');
905             radbuf[0] = crc >>16;
906             radbuf[1] = crc >> 8;
907             radbuf[2] = crc;
908             c = bintoasc[(*radbuf >> 2) & 077];
909             iobuf_put(a, c);
910             c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
911             iobuf_put(a, c);
912             c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
913             iobuf_put(a, c);
914             c = bintoasc[radbuf[2]&077];
915             iobuf_put(a, c);
916             iobuf_put(a, '\n');
917             /* and the the trailer */
918             if( afx->what >= DIM(tail_strings) )
919                 log_bug("afx->what=%d", afx->what);
920             iobuf_writestr(a, "-----");
921             iobuf_writestr(a, tail_strings[afx->what] );
922             iobuf_writestr(a, "-----\n");
923         }
924     }
925     else if( control == IOBUFCTRL_DESC )
926         *(char**)buf = "armor_filter";
927     return rc;
928 }
929