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