Update head to match stable 1.0
[gnupg.git] / tools / gpgsplit.c
1 /* gpgsplit.c - An OpenPGP packet splitting tool
2  * Copyright (C) 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 /* 
22  * TODO: Add an option to uncompress packets.  This should come quite handy.
23  */
24
25 #include <config.h>
26 #include <errno.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <unistd.h>
32 #include <assert.h>
33 #ifdef HAVE_DOSISH_SYSTEM
34   #include <fcntl.h> /* for setmode() */
35 #endif
36 #include <zlib.h>
37 #ifdef __riscos__
38 #include <unixlib/local.h>
39 #endif /* __riscos__ */
40
41 #define INCLUDED_BY_MAIN_MODULE 1
42 #include "../g10/packet.h"
43 #include "util.h"
44
45 static int opt_verbose;
46 static const char *opt_prefix = "";
47 static int opt_uncompress;
48
49 static void g10_exit( int rc );
50 static void split_packets (const char *fname);
51
52
53 enum cmd_and_opt_values { aNull = 0,
54     oVerbose      = 'v',
55     oPrefix       = 'p',                          
56     oUncompress   = 500,                      
57 aTest };
58
59
60 static ARGPARSE_OPTS opts[] = {
61
62     { 301, NULL, 0, "@Options:\n " },
63
64     { oVerbose, "verbose",   0, "verbose" },
65     { oPrefix,  "prefix",    2, "|STRING|Prepend filenames with STRING" },
66     { oUncompress, "uncompress", 0, "uncompress a packet"},
67 {0} };
68
69
70 const char *
71 strusage( int level )
72 {
73     const char *p;
74     switch( level ) {
75       case 11: p = "gpgsplit (GnuPG)";
76         break;
77       case 13: p = VERSION; break;
78       case 17: p = PRINTABLE_OS_NAME; break;
79       case 19: p =
80             "Please report bugs to <bug-gnupg@gnu.org>.\n";
81         break;
82       case 1:
83       case 40:  p =
84             "Usage: gpgsplit [options] [files] (-h for help)";
85         break;
86       case 41:  p =
87             "Syntax: gpgsplit [options] [files]\n"
88               "Split an OpenPGP message into packets\n";
89         break;
90
91       default:  p = default_strusage(level);
92     }
93     return p;
94 }
95
96
97
98 int
99 main( int argc, char **argv )
100 {
101     ARGPARSE_ARGS pargs;
102
103   #ifdef __riscos__
104     /* set global RISC OS specific properties */
105     __riscosify_control = __RISCOSIFY_NO_PROCESS;
106   #endif /* __riscos__ */
107   #ifdef HAVE_DOSISH_SYSTEM
108     setmode( fileno(stdin), O_BINARY );
109     setmode( fileno(stdout), O_BINARY );
110   #endif
111     log_set_name("gpgsplit");
112
113     pargs.argc = &argc;
114     pargs.argv = &argv;
115     pargs.flags=  1;  /* do not remove the args */
116     while( optfile_parse( NULL, NULL, NULL, &pargs, opts) ) {
117         switch( pargs.r_opt ) {
118           case oVerbose: opt_verbose = 1; break;
119           case oPrefix: opt_prefix = pargs.r.ret_str; break;
120           case oUncompress: opt_uncompress = 1; break;
121           default : pargs.err = 2; break;
122         }
123     }
124
125     if( log_get_errorcount(0) )
126         g10_exit(2);
127
128     if (!argc)
129         split_packets (NULL);
130     else {
131         for ( ;argc; argc--, argv++) 
132             split_packets (*argv);
133     }
134
135     g10_exit (0);
136     return 0; 
137 }
138
139
140 static void
141 g10_exit( int rc )
142 {
143     rc = rc? rc : log_get_errorcount(0)? 2 : 0;
144     exit(rc );
145 }
146
147 static const char *
148 pkttype_to_string (int pkttype)
149 {
150     const char *s;
151     switch (pkttype) {
152         case PKT_PUBKEY_ENC    : s = "pk_enc"; break;
153         case PKT_SIGNATURE     : s = "sig"; break;
154         case PKT_SYMKEY_ENC    : s = "sym_enc"; break;
155         case PKT_ONEPASS_SIG   : s = "onepass_sig"; break;
156         case PKT_SECRET_KEY    : s = "secret_key"; break;
157         case PKT_PUBLIC_KEY    : s = "public_key"; break;
158         case PKT_SECRET_SUBKEY : s = "secret_subkey"; break;
159         case PKT_COMPRESSED    : 
160           s = opt_uncompress? "uncompressed":"compressed";
161           break;
162         case PKT_ENCRYPTED     : s = "encrypted"; break;
163         case PKT_MARKER        : s = "marker"; break;
164         case PKT_PLAINTEXT     : s = "plaintext"; break;
165         case PKT_RING_TRUST    : s = "ring_trust"; break;
166         case PKT_USER_ID       : s = "user_id"; break;
167         case PKT_PUBLIC_SUBKEY : s = "public_subkey"; break;
168         case PKT_OLD_COMMENT   : s = "old_comment"; break;
169         case PKT_ATTRIBUTE     : s = "attribute"; break;
170         case PKT_ENCRYPTED_MDC : s = "encrypted_mdc"; break;
171         case PKT_MDC           : s = "mdc"; break;
172         case PKT_COMMENT       : s = "comment"; break;
173         case PKT_GPG_CONTROL   : s = "gpg_control"; break;
174       default: s = "unknown"; break;
175     }
176     return s;
177 }
178
179
180 /*
181  * Create a new filename and a return a pointer to a statically
182  * allocated buffer 
183  */
184 static char *
185 create_filename (int pkttype)
186 {
187     static unsigned int partno = 0;
188     static char *name;
189
190     if (!name) 
191         name = m_alloc (strlen (opt_prefix) + 100 );
192
193     assert (pkttype < 1000 && pkttype >= 0 );
194     partno++;
195     sprintf (name, "%s%06u-%03d" EXTSEP_S "%.40s",
196              opt_prefix, partno, pkttype, pkttype_to_string (pkttype));
197     return name;
198 }
199
200 static int
201 read_u16 (FILE *fp, size_t *rn)
202 {
203     int c;
204
205     if ( (c = getc (fp)) == EOF )
206         return -1;
207     *rn = c << 8;
208     if ( (c = getc (fp)) == EOF )
209         return -1;
210     *rn |= c;
211     return 0;
212 }
213
214 static int
215 read_u32 (FILE *fp, unsigned long *rn)
216 {
217     size_t tmp;
218
219     if (read_u16 (fp, &tmp))
220         return -1;
221     *rn = tmp << 16;
222     if (read_u16 (fp, &tmp))
223         return -1;
224     *rn |= tmp;
225     return 0;
226 }
227
228
229 /* hdr must pint to a buffer large enough to hold all header bytes */
230 static int
231 write_part ( const char *fname, FILE *fpin, unsigned long pktlen,
232              int pkttype, int partial, unsigned char *hdr, size_t hdrlen)
233 {
234     FILE *fpout;
235     int c, first;
236     unsigned char *p;
237     const char *outname = create_filename (pkttype);
238
239     /* fixme: should we check that this file does not yet exist? */
240     if (opt_verbose)
241         log_info ("writing `%s'\n", outname);
242     fpout = fopen (outname, "wb");
243     if (!fpout) {
244         log_error ("error creating `%s': %s\n", outname, strerror(errno));
245         /* stop right now, otherwise we would mess up the sequence of
246          * the part numbers */
247         g10_exit (1);
248     }
249
250     if (!opt_uncompress) {
251         for (p=hdr; hdrlen; p++, hdrlen--) {
252             if ( putc (*p, fpout) == EOF )
253                 goto write_error;
254         }
255     }
256
257     first = 1;
258     while (partial) {
259         size_t partlen;
260
261         if (partial == 1) { /* openpgp */
262             if( first ) {
263                 c = pktlen;
264                 assert( c >= 224 && c < 255 );
265                 first = 0;
266             }
267             else if( (c = getc (fpin)) == EOF ) {
268                 goto read_error;
269             }
270             else
271                 hdr[hdrlen++] = c;
272             
273             if( c < 192 ) {
274                 pktlen = c;
275                 partial = 0; /* (last segment may follow) */
276             }
277             else if( c < 224 ) {
278                 pktlen = (c - 192) * 256;
279                 if( (c = getc (fpin)) == EOF ) 
280                     goto read_error;
281                 hdr[hdrlen++] = c;
282                 pktlen += c + 192;
283                 partial = 0;
284             }
285             else if( c == 255 ) {
286                 if (read_u32 (fpin, &pktlen))
287                     goto read_error;
288                 hdr[hdrlen++] = pktlen >> 24;
289                 hdr[hdrlen++] = pktlen >> 16;
290                 hdr[hdrlen++] = pktlen >> 8;
291                 hdr[hdrlen++] = pktlen;
292                 partial = 0;
293             }
294             else { /* next partial body length */
295                 for (p=hdr; hdrlen; p++, hdrlen--) {
296                     if ( putc (*p, fpout) == EOF )
297                         goto write_error;
298                 }
299                 partlen = 1 << (c & 0x1f);
300                 for (; partlen; partlen--) {
301                     if ((c = getc (fpin)) == EOF) 
302                         goto read_error;
303                     if ( putc (c, fpout) == EOF )
304                         goto write_error;
305                 }
306             }
307         }
308         else if (partial == 2) { /* old gnupg */
309             assert (!pktlen);
310             if ( read_u16 (fpin, &partlen) )
311                 goto read_error;
312             hdr[hdrlen++] = partlen >> 8;
313             hdr[hdrlen++] = partlen;
314             for (p=hdr; hdrlen; p++, hdrlen--) {
315                 if ( putc (*p, fpout) == EOF )
316                     goto write_error;
317             }
318             if (!partlen)
319                 partial = 0; /* end of packet */
320             for (; partlen; partlen--) {
321                 c = getc (fpin);
322                 if (c == EOF) 
323                     goto read_error;
324                 if ( putc (c, fpout) == EOF )
325                     goto write_error;
326             }
327         }
328         else { /* compressed: read to end */
329             pktlen = 0;
330             partial = 0;
331             hdrlen = 0;
332             if (opt_uncompress) {
333                 z_stream zs;
334                 byte *inbuf, *outbuf;
335                 unsigned int inbufsize, outbufsize;
336                 int algo, zinit_done, zrc, nread, count;
337                 size_t n;
338
339                 if ((c = getc (fpin)) == EOF)
340                     goto read_error;
341                 algo = c;
342                 
343                 memset (&zs, 0, sizeof zs);
344                 inbufsize = 2048;
345                 inbuf = m_alloc (inbufsize);
346                 outbufsize = 8192;
347                 outbuf = m_alloc (outbufsize);
348                 zs.avail_in = 0;
349                 zinit_done = 0;
350                 
351                 do {
352                     if (zs.avail_in < inbufsize) {
353                         n = zs.avail_in;
354                         if (!n)
355                             zs.next_in = (Bytef *) inbuf;
356                         count = inbufsize - n;
357                         for (nread=0;
358                              nread < count && (c=getc (fpin)) != EOF;
359                              nread++) {
360                             inbuf[n+nread] = c;
361                         }
362                         n += nread;
363                         if (nread < count && algo == 1) {
364                             inbuf[n] = 0xFF; /* chew dummy byte */
365                             n++;
366                         }
367                         zs.avail_in = n;
368                     }
369                     zs.next_out = (Bytef *) outbuf;
370                     zs.avail_out = outbufsize;
371                     
372                     if (!zinit_done) {
373                         zrc = algo == 1? inflateInit2 ( &zs, -13)
374                                        : inflateInit ( &zs );
375                         if (zrc != Z_OK) {
376                             log_fatal ("zlib problem: %s\n", zs.msg? zs.msg :
377                                        zrc == Z_MEM_ERROR ? "out of core" :
378                                        zrc == Z_VERSION_ERROR ?
379                                        "invalid lib version" :
380                                        "unknown error" );
381                         }
382                         zinit_done = 1;
383                     }
384                     else {
385 #ifdef Z_SYNC_FLUSH
386                         zrc = inflate (&zs, Z_SYNC_FLUSH);
387 #else
388                         zrc = inflate (&zs, Z_PARTIAL_FLUSH);
389 #endif
390                         if (zrc == Z_STREAM_END)
391                             ; /* eof */
392                         else if (zrc != Z_OK && zrc != Z_BUF_ERROR) {
393                             if (zs.msg)
394                                 log_fatal ("zlib inflate problem: %s\n", zs.msg );
395                             else
396                                 log_fatal ("zlib inflate problem: rc=%d\n", zrc );
397                         }
398                         for (n=0; n < outbufsize - zs.avail_out; n++) {
399                             if (putc (outbuf[n], fpout) == EOF )
400                                 goto write_error;
401                         }
402                     }
403                 } while (zrc != Z_STREAM_END && zrc != Z_BUF_ERROR);
404                 inflateEnd (&zs);
405             }
406             else {
407                 while ( (c=getc (fpin)) != EOF ) {
408                     if ( putc (c, fpout) == EOF )
409                         goto write_error;
410                 }
411             }
412             if (!feof (fpin))
413                 goto read_error;
414         }
415
416     }
417
418     for (p=hdr; hdrlen; p++, hdrlen--) {
419         if ( putc (*p, fpout) == EOF )
420             goto write_error;
421     }
422     /* standard packet or last segment of partial length encoded packet */
423     for (; pktlen; pktlen--) {
424         c = getc (fpin);
425         if (c == EOF) 
426             goto read_error;
427         if ( putc (c, fpout) == EOF )
428             goto write_error;
429     }
430     
431         
432     if ( fclose (fpout) )
433         log_error ("error closing `%s': %s\n", outname, strerror (errno));
434     return 0;
435
436  write_error:    
437     log_error ("error writing `%s': %s\n", outname, strerror (errno));
438     fclose (fpout);
439     return 2;
440     
441  read_error: {
442         int save = errno;
443         fclose (fpout);
444         errno = save;
445     }
446     return -1;
447 }
448
449
450
451 static int
452 do_split (const char *fname, FILE *fp)
453 {
454     int c, ctb, pkttype;
455     unsigned long pktlen = 0;
456     int partial = 0;
457     unsigned char header[20];
458     int header_idx = 0;
459
460     ctb = getc (fp);
461     if (ctb == EOF)
462         return 3; /* ready */
463     header[header_idx++] = ctb;
464
465     if( !(ctb & 0x80) ) {
466         log_error("invalid CTB %02x\n", ctb );
467         return 1;
468     }
469     if ( (ctb & 0x40) ) { /* new CTB */
470         pkttype =  (ctb & 0x3f);
471         if( (c = getc (fp)) == EOF )
472             return -1;
473         header[header_idx++] = c;
474
475         if( c < 192 )
476             pktlen = c;
477         else if( c < 224 ) {
478             pktlen = (c - 192) * 256;
479             if( (c = getc (fp)) == EOF ) 
480                 return -1;
481             header[header_idx++] = c;
482             pktlen += c + 192;
483         }
484         else if( c == 255 ) {
485             if (read_u32 (fp, &pktlen))
486                 return -1;
487             header[header_idx++] = pktlen >> 24;
488             header[header_idx++] = pktlen >> 16;
489             header[header_idx++] = pktlen >> 8;
490             header[header_idx++] = pktlen; 
491         }
492         else { /* partial body length */
493             pktlen = c;
494             partial = 1;
495         }
496     }
497     else {
498         int lenbytes;
499
500         pkttype = (ctb>>2)&0xf;
501         lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3));
502         if( !lenbytes ) {
503             pktlen = 0; /* don't know the value */
504             if( pkttype == PKT_COMPRESSED )
505                 partial = 3;
506             else
507                 partial = 2; /* the old GnuPG partial length encoding */
508         }
509         else {
510             for( ; lenbytes; lenbytes-- ) {
511                 pktlen <<= 8;
512                 if( (c = getc (fp)) == EOF ) 
513                     return -1;
514                 header[header_idx++] = c;
515
516                 pktlen |= c;
517             }
518         }
519     }
520
521     return write_part (fname, fp, pktlen, pkttype, partial,
522                        header, header_idx);
523 }
524
525
526 static void
527 split_packets (const char *fname)
528 {
529     FILE *fp;
530     int rc;
531
532     if (!fname || !strcmp (fname, "-")) {
533         fp = stdin;
534         fname = "-";
535     }
536     else if ( !(fp = fopen (fname,"rb")) ) {
537         log_error ("can't open `%s': %s\n", fname, strerror (errno));
538         return;
539     }
540
541     while ( !(rc = do_split (fname, fp)) )
542         ;
543     if ( rc > 0 )
544         ; /* error already handled */
545     else if ( ferror (fp) )
546         log_error ("error reading `%s': %s\n", fname, strerror (errno));
547     else
548         log_error ("premature EOF while reading `%s'\n", fname );
549
550     if ( fp != stdin )
551         fclose (fp);
552 }
553