mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-23 10:29:58 +01:00
a028f24136
* configure.ac (GPGRT_ENABLE_ARGPARSE_MACROS): Define. * common/argparse.c, common/argparse.h: Rewrite. * tests/gpgscm/main.c: Switch to the new option parser. * g10/gpg.c: Switch to the new option parser and enable a global conf file. * g10/gpgv.c: Ditto. * agent/gpg-agent.c: Ditto. * agent/preset-passphrase.c: Ditto. * agent/protect-tool.c: Ditto. * scd/scdaemon.c: Ditto. * dirmngr/dirmngr.c: Ditto. * dirmngr/dirmngr_ldap.c: Ditto * dirmngr/dirmngr-client.c: Ditto. * kbx/kbxutil.c: Ditto. * tools/gpg-card.c: Ditto. * tools/gpg-check-pattern.c: Ditto. * tools/gpg-connect-agent.c: Ditto. * tools/gpg-pair-tool.c: Ditto. * tools/gpg-wks-client.c: Ditto. * tools/gpg-wks-server.c: Ditto. * tools/gpgconf.c: Ditto. * tools/gpgsplit.c: Ditto. * tools/gpgtar.c: Ditto. * g13/g13.c: Ditto. * g13/g13-syshelp.c: Ditto. Do not force verbose mode. * sm/gpgsm.c: Ditto. Add option --no-options. -- This is backport from master commit cdbe10b762f38449b86da69076209324b0c99982 commit ba463128ce65a0f347643f7246a8e097c5be19f1 commit 3bc004decd289810bc1b6ad6fb8f47e45c770ce6 commit 2c823bd878fcdbcc4f6c34993e1d0539d9a6b237 commit 0e8f6e2aa98c212442001036fb5178cd6cd8af59 but without changing all functions names to gpgrt. Instead we use wrapper functions which, when building against old Libgpg-error versions, are implemented in argparse.c using code from the current libgpg-error. This allows to keep the dependency requirement at libgpg-error 1.27 to support older distributions. Tested builds against 1.27 and 1.40-beta. Note that g13-syshelp does not anymore default to --verbose because that can now be enabled in /etc/gnupg/g13-syshelp.conf. GnuPG-bug-id: 4788 Signed-off-by: Werner Koch <wk@gnupg.org>
898 lines
20 KiB
C
898 lines
20 KiB
C
/* gpgsplit.c - An OpenPGP packet splitting tool
|
|
* Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
|
|
*
|
|
* This file is part of GnuPG.
|
|
*
|
|
* GnuPG is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* GnuPG is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <unistd.h>
|
|
#include <assert.h>
|
|
#include <sys/types.h>
|
|
#ifdef HAVE_DOSISH_SYSTEM
|
|
# include <fcntl.h> /* for setmode() */
|
|
#endif
|
|
#ifdef HAVE_ZIP
|
|
# include <zlib.h>
|
|
#endif
|
|
#if defined(__riscos__) && defined(USE_ZLIBRISCOS)
|
|
# include "zlib-riscos.h"
|
|
#endif
|
|
|
|
#define INCLUDED_BY_MAIN_MODULE 1
|
|
#include "../common/util.h"
|
|
#include "../common/openpgpdefs.h"
|
|
|
|
#ifdef HAVE_BZIP2
|
|
# include <bzlib.h>
|
|
#endif /* HAVE_BZIP2 */
|
|
|
|
static int opt_verbose;
|
|
static const char *opt_prefix = "";
|
|
static int opt_uncompress;
|
|
static int opt_secret_to_public;
|
|
static int opt_no_split;
|
|
|
|
static void g10_exit( int rc );
|
|
static void split_packets (const char *fname);
|
|
|
|
|
|
enum cmd_and_opt_values {
|
|
aNull = 0,
|
|
oVerbose = 'v',
|
|
oPrefix = 'p',
|
|
oUncompress = 500,
|
|
oSecretToPublic,
|
|
oNoSplit,
|
|
|
|
aTest
|
|
};
|
|
|
|
|
|
static ARGPARSE_OPTS opts[] = {
|
|
|
|
{ 301, NULL, 0, "@Options:\n " },
|
|
|
|
{ oVerbose, "verbose", 0, "verbose" },
|
|
{ oPrefix, "prefix", 2, "|STRING|Prepend filenames with STRING" },
|
|
{ oUncompress, "uncompress", 0, "uncompress a packet"},
|
|
{ oSecretToPublic, "secret-to-public", 0, "convert secret keys to public keys"},
|
|
{ oNoSplit, "no-split", 0, "write to stdout and don't actually split"},
|
|
|
|
ARGPARSE_end ()
|
|
};
|
|
|
|
|
|
static const char *
|
|
my_strusage (int level)
|
|
{
|
|
const char *p;
|
|
switch (level)
|
|
{
|
|
case 9: p = "GPL-3.0-or-later"; break;
|
|
case 11: p = "gpgsplit (@GNUPG@)";
|
|
break;
|
|
case 13: p = VERSION; break;
|
|
case 14: p = GNUPG_DEF_COPYRIGHT_LINE; break;
|
|
case 17: p = PRINTABLE_OS_NAME; break;
|
|
case 19: p = "Please report bugs to <@EMAIL@>.\n"; break;
|
|
|
|
case 1:
|
|
case 40: p =
|
|
"Usage: gpgsplit [options] [files] (-h for help)";
|
|
break;
|
|
case 41: p =
|
|
"Syntax: gpgsplit [options] [files]\n"
|
|
"Split an OpenPGP message into packets\n";
|
|
break;
|
|
|
|
default: p = NULL;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
|
|
|
|
int
|
|
main (int argc, char **argv)
|
|
{
|
|
ARGPARSE_ARGS pargs;
|
|
|
|
#ifdef HAVE_DOSISH_SYSTEM
|
|
setmode( fileno(stdin), O_BINARY );
|
|
setmode( fileno(stdout), O_BINARY );
|
|
#endif
|
|
log_set_prefix ("gpgsplit", GPGRT_LOG_WITH_PREFIX);
|
|
set_strusage (my_strusage);
|
|
|
|
pargs.argc = &argc;
|
|
pargs.argv = &argv;
|
|
pargs.flags= ARGPARSE_FLAG_KEEP;
|
|
while (gnupg_argparse (NULL, &pargs, opts))
|
|
{
|
|
switch (pargs.r_opt)
|
|
{
|
|
case oVerbose: opt_verbose = 1; break;
|
|
case oPrefix: opt_prefix = pargs.r.ret_str; break;
|
|
case oUncompress: opt_uncompress = 1; break;
|
|
case oSecretToPublic: opt_secret_to_public = 1; break;
|
|
case oNoSplit: opt_no_split = 1; break;
|
|
default : pargs.err = ARGPARSE_PRINT_ERROR; break;
|
|
}
|
|
}
|
|
gnupg_argparse (NULL, &pargs, NULL); /* Release internal state. */
|
|
|
|
if (log_get_errorcount(0))
|
|
g10_exit (2);
|
|
|
|
if (!argc)
|
|
split_packets (NULL);
|
|
else
|
|
{
|
|
for ( ;argc; argc--, argv++)
|
|
split_packets (*argv);
|
|
}
|
|
|
|
g10_exit (0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void
|
|
g10_exit (int rc)
|
|
{
|
|
rc = rc? rc : log_get_errorcount(0)? 2 : 0;
|
|
exit(rc );
|
|
}
|
|
|
|
static const char *
|
|
pkttype_to_string (int pkttype)
|
|
{
|
|
const char *s;
|
|
|
|
switch (pkttype)
|
|
{
|
|
case PKT_PUBKEY_ENC : s = "pk_enc"; break;
|
|
case PKT_SIGNATURE : s = "sig"; break;
|
|
case PKT_SYMKEY_ENC : s = "sym_enc"; break;
|
|
case PKT_ONEPASS_SIG : s = "onepass_sig"; break;
|
|
case PKT_SECRET_KEY : s = "secret_key"; break;
|
|
case PKT_PUBLIC_KEY : s = "public_key"; break;
|
|
case PKT_SECRET_SUBKEY : s = "secret_subkey"; break;
|
|
case PKT_COMPRESSED :
|
|
s = opt_uncompress? "uncompressed":"compressed";
|
|
break;
|
|
case PKT_ENCRYPTED : s = "encrypted"; break;
|
|
case PKT_MARKER : s = "marker"; break;
|
|
case PKT_PLAINTEXT : s = "plaintext"; break;
|
|
case PKT_RING_TRUST : s = "ring_trust"; break;
|
|
case PKT_USER_ID : s = "user_id"; break;
|
|
case PKT_PUBLIC_SUBKEY : s = "public_subkey"; break;
|
|
case PKT_OLD_COMMENT : s = "old_comment"; break;
|
|
case PKT_ATTRIBUTE : s = "attribute"; break;
|
|
case PKT_ENCRYPTED_MDC : s = "encrypted_mdc"; break;
|
|
case PKT_MDC : s = "mdc"; break;
|
|
case PKT_COMMENT : s = "comment"; break;
|
|
case PKT_GPG_CONTROL : s = "gpg_control"; break;
|
|
default: s = "unknown"; break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
|
|
/*
|
|
* Create a new filename and a return a pointer to a statically
|
|
* allocated buffer
|
|
*/
|
|
static char *
|
|
create_filename (int pkttype)
|
|
{
|
|
static unsigned int partno = 0;
|
|
static char *name;
|
|
|
|
if (!name)
|
|
name = xmalloc (strlen (opt_prefix) + 100 );
|
|
|
|
assert (pkttype < 1000 && pkttype >= 0 );
|
|
partno++;
|
|
sprintf (name, "%s%06u-%03d" EXTSEP_S "%.40s",
|
|
opt_prefix, partno, pkttype, pkttype_to_string (pkttype));
|
|
return name;
|
|
}
|
|
|
|
static int
|
|
read_u16 (FILE *fp, size_t *rn)
|
|
{
|
|
int c;
|
|
|
|
if ( (c = getc (fp)) == EOF )
|
|
return -1;
|
|
*rn = c << 8;
|
|
if ( (c = getc (fp)) == EOF )
|
|
return -1;
|
|
*rn |= c;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
read_u32 (FILE *fp, unsigned long *rn)
|
|
{
|
|
size_t tmp;
|
|
|
|
if (read_u16 (fp, &tmp))
|
|
return -1;
|
|
*rn = tmp << 16;
|
|
if (read_u16 (fp, &tmp))
|
|
return -1;
|
|
*rn |= tmp;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
write_old_header (FILE *fp, int pkttype, unsigned int len)
|
|
{
|
|
int ctb = (0x80 | ((pkttype & 15)<<2));
|
|
|
|
if (len < 256)
|
|
;
|
|
else if (len < 65536)
|
|
ctb |= 1;
|
|
else
|
|
ctb |= 2;
|
|
|
|
if ( putc ( ctb, fp) == EOF )
|
|
return -1;
|
|
|
|
if ( (ctb & 2) )
|
|
{
|
|
if (putc ((len>>24), fp) == EOF)
|
|
return -1;
|
|
if (putc ((len>>16), fp) == EOF)
|
|
return -1;
|
|
}
|
|
if ( (ctb & 3) )
|
|
{
|
|
if (putc ((len>>8), fp) == EOF)
|
|
return -1;
|
|
}
|
|
if (putc ((len&0xff), fp) == EOF)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
write_new_header (FILE *fp, int pkttype, unsigned int len)
|
|
{
|
|
if ( putc ((0xc0 | (pkttype & 0x3f)), fp) == EOF )
|
|
return -1;
|
|
|
|
if (len < 192)
|
|
{
|
|
if (putc (len, fp) == EOF)
|
|
return -1;
|
|
}
|
|
else if (len < 8384)
|
|
{
|
|
len -= 192;
|
|
if (putc ((len/256)+192, fp) == EOF)
|
|
return -1;
|
|
if (putc ((len%256), fp) == EOF)
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
if (putc ( 0xff, fp) == EOF)
|
|
return -1;
|
|
if (putc ( (len >> 24), fp) == EOF)
|
|
return -1;
|
|
if (putc ( (len >> 16), fp) == EOF)
|
|
return -1;
|
|
if (putc ( (len >> 8), fp) == EOF)
|
|
return -1;
|
|
if (putc ( (len & 0xff), fp) == EOF)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Return the length of the public key given BUF of BUFLEN with a
|
|
secret key. */
|
|
static int
|
|
public_key_length (const unsigned char *buf, size_t buflen)
|
|
{
|
|
const unsigned char *s;
|
|
int nmpis;
|
|
|
|
/* byte version number (3 or 4)
|
|
u32 creation time
|
|
[u16 valid days (version 3 only)]
|
|
byte algorithm
|
|
n MPIs (n and e) */
|
|
if (!buflen)
|
|
return 0;
|
|
if (buf[0] < 2 || buf[0] > 4)
|
|
return 0; /* wrong version number */
|
|
if (buflen < (buf[0] == 4? 6:8))
|
|
return 0;
|
|
s = buf + (buf[0] == 4? 6:8);
|
|
buflen -= (buf[0] == 4? 6:8);
|
|
switch (s[-1])
|
|
{
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
nmpis = 2;
|
|
break;
|
|
case 16:
|
|
case 20:
|
|
nmpis = 3;
|
|
break;
|
|
case 17:
|
|
nmpis = 4;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
for (; nmpis; nmpis--)
|
|
{
|
|
unsigned int nbits, nbytes;
|
|
|
|
if (buflen < 2)
|
|
return 0;
|
|
nbits = (s[0] << 8) | s[1];
|
|
s += 2; buflen -= 2;
|
|
nbytes = (nbits+7) / 8;
|
|
if (buflen < nbytes)
|
|
return 0;
|
|
s += nbytes; buflen -= nbytes;
|
|
}
|
|
|
|
return s - buf;
|
|
}
|
|
|
|
#ifdef HAVE_ZIP
|
|
static int
|
|
handle_zlib(int algo,FILE *fpin,FILE *fpout)
|
|
{
|
|
z_stream zs;
|
|
byte *inbuf, *outbuf;
|
|
unsigned int inbufsize, outbufsize;
|
|
int c,zinit_done, zrc, nread, count;
|
|
size_t n;
|
|
|
|
memset (&zs, 0, sizeof zs);
|
|
inbufsize = 2048;
|
|
inbuf = xmalloc (inbufsize);
|
|
outbufsize = 8192;
|
|
outbuf = xmalloc (outbufsize);
|
|
zs.avail_in = 0;
|
|
zinit_done = 0;
|
|
|
|
do
|
|
{
|
|
if (zs.avail_in < inbufsize)
|
|
{
|
|
n = zs.avail_in;
|
|
if (!n)
|
|
zs.next_in = (Bytef *) inbuf;
|
|
count = inbufsize - n;
|
|
for (nread=0;
|
|
nread < count && (c=getc (fpin)) != EOF;
|
|
nread++)
|
|
inbuf[n+nread] = c;
|
|
|
|
n += nread;
|
|
if (nread < count && algo == 1)
|
|
{
|
|
inbuf[n] = 0xFF; /* chew dummy byte */
|
|
n++;
|
|
}
|
|
zs.avail_in = n;
|
|
}
|
|
zs.next_out = (Bytef *) outbuf;
|
|
zs.avail_out = outbufsize;
|
|
|
|
if (!zinit_done)
|
|
{
|
|
zrc = (algo == 1? inflateInit2 ( &zs, -13)
|
|
: inflateInit ( &zs ));
|
|
if (zrc != Z_OK)
|
|
{
|
|
log_fatal ("zlib problem: %s\n", zs.msg? zs.msg :
|
|
zrc == Z_MEM_ERROR ? "out of core" :
|
|
zrc == Z_VERSION_ERROR ?
|
|
"invalid lib version" :
|
|
"unknown error" );
|
|
}
|
|
zinit_done = 1;
|
|
}
|
|
else
|
|
{
|
|
#ifdef Z_SYNC_FLUSH
|
|
zrc = inflate (&zs, Z_SYNC_FLUSH);
|
|
#else
|
|
zrc = inflate (&zs, Z_PARTIAL_FLUSH);
|
|
#endif
|
|
if (zrc == Z_STREAM_END)
|
|
; /* eof */
|
|
else if (zrc != Z_OK && zrc != Z_BUF_ERROR)
|
|
{
|
|
if (zs.msg)
|
|
log_fatal ("zlib inflate problem: %s\n", zs.msg );
|
|
else
|
|
log_fatal ("zlib inflate problem: rc=%d\n", zrc );
|
|
}
|
|
for (n=0; n < outbufsize - zs.avail_out; n++)
|
|
{
|
|
if (putc (outbuf[n], fpout) == EOF )
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
while (zrc != Z_STREAM_END && zrc != Z_BUF_ERROR);
|
|
{
|
|
int i;
|
|
|
|
fputs ("Left over bytes:", stderr);
|
|
for (i=0; i < zs.avail_in; i++)
|
|
fprintf (stderr, " %02X", zs.next_in[i]);
|
|
putc ('\n', stderr);
|
|
|
|
}
|
|
inflateEnd (&zs);
|
|
|
|
return 0;
|
|
}
|
|
#endif /*HAVE_ZIP*/
|
|
|
|
#ifdef HAVE_BZIP2
|
|
static int
|
|
handle_bzip2(int algo,FILE *fpin,FILE *fpout)
|
|
{
|
|
bz_stream bzs;
|
|
byte *inbuf, *outbuf;
|
|
unsigned int inbufsize, outbufsize;
|
|
int c,zinit_done, zrc, nread, count;
|
|
size_t n;
|
|
|
|
memset (&bzs, 0, sizeof bzs);
|
|
inbufsize = 2048;
|
|
inbuf = xmalloc (inbufsize);
|
|
outbufsize = 8192;
|
|
outbuf = xmalloc (outbufsize);
|
|
bzs.avail_in = 0;
|
|
zinit_done = 0;
|
|
|
|
do
|
|
{
|
|
if (bzs.avail_in < inbufsize)
|
|
{
|
|
n = bzs.avail_in;
|
|
if (!n)
|
|
bzs.next_in = inbuf;
|
|
count = inbufsize - n;
|
|
for (nread=0;
|
|
nread < count && (c=getc (fpin)) != EOF;
|
|
nread++)
|
|
inbuf[n+nread] = c;
|
|
|
|
n += nread;
|
|
if (nread < count && algo == 1)
|
|
{
|
|
inbuf[n] = 0xFF; /* chew dummy byte */
|
|
n++;
|
|
}
|
|
bzs.avail_in = n;
|
|
}
|
|
bzs.next_out = outbuf;
|
|
bzs.avail_out = outbufsize;
|
|
|
|
if (!zinit_done)
|
|
{
|
|
zrc = BZ2_bzDecompressInit(&bzs,0,0);
|
|
if (zrc != BZ_OK)
|
|
log_fatal ("bz2lib problem: %d\n",zrc);
|
|
zinit_done = 1;
|
|
}
|
|
else
|
|
{
|
|
zrc = BZ2_bzDecompress(&bzs);
|
|
if (zrc == BZ_STREAM_END)
|
|
; /* eof */
|
|
else if (zrc != BZ_OK && zrc != BZ_PARAM_ERROR)
|
|
log_fatal ("bz2lib inflate problem: %d\n", zrc );
|
|
for (n=0; n < outbufsize - bzs.avail_out; n++)
|
|
{
|
|
if (putc (outbuf[n], fpout) == EOF )
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
while (zrc != BZ_STREAM_END && zrc != BZ_PARAM_ERROR);
|
|
BZ2_bzDecompressEnd(&bzs);
|
|
|
|
return 0;
|
|
}
|
|
#endif /* HAVE_BZIP2 */
|
|
|
|
/* hdr must point to a buffer large enough to hold all header bytes */
|
|
static int
|
|
write_part (FILE *fpin, unsigned long pktlen,
|
|
int pkttype, int partial, unsigned char *hdr, size_t hdrlen)
|
|
{
|
|
FILE *fpout;
|
|
int c, first;
|
|
unsigned char *p;
|
|
const char *outname = create_filename (pkttype);
|
|
|
|
#if defined(__riscos__) && defined(USE_ZLIBRISCOS)
|
|
static int initialized = 0;
|
|
|
|
if (!initialized)
|
|
initialized = riscos_load_module("ZLib", zlib_path, 1);
|
|
#endif
|
|
if (opt_no_split)
|
|
fpout = stdout;
|
|
else
|
|
{
|
|
if (opt_verbose)
|
|
log_info ("writing '%s'\n", outname);
|
|
fpout = gnupg_fopen (outname, "wb");
|
|
if (!fpout)
|
|
{
|
|
log_error ("error creating '%s': %s\n", outname, strerror(errno));
|
|
/* stop right now, otherwise we would mess up the sequence
|
|
of the part numbers */
|
|
g10_exit (1);
|
|
}
|
|
}
|
|
|
|
if (opt_secret_to_public
|
|
&& (pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY))
|
|
{
|
|
unsigned char *blob = xmalloc (pktlen);
|
|
int i, len;
|
|
|
|
pkttype = pkttype == PKT_SECRET_KEY? PKT_PUBLIC_KEY:PKT_PUBLIC_SUBKEY;
|
|
|
|
for (i=0; i < pktlen; i++)
|
|
{
|
|
c = getc (fpin);
|
|
if (c == EOF)
|
|
goto read_error;
|
|
blob[i] = c;
|
|
}
|
|
len = public_key_length (blob, pktlen);
|
|
if (!len)
|
|
{
|
|
log_error ("error calculating public key length\n");
|
|
g10_exit (1);
|
|
}
|
|
if ( (hdr[0] & 0x40) )
|
|
{
|
|
if (write_new_header (fpout, pkttype, len))
|
|
goto write_error;
|
|
}
|
|
else
|
|
{
|
|
if (write_old_header (fpout, pkttype, len))
|
|
goto write_error;
|
|
}
|
|
|
|
for (i=0; i < len; i++)
|
|
{
|
|
if ( putc (blob[i], fpout) == EOF )
|
|
goto write_error;
|
|
}
|
|
|
|
goto ready;
|
|
}
|
|
|
|
|
|
if (!opt_uncompress)
|
|
{
|
|
for (p=hdr; hdrlen; p++, hdrlen--)
|
|
{
|
|
if ( putc (*p, fpout) == EOF )
|
|
goto write_error;
|
|
}
|
|
}
|
|
|
|
first = 1;
|
|
while (partial)
|
|
{
|
|
size_t partlen;
|
|
|
|
if (partial == 1)
|
|
{ /* openpgp */
|
|
if (first )
|
|
{
|
|
c = pktlen;
|
|
assert( c >= 224 && c < 255 );
|
|
first = 0;
|
|
}
|
|
else if ((c = getc (fpin)) == EOF )
|
|
goto read_error;
|
|
else
|
|
hdr[hdrlen++] = c;
|
|
|
|
if (c < 192)
|
|
{
|
|
pktlen = c;
|
|
partial = 0; /* (last segment may follow) */
|
|
}
|
|
else if (c < 224 )
|
|
{
|
|
pktlen = (c - 192) * 256;
|
|
if ((c = getc (fpin)) == EOF)
|
|
goto read_error;
|
|
hdr[hdrlen++] = c;
|
|
pktlen += c + 192;
|
|
partial = 0;
|
|
}
|
|
else if (c == 255)
|
|
{
|
|
if (read_u32 (fpin, &pktlen))
|
|
goto read_error;
|
|
hdr[hdrlen++] = pktlen >> 24;
|
|
hdr[hdrlen++] = pktlen >> 16;
|
|
hdr[hdrlen++] = pktlen >> 8;
|
|
hdr[hdrlen++] = pktlen;
|
|
partial = 0;
|
|
}
|
|
else
|
|
{ /* next partial body length */
|
|
for (p=hdr; hdrlen; p++, hdrlen--)
|
|
{
|
|
if ( putc (*p, fpout) == EOF )
|
|
goto write_error;
|
|
}
|
|
partlen = 1 << (c & 0x1f);
|
|
for (; partlen; partlen--)
|
|
{
|
|
if ((c = getc (fpin)) == EOF)
|
|
goto read_error;
|
|
if ( putc (c, fpout) == EOF )
|
|
goto write_error;
|
|
}
|
|
}
|
|
}
|
|
else if (partial == 2)
|
|
{ /* old gnupg */
|
|
assert (!pktlen);
|
|
if ( read_u16 (fpin, &partlen) )
|
|
goto read_error;
|
|
hdr[hdrlen++] = partlen >> 8;
|
|
hdr[hdrlen++] = partlen;
|
|
for (p=hdr; hdrlen; p++, hdrlen--)
|
|
{
|
|
if ( putc (*p, fpout) == EOF )
|
|
goto write_error;
|
|
}
|
|
if (!partlen)
|
|
partial = 0; /* end of packet */
|
|
for (; partlen; partlen--)
|
|
{
|
|
c = getc (fpin);
|
|
if (c == EOF)
|
|
goto read_error;
|
|
if ( putc (c, fpout) == EOF )
|
|
goto write_error;
|
|
}
|
|
}
|
|
else
|
|
{ /* compressed: read to end */
|
|
pktlen = 0;
|
|
partial = 0;
|
|
hdrlen = 0;
|
|
if (opt_uncompress)
|
|
{
|
|
if ((c = getc (fpin)) == EOF)
|
|
goto read_error;
|
|
|
|
if (0)
|
|
;
|
|
#ifdef HAVE_ZIP
|
|
else if(c==1 || c==2)
|
|
{
|
|
if(handle_zlib(c,fpin,fpout))
|
|
goto write_error;
|
|
}
|
|
#endif /* HAVE_ZIP */
|
|
#ifdef HAVE_BZIP2
|
|
else if(c==3)
|
|
{
|
|
if(handle_bzip2(c,fpin,fpout))
|
|
goto write_error;
|
|
}
|
|
#endif /* HAVE_BZIP2 */
|
|
else
|
|
{
|
|
log_error("invalid compression algorithm (%d)\n",c);
|
|
goto read_error;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while ( (c=getc (fpin)) != EOF )
|
|
{
|
|
if ( putc (c, fpout) == EOF )
|
|
goto write_error;
|
|
}
|
|
}
|
|
if (!feof (fpin))
|
|
goto read_error;
|
|
}
|
|
}
|
|
|
|
for (p=hdr; hdrlen; p++, hdrlen--)
|
|
{
|
|
if ( putc (*p, fpout) == EOF )
|
|
goto write_error;
|
|
}
|
|
|
|
/* standard packet or last segment of partial length encoded packet */
|
|
for (; pktlen; pktlen--)
|
|
{
|
|
c = getc (fpin);
|
|
if (c == EOF)
|
|
goto read_error;
|
|
if ( putc (c, fpout) == EOF )
|
|
goto write_error;
|
|
}
|
|
|
|
ready:
|
|
if ( !opt_no_split && fclose (fpout) )
|
|
log_error ("error closing '%s': %s\n", outname, strerror (errno));
|
|
return 0;
|
|
|
|
write_error:
|
|
log_error ("error writing '%s': %s\n", outname, strerror (errno));
|
|
if (!opt_no_split)
|
|
fclose (fpout);
|
|
return 2;
|
|
|
|
read_error:
|
|
if (!opt_no_split)
|
|
{
|
|
int save = errno;
|
|
fclose (fpout);
|
|
errno = save;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
do_split (FILE *fp)
|
|
{
|
|
int c, ctb, pkttype;
|
|
unsigned long pktlen = 0;
|
|
int partial = 0;
|
|
unsigned char header[20];
|
|
int header_idx = 0;
|
|
|
|
ctb = getc (fp);
|
|
if (ctb == EOF)
|
|
return 3; /* ready */
|
|
header[header_idx++] = ctb;
|
|
|
|
if (!(ctb & 0x80))
|
|
{
|
|
log_error("invalid CTB %02x\n", ctb );
|
|
return 1;
|
|
}
|
|
if ( (ctb & 0x40) )
|
|
{ /* new CTB */
|
|
pkttype = (ctb & 0x3f);
|
|
if( (c = getc (fp)) == EOF )
|
|
return -1;
|
|
header[header_idx++] = c;
|
|
|
|
if ( c < 192 )
|
|
pktlen = c;
|
|
else if ( c < 224 )
|
|
{
|
|
pktlen = (c - 192) * 256;
|
|
if( (c = getc (fp)) == EOF )
|
|
return -1;
|
|
header[header_idx++] = c;
|
|
pktlen += c + 192;
|
|
}
|
|
else if ( c == 255 )
|
|
{
|
|
if (read_u32 (fp, &pktlen))
|
|
return -1;
|
|
header[header_idx++] = pktlen >> 24;
|
|
header[header_idx++] = pktlen >> 16;
|
|
header[header_idx++] = pktlen >> 8;
|
|
header[header_idx++] = pktlen;
|
|
}
|
|
else
|
|
{ /* partial body length */
|
|
pktlen = c;
|
|
partial = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int lenbytes;
|
|
|
|
pkttype = (ctb>>2)&0xf;
|
|
lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3));
|
|
if (!lenbytes )
|
|
{
|
|
pktlen = 0; /* don't know the value */
|
|
if( pkttype == PKT_COMPRESSED )
|
|
partial = 3;
|
|
else
|
|
partial = 2; /* the old GnuPG partial length encoding */
|
|
}
|
|
else
|
|
{
|
|
for ( ; lenbytes; lenbytes-- )
|
|
{
|
|
pktlen <<= 8;
|
|
if( (c = getc (fp)) == EOF )
|
|
return -1;
|
|
header[header_idx++] = c;
|
|
|
|
pktlen |= c;
|
|
}
|
|
}
|
|
}
|
|
|
|
return write_part (fp, pktlen, pkttype, partial, header, header_idx);
|
|
}
|
|
|
|
|
|
static void
|
|
split_packets (const char *fname)
|
|
{
|
|
FILE *fp;
|
|
int rc;
|
|
|
|
if (!fname || !strcmp (fname, "-"))
|
|
{
|
|
fp = stdin;
|
|
fname = "-";
|
|
}
|
|
else if ( !(fp = gnupg_fopen (fname,"rb")) )
|
|
{
|
|
log_error ("can't open '%s': %s\n", fname, strerror (errno));
|
|
return;
|
|
}
|
|
|
|
while ( !(rc = do_split (fp)) )
|
|
;
|
|
if ( rc > 0 )
|
|
; /* error already handled */
|
|
else if ( ferror (fp) )
|
|
log_error ("error reading '%s': %s\n", fname, strerror (errno));
|
|
else
|
|
log_error ("premature EOF while reading '%s'\n", fname );
|
|
|
|
if ( fp != stdin )
|
|
fclose (fp);
|
|
}
|