Fixed a couple of problems
[gnupg.git] / g10 / pipemode.c
1 /* pipemode.c - pipemode handler
2  * Copyright (C) 1998, 1990, 2000, 2001 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG 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  * GnuPG 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA.
20  */
21
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <assert.h>
28
29 #include "options.h"
30 #include "packet.h"
31 #include "errors.h"
32 #include "iobuf.h"
33 #include "keydb.h"
34 #include "memory.h"
35 #include "util.h"
36 #include "main.h"
37 #include "status.h"
38 #include "filter.h"
39
40
41 #define CONTROL_PACKET_SPACE 30 
42 #define FAKED_LITERAL_PACKET_SPACE (9+2+2)
43
44
45 enum pipemode_state_e {
46     STX_init = 0,
47     STX_wait_operation,
48     STX_begin,
49     STX_text,
50     STX_detached_signature,
51     STX_detached_signature_wait_text,
52     STX_signed_data,
53     STX_wait_init
54 };
55
56 struct pipemode_context_s {
57     enum pipemode_state_e state;
58     int operation;
59     int stop;
60     int block_mode;
61     UnarmorPump unarmor_ctx;
62 };
63
64
65 static size_t
66 make_control ( byte *buf, int code, int operation )
67 {
68     const byte *sesmark;
69     size_t sesmarklen, n=0;;
70
71     sesmark = get_session_marker( &sesmarklen );
72     if ( sesmarklen > 20 )
73         BUG();
74
75     buf[n++] = 0xff; /* new format, type 63, 1 length byte */
76     n++;   /* length will fixed below */
77     memcpy(buf+n, sesmark, sesmarklen ); n+= sesmarklen;
78     buf[n++] = CTRLPKT_PIPEMODE;    
79     buf[n++] = code;
80     buf[n++] = operation;
81     buf[1] = n-2;
82     return n;
83 }
84
85
86
87 static int
88 pipemode_filter( void *opaque, int control,
89                  IOBUF a, byte *buf, size_t *ret_len)
90
91     size_t size = *ret_len;
92     struct pipemode_context_s *stx = opaque;
93     int rc=0;
94     size_t n = 0;
95     int esc = 0;
96
97     if( control == IOBUFCTRL_UNDERFLOW ) {
98         *ret_len = 0;
99         /* reserve some space for one control packet */
100         if ( size <= CONTROL_PACKET_SPACE+FAKED_LITERAL_PACKET_SPACE )
101             BUG();
102         size -= CONTROL_PACKET_SPACE+FAKED_LITERAL_PACKET_SPACE;
103
104         if ( stx->block_mode ) {
105             /* reserve 2 bytes for the block length */
106             buf[n++] = 0;
107             buf[n++] = 0;
108         }
109             
110
111         while ( n < size ) {
112             /* FIXME: we have to make sure that we have a large enough
113              * buffer for a control packet even after we already read 
114              * something. The easest way to do this is probably by ungetting
115              * the control sequence and returning the buffer we have
116              * already assembled */
117             int c = iobuf_get (a);
118             if (c == -1) {
119                 if ( stx->state != STX_init ) {
120                     log_error ("EOF encountered at wrong state\n");
121                     stx->stop = 1;
122                     return -1;
123                 }
124                 break;
125             }
126             if ( esc ) {
127                 switch (c) {
128                   case '@':  
129                     if ( stx->state == STX_text ) {
130                         buf[n++] = c;
131                         break;
132                     }
133                     else if ( stx->state == STX_detached_signature ) {
134                         esc = 0;
135                         goto do_unarmor; /* not a very elegant solution */
136                     }
137                     else if ( stx->state == STX_detached_signature_wait_text) {
138                         esc = 0;
139                         break; /* just ignore it in this state */
140                     }
141                     log_error ("@@ not allowed in current state\n");
142                     return -1;
143                   case '<': /* begin of stream part */
144                     if ( stx->state != STX_init ) {
145                         log_error ("nested begin of stream\n");
146                         stx->stop = 1;
147                         return -1;
148                     }
149                     stx->state = STX_wait_operation;
150                     stx->block_mode = 0;
151                     unarmor_pump_release (stx->unarmor_ctx);
152                     stx->unarmor_ctx = NULL;
153                     break;
154                    case '>': /* end of stream part */
155                      if ( stx->state != STX_wait_init ) {
156                         log_error ("invalid state for @>\n");
157                         stx->stop = 1;
158                         return -1;
159                     }
160                     stx->state = STX_init;
161                     break;
162                   case 'V': /* operation = verify */
163                   case 'E': /* operation = encrypt */
164                   case 'S': /* operation = sign */
165                   case 'B': /* operation = detach sign */
166                   case 'C': /* operation = clearsign */
167                   case 'D': /* operation = decrypt */
168                     if ( stx->state != STX_wait_operation ) {
169                         log_error ("invalid state for operation code\n");
170                         stx->stop = 1;
171                         return -1;
172                     }
173                     stx->operation = c;
174                     if ( stx->operation == 'B') {
175                         stx->state = STX_detached_signature;
176                         if ( !opt.no_armor )
177                             stx->unarmor_ctx = unarmor_pump_new ();
178                     }
179                     else
180                         stx->state = STX_begin;
181                     n += make_control ( buf+n, 1, stx->operation );
182                     /* must leave after a control packet */
183                     goto leave;
184
185                   case 't': /* plaintext text follows */
186                     if ( stx->state == STX_detached_signature_wait_text ) 
187                         stx->state = STX_detached_signature;
188                     if ( stx->state == STX_detached_signature ) {
189                         if ( stx->operation != 'B' ) {
190                             log_error ("invalid operation for this state\n");
191                             stx->stop = 1;
192                             return -1;
193                         }
194                         stx->state = STX_signed_data;
195                         n += make_control ( buf+n, 2, 'B' );
196                         /* and now we fake a literal data packet much the same
197                          * as in armor.c */
198                         buf[n++] = 0xaf; /* old packet format, type 11,
199                                             var length */
200                         buf[n++] = 0;    /* set the length header */
201                         buf[n++] = 6;
202                         buf[n++] = 'b';  /* we ignore it anyway */
203                         buf[n++] = 0;    /* namelength */
204                         memset(buf+n, 0, 4); /* timestamp */
205                         n += 4;
206                         /* and return now so that we are sure to have
207                          * more space in the bufer for the next control
208                          * packet */
209                         stx->block_mode = 1;
210                         goto leave2;
211                     }
212                     else {
213                         log_error ("invalid state for @t\n");
214                         stx->stop = 1;
215                         return -1;
216                     }
217                     break;
218
219                   case '.': /* ready */
220                     if ( stx->state == STX_signed_data ) { 
221                         if (stx->block_mode) {
222                             buf[0] = (n-2) >> 8;
223                             buf[1] = (n-2);
224                             if ( buf[0] || buf[1] ) {
225                                 /* end of blocks marker */
226                                 buf[n++] = 0;
227                                 buf[n++] = 0;
228                             }
229                             stx->block_mode = 0;
230                         }
231                         n += make_control ( buf+n, 3, 'B' );
232                     }
233                     else {
234                         log_error ("invalid state for @.\n");
235                         stx->stop = 1;
236                         return -1;
237                     }
238                     stx->state = STX_wait_init;
239                     goto leave;
240
241                  default:      
242                     log_error ("invalid escape sequence 0x%02x in stream\n",
243                                c);
244                     stx->stop = 1;
245                     return -1;
246                 }
247                 esc = 0;
248             }
249             else if (c == '@') 
250                 esc = 1;
251             else if (stx->unarmor_ctx) {
252           do_unarmor: /* used to handle a @@ */
253                 c = unarmor_pump (stx->unarmor_ctx, c);
254                 if ( !(c & ~255) )
255                     buf[n++] = c;
256                 else if ( c < 0 ) {
257                     /* end of armor or error - we don't care becuase
258                       the armor can be modified anyway.  The unarmored
259                       stuff should stand for itself. */ 
260                     unarmor_pump_release (stx->unarmor_ctx);
261                     stx->unarmor_ctx = NULL;
262                     stx->state = STX_detached_signature_wait_text;
263                 }
264             }
265             else if (stx->state == STX_detached_signature_wait_text)
266                 ; /* just wait */
267             else
268                 buf[n++] = c; 
269         }
270
271       leave:      
272         if ( !n ) {
273             stx->stop = 1;
274             rc = -1; /* eof */
275         }
276         if ( stx->block_mode ) {
277             /* fixup the block length */
278             buf[0] = (n-2) >> 8;
279             buf[1] = (n-2);
280         }
281       leave2:
282         /*log_hexdump ("pipemode:", buf, n );*/
283         *ret_len = n;
284     }
285     else if( control == IOBUFCTRL_DESC )
286         *(char**)buf = "pipemode_filter";
287     return rc;
288 }
289
290
291
292 void
293 run_in_pipemode(void)
294 {
295     IOBUF fp;
296     armor_filter_context_t afx;
297     struct pipemode_context_s stx;
298     int rc;
299
300     memset( &afx, 0, sizeof afx);
301     memset( &stx, 0, sizeof stx);
302
303     fp = iobuf_open("-");
304     iobuf_push_filter (fp, pipemode_filter, &stx );
305
306     do {
307         write_status (STATUS_BEGIN_STREAM);
308         rc = proc_packets( NULL, fp );
309         write_status (STATUS_END_STREAM);
310     } while ( !stx.stop );
311   
312 }
313
314
315
316
317
318