diff --git a/g10/armor.c b/g10/armor.c
index be03692f1..1ae3c6062 100644
--- a/g10/armor.c
+++ b/g10/armor.c
@@ -1,1551 +1,1551 @@
/* armor.c - Armor flter
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
* 2007 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 .
*/
#include
#include
#include
#include
#include
#include
#include
#include "errors.h"
#include "iobuf.h"
#include "memory.h"
#include "util.h"
#include "filter.h"
#include "packet.h"
#include "options.h"
#include "main.h"
#include "status.h"
#include "i18n.h"
#define MAX_LINELEN 20000
#define CRCINIT 0xB704CE
#define CRCPOLY 0X864CFB
#define CRCUPDATE(a,c) do { \
a = ((a) << 8) ^ crc_table[((a)&0xff >> 16) ^ (c)]; \
a &= 0x00ffffff; \
} while(0)
static u32 crc_table[256];
static byte bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static byte asctobin[256]; /* runtime initialized */
static int is_initialized;
typedef enum {
fhdrHASArmor = 0,
fhdrNOArmor,
fhdrINIT,
fhdrINITCont,
fhdrINITSkip,
fhdrCHECKBegin,
fhdrWAITHeader,
fhdrWAITClearsig,
fhdrSKIPHeader,
fhdrCLEARSIG,
fhdrREADClearsig,
fhdrNullClearsig,
fhdrEMPTYClearsig,
fhdrCHECKClearsig,
fhdrCHECKClearsig2,
fhdrCHECKDashEscaped,
fhdrCHECKDashEscaped2,
fhdrCHECKDashEscaped3,
fhdrREADClearsigNext,
fhdrENDClearsig,
fhdrENDClearsigHelp,
fhdrTESTSpaces,
fhdrCLEARSIGSimple,
fhdrCLEARSIGSimpleNext,
fhdrTEXT,
fhdrTEXTSimple,
fhdrERROR,
fhdrERRORShow,
fhdrEOF
} fhdr_state_t;
/* if we encounter this armor string with this index, go
* into a mode which fakes packets and wait for the next armor */
#define BEGIN_SIGNATURE 2
#define BEGIN_SIGNED_MSG_IDX 3
static char *head_strings[] = {
"BEGIN PGP MESSAGE",
"BEGIN PGP PUBLIC KEY BLOCK",
"BEGIN PGP SIGNATURE",
"BEGIN PGP SIGNED MESSAGE",
"BEGIN PGP ARMORED FILE", /* gnupg extension */
"BEGIN PGP PRIVATE KEY BLOCK",
"BEGIN PGP SECRET KEY BLOCK", /* only used by pgp2 */
NULL
};
static char *tail_strings[] = {
"END PGP MESSAGE",
"END PGP PUBLIC KEY BLOCK",
"END PGP SIGNATURE",
"END dummy",
"END PGP ARMORED FILE",
"END PGP PRIVATE KEY BLOCK",
"END PGP SECRET KEY BLOCK",
NULL
};
/* Create a new context for armor filters. */
armor_filter_context_t *
new_armor_context (void)
{
armor_filter_context_t *afx;
afx = xcalloc (1, sizeof *afx);
afx->refcount = 1;
return afx;
}
/* Release an armor filter context. Passing NULL is explicitly
allowed and a no-op. */
void
release_armor_context (armor_filter_context_t *afx)
{
if (!afx)
return;
/* In contrast to 2.0, we use in 1.4 heap based contexts only in a
very few places and in general keep the stack based contexts. A
REFCOUNT of 0 indicates a stack based context and thus we don't
do anything in this case. */
if (!afx->refcount)
return;
if ( --afx->refcount )
return;
xfree (afx);
}
/* Push the armor filter onto the iobuf stream IOBUF. */
int
push_armor_filter (armor_filter_context_t *afx, iobuf_t iobuf)
{
int rc;
if (!afx->refcount)
return iobuf_push_filter (iobuf, armor_filter, afx);
afx->refcount++;
rc = iobuf_push_filter (iobuf, armor_filter, afx);
if (rc)
afx->refcount--;
return rc;
}
static void
initialize(void)
{
int i, j;
u32 t;
byte *s;
/* init the crc lookup table */
crc_table[0] = 0;
for(i=j=0; j < 128; j++ ) {
t = crc_table[j];
if( t & 0x00800000 ) {
t <<= 1;
crc_table[i++] = t ^ CRCPOLY;
crc_table[i++] = t;
}
else {
t <<= 1;
crc_table[i++] = t;
crc_table[i++] = t ^ CRCPOLY;
}
}
/* build the helptable for radix64 to bin conversion */
for(i=0; i < 256; i++ )
asctobin[i] = 255; /* used to detect invalid characters */
for(s=bintoasc,i=0; *s; s++,i++ )
asctobin[*s] = i;
is_initialized=1;
}
/****************
* Check whether this is an armored file or not See also
* parse-packet.c for details on this code For unknown historic
* reasons we use a string here but only the first byte will be used.
* Returns: True if it seems to be armored
*/
static int
is_armored( const byte *buf )
{
int ctb, pkttype;
ctb = *buf;
if( !(ctb & 0x80) )
return 1; /* invalid packet: assume it is armored */
pkttype = ctb & 0x40 ? (ctb & 0x3f) : ((ctb>>2)&0xf);
switch( pkttype ) {
case PKT_MARKER:
case PKT_SYMKEY_ENC:
case PKT_ONEPASS_SIG:
case PKT_PUBLIC_KEY:
case PKT_SECRET_KEY:
case PKT_PUBKEY_ENC:
case PKT_SIGNATURE:
case PKT_COMMENT:
case PKT_OLD_COMMENT:
case PKT_PLAINTEXT:
case PKT_COMPRESSED:
case PKT_ENCRYPTED:
return 0; /* seems to be a regular packet: not armored */
}
return 1;
}
/****************
* Try to check whether the iobuf is armored
* Returns true if this may be the case; the caller should use the
* filter to do further processing.
*/
int
use_armor_filter( IOBUF a )
{
byte buf[1];
int n;
/* fixme: there might be a problem with iobuf_peek */
n = iobuf_peek(a, buf, 1 );
if( n == -1 )
return 0; /* EOF, doesn't matter whether armored or not */
if( !n )
return 1; /* can't check it: try armored */
return is_armored(buf);
}
static void
invalid_armor(void)
{
write_status(STATUS_BADARMOR);
g10_exit(1); /* stop here */
}
/****************
* check whether the armor header is valid on a signed message.
* this is for security reasons: the header lines are not included in the
* hash and by using some creative formatting rules, Mallory could fake
* any text at the beginning of a document; assuming it is read with
* a simple viewer. We only allow the Hash Header.
*/
static int
parse_hash_header( const char *line )
{
const char *s, *s2;
unsigned found = 0;
if( strlen(line) < 6 || strlen(line) > 60 )
return 0; /* too short or too long */
if( memcmp( line, "Hash:", 5 ) )
return 0; /* invalid header */
s = line+5;
for(s=line+5;;s=s2) {
for(; *s && (*s==' ' || *s == '\t'); s++ )
;
if( !*s )
break;
for(s2=s+1; *s2 && *s2!=' ' && *s2 != '\t' && *s2 != ','; s2++ )
;
if( !strncmp( s, "RIPEMD160", s2-s ) )
found |= 1;
else if( !strncmp( s, "SHA1", s2-s ) )
found |= 2;
else if( !strncmp( s, "MD5", s2-s ) )
found |= 4;
else if( !strncmp( s, "SHA224", s2-s ) )
found |= 8;
else if( !strncmp( s, "SHA256", s2-s ) )
found |= 16;
else if( !strncmp( s, "SHA384", s2-s ) )
found |= 32;
else if( !strncmp( s, "SHA512", s2-s ) )
found |= 64;
else
return 0;
for(; *s2 && (*s2==' ' || *s2 == '\t'); s2++ )
;
if( *s2 && *s2 != ',' )
return 0;
if( *s2 )
s2++;
}
return found;
}
/* Returns true if this is a valid armor tag as per RFC-2440bis-21. */
static int
is_armor_tag(const char *line)
{
if(strncmp(line,"Version",7)==0
|| strncmp(line,"Comment",7)==0
|| strncmp(line,"MessageID",9)==0
|| strncmp(line,"Hash",4)==0
|| strncmp(line,"Charset",7)==0)
return 1;
return 0;
}
/****************
* Check whether this is a armor line.
* returns: -1 if it is not a armor header or the index number of the
* armor header.
*/
static int
is_armor_header( byte *line, unsigned len )
{
const char *s;
byte *save_p, *p;
int save_c;
int i;
if( len < 15 )
return -1; /* too short */
if( memcmp( line, "-----", 5 ) )
return -1; /* no */
p = strstr( line+5, "-----");
if( !p )
return -1;
save_p = p;
p += 5;
/* Some Windows environments seem to add whitespace to the end of
the line, so we strip it here. This becomes strict if
--rfc2440 is set since 2440 reads "The header lines, therefore,
MUST start at the beginning of a line, and MUST NOT have text
following them on the same line." It is unclear whether "text"
refers to all text or just non-whitespace text. 4880 clarified
this was only non-whitespace text. */
if(RFC2440)
{
if( *p == '\r' )
p++;
if( *p == '\n' )
p++;
}
else
while(*p==' ' || *p=='\r' || *p=='\n' || *p=='\t')
p++;
if( *p )
return -1; /* garbage after dashes */
save_c = *save_p; *save_p = 0;
p = line+5;
for(i=0; (s=head_strings[i]); i++ )
if( !strcmp(s, p) )
break;
*save_p = save_c;
if( !s )
return -1; /* unknown armor line */
if( opt.verbose > 1 )
log_info(_("armor: %s\n"), head_strings[i]);
return i;
}
/* Helper to parse a "KEY FAILED " line and return the
error code. LINEPTR points right behind "KEY ". */
int
parse_key_failed_line (const void *lineptr, unsigned int len)
{
const byte *line = lineptr;
int code = 0;
for (; len && !spacep (line); len--, line++)
;
for (; len && spacep (line); len--, line++)
;
if (len > 7 && !memcmp (line, "FAILED ", 7))
{
line += 7;
len -= 7;
for (; len && digitp (line); len--, line++)
{
code *= 10;
code += atoi_1 (line);
}
}
return code;
}
/****************
* Parse a header lines
* Return 0: Empty line (end of header lines)
* -1: invalid header line
* >0: Good header line
*/
static int
parse_header_line( armor_filter_context_t *afx, byte *line, unsigned int len )
{
byte *p;
int hashes=0;
unsigned int len2;
len2 = check_trailing_ws( line, len );
if( !len2 ) {
afx->buffer_pos = len2; /* (it is not the fine way to do it here) */
return 0; /* WS only: same as empty line */
}
/*
This is fussy. The spec says that a header line is delimited
with a colon-space pair. This means that a line such as
"Comment: " (with nothing else) is actually legal as an empty
string comment. However, email and cut-and-paste being what it
is, that trailing space may go away. Therefore, we accept empty
headers delimited with only a colon. --rfc2440, as always,
makes this strict and enforces the colon-space pair. -dms
*/
p = strchr( line, ':');
if( !p || (RFC2440 && p[1]!=' ')
|| (!RFC2440 && p[1]!=' ' && p[1]!='\n' && p[1]!='\r'))
{
log_error(_("invalid armor header: "));
print_string( stderr, line, len, 0 );
putc('\n', stderr);
return -1;
}
/* Chop off the whitespace we detected before */
len=len2;
line[len2]='\0';
if( opt.verbose ) {
log_info(_("armor header: "));
print_string( stderr, line, len, 0 );
putc('\n', stderr);
}
if( afx->in_cleartext )
{
if( (hashes=parse_hash_header( line )) )
afx->hashes |= hashes;
else if( strlen(line) > 15 && !memcmp( line, "NotDashEscaped:", 15 ) )
afx->not_dash_escaped = 1;
else
{
log_error(_("invalid clearsig header\n"));
return -1;
}
}
else if(!is_armor_tag(line))
{
/* Section 6.2: "Unknown keys should be reported to the user,
but OpenPGP should continue to process the message." Note
that in a clearsigned message this applies to the signature
part (i.e. "BEGIN PGP SIGNATURE") and not the signed data
("BEGIN PGP SIGNED MESSAGE"). The only key allowed in the
signed data section is "Hash". */
log_info(_("unknown armor header: "));
print_string( stderr, line, len, 0 );
putc('\n', stderr);
}
return 1;
}
/* figure out whether the data is armored or not */
static int
check_input( armor_filter_context_t *afx, IOBUF a )
{
int rc = 0;
int i;
byte *line;
unsigned len;
unsigned maxlen;
int hdr_line = -1;
/* read the first line to see whether this is armored data */
maxlen = MAX_LINELEN;
len = afx->buffer_len = iobuf_read_line( a, &afx->buffer,
&afx->buffer_size, &maxlen );
line = afx->buffer;
if( !maxlen ) {
/* line has been truncated: assume not armored */
afx->inp_checked = 1;
afx->inp_bypass = 1;
return 0;
}
if( !len ) {
return -1; /* eof */
}
/* (the line is always a C string but maybe longer) */
if( *line == '\n' || ( len && (*line == '\r' && line[1]=='\n') ) )
;
else if( !is_armored( line ) ) {
afx->inp_checked = 1;
afx->inp_bypass = 1;
return 0;
}
/* find the armor header */
while(len) {
i = is_armor_header( line, len );
if (i == -1 && afx->only_keyblocks
&& !afx->key_failed_code
&& len > 4 && !memcmp (line, "KEY ", 4))
{
/* This is probably input from a keyserver helper and we
have not yet seen an error line. */
afx->key_failed_code = parse_key_failed_line (line+4, len-4);
}
if( i >= 0 && !(afx->only_keyblocks && i != 1 && i != 5 && i != 6 )) {
hdr_line = i;
if( hdr_line == BEGIN_SIGNED_MSG_IDX ) {
if( afx->in_cleartext ) {
log_error(_("nested clear text signatures\n"));
rc = G10ERR_INVALID_ARMOR;
}
afx->in_cleartext = 1;
}
break;
}
/* read the next line (skip all truncated lines) */
do {
maxlen = MAX_LINELEN;
afx->buffer_len = iobuf_read_line( a, &afx->buffer,
&afx->buffer_size, &maxlen );
line = afx->buffer;
len = afx->buffer_len;
} while( !maxlen );
}
/* Parse the header lines. */
while(len) {
/* Read the next line (skip all truncated lines). */
do {
maxlen = MAX_LINELEN;
afx->buffer_len = iobuf_read_line( a, &afx->buffer,
&afx->buffer_size, &maxlen );
line = afx->buffer;
len = afx->buffer_len;
} while( !maxlen );
i = parse_header_line( afx, line, len );
if( i <= 0 ) {
if (i && RFC2440)
rc = G10ERR_INVALID_ARMOR;
break;
}
}
if( rc )
invalid_armor();
else if( afx->in_cleartext )
afx->faked = 1;
else {
afx->inp_checked = 1;
afx->crc = CRCINIT;
afx->idx = 0;
afx->radbuf[0] = 0;
}
return rc;
}
#define PARTIAL_CHUNK 512
#define PARTIAL_POW 9
/****************
* Fake a literal data packet and wait for the next armor line
* fixme: empty line handling and null length clear text signature are
* not implemented/checked.
*/
static int
fake_packet( armor_filter_context_t *afx, IOBUF a,
size_t *retn, byte *buf, size_t size )
{
int rc = 0;
size_t len = 0;
int lastline = 0;
unsigned maxlen, n;
byte *p;
byte tempbuf[PARTIAL_CHUNK];
size_t tempbuf_len=0;
while( !rc && size-len>=(PARTIAL_CHUNK+1)) {
/* copy what we have in the line buffer */
if( afx->faked == 1 )
afx->faked++; /* skip the first (empty) line */
else
{
/* It's full, so write this partial chunk */
if(tempbuf_len==PARTIAL_CHUNK)
{
buf[len++]=0xE0+PARTIAL_POW;
memcpy(&buf[len],tempbuf,PARTIAL_CHUNK);
len+=PARTIAL_CHUNK;
tempbuf_len=0;
continue;
}
while( tempbuf_len < PARTIAL_CHUNK
&& afx->buffer_pos < afx->buffer_len )
tempbuf[tempbuf_len++] = afx->buffer[afx->buffer_pos++];
if( tempbuf_len==PARTIAL_CHUNK )
continue;
}
/* read the next line */
maxlen = MAX_LINELEN;
afx->buffer_pos = 0;
afx->buffer_len = iobuf_read_line( a, &afx->buffer,
&afx->buffer_size, &maxlen );
if( !afx->buffer_len ) {
rc = -1; /* eof (should not happen) */
continue;
}
if( !maxlen )
afx->truncated++;
p = afx->buffer;
n = afx->buffer_len;
/* Armor header or dash-escaped line? */
if(p[0]=='-')
{
/* 2440bis-10: When reversing dash-escaping, an
implementation MUST strip the string "- " if it occurs
at the beginning of a line, and SHOULD warn on "-" and
any character other than a space at the beginning of a
line. */
if(p[1]==' ' && !afx->not_dash_escaped)
{
/* It's a dash-escaped line, so skip over the
escape. */
afx->buffer_pos = 2;
}
else if(p[1]=='-' && p[2]=='-' && p[3]=='-' && p[4]=='-')
{
/* Five dashes in a row mean it's probably armor
header. */
int type = is_armor_header( p, n );
if( afx->not_dash_escaped && type != BEGIN_SIGNATURE )
; /* this is okay */
else
{
if( type != BEGIN_SIGNATURE )
{
log_info(_("unexpected armor: "));
print_string( stderr, p, n, 0 );
putc('\n', stderr);
}
lastline = 1;
rc = -1;
}
}
else if(!afx->not_dash_escaped)
{
/* Bad dash-escaping. */
log_info(_("invalid dash escaped line: "));
print_string( stderr, p, n, 0 );
putc('\n', stderr);
}
}
/* Now handle the end-of-line canonicalization */
if( !afx->not_dash_escaped )
{
int crlf = n > 1 && p[n-2] == '\r' && p[n-1]=='\n';
/* PGP2 does not treat a tab as white space character */
afx->buffer_len=
trim_trailing_chars( &p[afx->buffer_pos], n-afx->buffer_pos,
afx->pgp2mode ? " \r\n" : " \t\r\n");
afx->buffer_len+=afx->buffer_pos;
/* the buffer is always allocated with enough space to append
* the removed [CR], LF and a Nul
* The reason for this complicated procedure is to keep at least
* the original type of lineending - handling of the removed
* trailing spaces seems to be impossible in our method
* of faking a packet; either we have to use a temporary file
* or calculate the hash here in this module and somehow find
* a way to send the hash down the processing line (well, a special
* faked packet could do the job).
*/
if( crlf )
afx->buffer[afx->buffer_len++] = '\r';
afx->buffer[afx->buffer_len++] = '\n';
afx->buffer[afx->buffer_len] = '\0';
}
}
if( lastline ) { /* write last (ending) length header */
if(tempbuf_len<192)
buf[len++]=tempbuf_len;
else
{
buf[len++]=((tempbuf_len-192)/256) + 192;
buf[len++]=(tempbuf_len-192) % 256;
}
memcpy(&buf[len],tempbuf,tempbuf_len);
len+=tempbuf_len;
rc = 0;
afx->faked = 0;
afx->in_cleartext = 0;
/* and now read the header lines */
afx->buffer_pos = 0;
for(;;) {
int i;
/* read the next line (skip all truncated lines) */
do {
maxlen = MAX_LINELEN;
afx->buffer_len = iobuf_read_line( a, &afx->buffer,
&afx->buffer_size, &maxlen );
} while( !maxlen );
p = afx->buffer;
n = afx->buffer_len;
if( !n ) {
rc = -1;
break; /* eof */
}
i = parse_header_line( afx, p , n );
if( i <= 0 ) {
if( i )
invalid_armor();
break;
}
}
afx->inp_checked = 1;
afx->crc = CRCINIT;
afx->idx = 0;
afx->radbuf[0] = 0;
}
*retn = len;
return rc;
}
static int
invalid_crc(void)
{
if ( opt.ignore_crc_error )
return 0;
log_inc_errorcount();
return G10ERR_INVALID_ARMOR;
}
static int
radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
byte *buf, size_t size )
{
byte val;
int c=0, c2; /*init c because gcc is not clever enough for the continue*/
int checkcrc=0;
int rc = 0;
size_t n = 0;
int idx, i, onlypad=0;
u32 crc;
crc = afx->crc;
idx = afx->idx;
val = afx->radbuf[0];
for( n=0; n < size; ) {
if( afx->buffer_pos < afx->buffer_len )
c = afx->buffer[afx->buffer_pos++];
else { /* read the next line */
unsigned maxlen = MAX_LINELEN;
afx->buffer_pos = 0;
afx->buffer_len = iobuf_read_line( a, &afx->buffer,
&afx->buffer_size, &maxlen );
if( !maxlen )
afx->truncated++;
if( !afx->buffer_len )
break; /* eof */
continue;
}
again:
if( c == '\n' || c == ' ' || c == '\r' || c == '\t' )
continue;
else if( c == '=' ) { /* Pad character: stop or CRC sum starts. */
/* some mailers leave quoted-printable encoded characters
* so we try to workaround this */
if( afx->buffer_pos+2 < afx->buffer_len ) {
int cc1, cc2, cc3;
cc1 = afx->buffer[afx->buffer_pos];
cc2 = afx->buffer[afx->buffer_pos+1];
cc3 = afx->buffer[afx->buffer_pos+2];
if( isxdigit(cc1) && isxdigit(cc2)
&& strchr( "=\n\r\t ", cc3 )) {
/* well it seems to be the case - adjust */
c = isdigit(cc1)? (cc1 - '0'): (ascii_toupper(cc1)-'A'+10);
c <<= 4;
c |= isdigit(cc2)? (cc2 - '0'): (ascii_toupper(cc2)-'A'+10);
afx->buffer_pos += 2;
afx->qp_detected = 1;
goto again;
}
}
if (!n)
onlypad = 1;
if( idx == 1 )
buf[n++] = val;
checkcrc++;
break;
}
else if( (c = asctobin[(c2=c)]) == 255 ) {
log_error(_("invalid radix64 character %02X skipped\n"), c2);
continue;
}
switch(idx) {
case 0: val = c << 2; break;
case 1: val |= (c>>4)&3; buf[n++]=val;val=(c<<4)&0xf0;break;
case 2: val |= (c>>2)&15; buf[n++]=val;val=(c<<6)&0xc0;break;
case 3: val |= c&0x3f; buf[n++] = val; break;
}
idx = (idx+1) % 4;
}
for(i=0; i < n; i++ )
crc = (crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ buf[i]];
crc &= 0x00ffffff;
afx->crc = crc;
afx->idx = idx;
afx->radbuf[0] = val;
if( checkcrc ) {
afx->any_data = 1;
afx->inp_checked=0;
afx->faked = 0;
for(;;) { /* skip lf and pad characters */
if( afx->buffer_pos < afx->buffer_len )
c = afx->buffer[afx->buffer_pos++];
else { /* read the next line */
unsigned maxlen = MAX_LINELEN;
afx->buffer_pos = 0;
afx->buffer_len = iobuf_read_line( a, &afx->buffer,
&afx->buffer_size, &maxlen );
if( !maxlen )
afx->truncated++;
if( !afx->buffer_len )
break; /* eof */
continue;
}
if( c == '\n' || c == ' ' || c == '\r'
|| c == '\t' || c == '=' )
continue;
break;
}
if( c == -1 )
log_error(_("premature eof (no CRC)\n"));
else {
u32 mycrc = 0;
idx = 0;
do {
if( (c = asctobin[c]) == 255 )
break;
switch(idx) {
case 0: val = c << 2; break;
case 1: val |= (c>>4)&3; mycrc |= val << 16;val=(c<<4)&0xf0;break;
case 2: val |= (c>>2)&15; mycrc |= val << 8;val=(c<<6)&0xc0;break;
case 3: val |= c&0x3f; mycrc |= val; break;
}
for(;;) {
if( afx->buffer_pos < afx->buffer_len )
c = afx->buffer[afx->buffer_pos++];
else { /* read the next line */
unsigned maxlen = MAX_LINELEN;
afx->buffer_pos = 0;
afx->buffer_len = iobuf_read_line( a, &afx->buffer,
&afx->buffer_size,
&maxlen );
if( !maxlen )
afx->truncated++;
if( !afx->buffer_len )
break; /* eof */
continue;
}
break;
}
if( !afx->buffer_len )
break; /* eof */
} while( ++idx < 4 );
if( c == -1 ) {
log_info(_("premature eof (in CRC)\n"));
rc = invalid_crc();
}
else if( idx == 0 ) {
/* No CRC at all is legal ("MAY") */
rc=0;
}
else if( idx != 4 ) {
log_info(_("malformed CRC\n"));
rc = invalid_crc();
}
else if( mycrc != afx->crc ) {
log_info (_("CRC error; %06lX - %06lX\n"),
(ulong)afx->crc, (ulong)mycrc);
rc = invalid_crc();
}
else {
rc = 0;
/* FIXME: Here we should emit another control packet,
* so that we know in mainproc that we are processing
* a clearsign message */
#if 0
for(rc=0;!rc;) {
rc = 0 /*check_trailer( &fhdr, c )*/;
if( !rc ) {
if( (c=iobuf_get(a)) == -1 )
rc = 2;
}
}
if( rc == -1 )
rc = 0;
else if( rc == 2 ) {
log_error(_("premature eof (in trailer)\n"));
rc = G10ERR_INVALID_ARMOR;
}
else {
log_error(_("error in trailer line\n"));
rc = G10ERR_INVALID_ARMOR;
}
#endif
}
}
}
if( !n && !onlypad )
rc = -1;
*retn = n;
return rc;
}
/****************
* This filter is used to handle the armor stuff
*/
int
armor_filter( void *opaque, int control,
IOBUF a, byte *buf, size_t *ret_len)
{
size_t size = *ret_len;
armor_filter_context_t *afx = opaque;
int rc=0, i, c;
byte radbuf[3];
int idx, idx2;
size_t n=0;
u32 crc;
#if 0
static FILE *fp ;
if( !fp ) {
fp = fopen("armor.out", "w");
assert(fp);
}
#endif
if( DBG_FILTER )
log_debug("armor-filter: control: %d\n", control );
if( control == IOBUFCTRL_UNDERFLOW && afx->inp_bypass ) {
n = 0;
if( afx->buffer_len ) {
for(; n < size && afx->buffer_pos < afx->buffer_len; n++ )
buf[n++] = afx->buffer[afx->buffer_pos++];
if( afx->buffer_pos >= afx->buffer_len )
afx->buffer_len = 0;
}
for(; n < size; n++ ) {
if( (c=iobuf_get(a)) == -1 )
break;
buf[n] = c & 0xff;
}
if( !n )
rc = -1;
*ret_len = n;
}
else if( control == IOBUFCTRL_UNDERFLOW ) {
/* We need some space for the faked packet. The minmum
* required size is the PARTIAL_CHUNK size plus a byte for the
* length itself */
if( size < PARTIAL_CHUNK+1 )
BUG(); /* supplied buffer too short */
if( afx->faked )
rc = fake_packet( afx, a, &n, buf, size );
else if( !afx->inp_checked ) {
rc = check_input( afx, a );
if( afx->inp_bypass ) {
for(n=0; n < size && afx->buffer_pos < afx->buffer_len; )
buf[n++] = afx->buffer[afx->buffer_pos++];
if( afx->buffer_pos >= afx->buffer_len )
afx->buffer_len = 0;
if( !n )
rc = -1;
}
else if( afx->faked ) {
unsigned int hashes = afx->hashes;
const byte *sesmark;
size_t sesmarklen;
sesmark = get_session_marker( &sesmarklen );
if ( sesmarklen > 20 )
BUG();
/* the buffer is at least 15+n*15 bytes long, so it
* is easy to construct the packets */
hashes &= 1|2|4|8|16|32|64;
if( !hashes ) {
hashes |= 4; /* default to MD 5 */
/* This is non-ideal since PGP 5-8 have the same
end-of-line bugs as PGP 2. However, we only
enable pgp2mode if there is no Hash: header. */
if( opt.pgp2_workarounds )
afx->pgp2mode = 1;
}
n=0;
/* First a gpg control packet... */
buf[n++] = 0xff; /* new format, type 63, 1 length byte */
n++; /* see below */
memcpy(buf+n, sesmark, sesmarklen ); n+= sesmarklen;
buf[n++] = CTRLPKT_CLEARSIGN_START;
buf[n++] = afx->not_dash_escaped? 0:1; /* sigclass */
if( hashes & 1 )
buf[n++] = DIGEST_ALGO_RMD160;
if( hashes & 2 )
buf[n++] = DIGEST_ALGO_SHA1;
if( hashes & 4 )
buf[n++] = DIGEST_ALGO_MD5;
if( hashes & 8 )
buf[n++] = DIGEST_ALGO_SHA224;
if( hashes & 16 )
buf[n++] = DIGEST_ALGO_SHA256;
if( hashes & 32 )
buf[n++] = DIGEST_ALGO_SHA384;
if( hashes & 64 )
buf[n++] = DIGEST_ALGO_SHA512;
buf[1] = n - 2;
/* ...followed by an invented plaintext packet.
Amusingly enough, this packet is not compliant with
2440 as the initial partial length is less than 512
bytes. Of course, we'll accept it anyway ;) */
buf[n++] = 0xCB; /* new packet format, type 11 */
buf[n++] = 0xE1; /* 2^1 == 2 bytes */
buf[n++] = 't'; /* canonical text mode */
buf[n++] = 0; /* namelength */
buf[n++] = 0xE2; /* 2^2 == 4 more bytes */
memset(buf+n, 0, 4); /* timestamp */
n += 4;
}
else if( !rc )
rc = radix64_read( afx, a, &n, buf, size );
}
else
rc = radix64_read( afx, a, &n, buf, size );
#if 0
if( n )
if( fwrite(buf, n, 1, fp ) != 1 )
BUG();
#endif
*ret_len = n;
}
else if( control == IOBUFCTRL_FLUSH && !afx->cancel ) {
if( !afx->status ) { /* write the header line */
const char *s;
STRLIST comment=opt.comments;
if( afx->what >= DIM(head_strings) )
log_bug("afx->what=%d", afx->what);
iobuf_writestr(a, "-----");
iobuf_writestr(a, head_strings[afx->what] );
iobuf_writestr(a, "-----" );
iobuf_writestr(a,afx->eol);
if (opt.emit_version)
{
iobuf_writestr (a, "Version: GnuPG v");
for (s=VERSION; *s && *s != '.'; s++)
iobuf_writebyte (a, *s);
if (opt.emit_version > 1 && *s)
{
iobuf_writebyte (a, *s++);
for (; *s && *s != '.'; s++)
iobuf_writebyte (a, *s);
if (opt.emit_version > 2)
{
for (; *s && *s != '-' && !spacep (s); s++)
iobuf_writebyte (a, *s);
if (opt.emit_version > 3)
iobuf_writestr (a, " (" PRINTABLE_OS_NAME ")");
}
}
iobuf_writestr(a,afx->eol);
}
/* write the comment strings */
for(s=comment->d;comment;comment=comment->next,s=comment->d)
{
iobuf_writestr(a, "Comment: " );
for( ; *s; s++ )
{
if( *s == '\n' )
iobuf_writestr(a, "\\n" );
else if( *s == '\r' )
iobuf_writestr(a, "\\r" );
else if( *s == '\v' )
iobuf_writestr(a, "\\v" );
else
iobuf_put(a, *s );
}
iobuf_writestr(a,afx->eol);
}
if ( afx->hdrlines ) {
for ( s = afx->hdrlines; *s; s++ ) {
#ifdef HAVE_DOSISH_SYSTEM
if ( *s == '\n' )
iobuf_put( a, '\r');
#endif
iobuf_put(a, *s );
}
}
iobuf_writestr(a,afx->eol);
afx->status++;
afx->idx = 0;
afx->idx2 = 0;
afx->crc = CRCINIT;
}
crc = afx->crc;
idx = afx->idx;
idx2 = afx->idx2;
for(i=0; i < idx; i++ )
radbuf[i] = afx->radbuf[i];
for(i=0; i < size; i++ )
crc = (crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ buf[i]];
crc &= 0x00ffffff;
for( ; size; buf++, size-- ) {
radbuf[idx++] = *buf;
if( idx > 2 ) {
idx = 0;
c = bintoasc[(*radbuf >> 2) & 077];
iobuf_put(a, c);
c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
iobuf_put(a, c);
c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
iobuf_put(a, c);
c = bintoasc[radbuf[2]&077];
iobuf_put(a, c);
if( ++idx2 >= (64/4) )
{ /* pgp doesn't like 72 here */
iobuf_writestr(a,afx->eol);
idx2=0;
}
}
}
for(i=0; i < idx; i++ )
afx->radbuf[i] = radbuf[i];
afx->idx = idx;
afx->idx2 = idx2;
afx->crc = crc;
}
else if( control == IOBUFCTRL_INIT )
{
if( !is_initialized )
initialize();
/* Figure out what we're using for line endings if the caller
didn't specify. */
if(afx->eol[0]==0)
{
#ifdef HAVE_DOSISH_SYSTEM
afx->eol[0]='\r';
afx->eol[1]='\n';
#else
afx->eol[0]='\n';
#endif
}
}
else if( control == IOBUFCTRL_CANCEL ) {
afx->cancel = 1;
}
else if( control == IOBUFCTRL_FREE ) {
if( afx->cancel )
;
else if( afx->status ) { /* pad, write cecksum, and bottom line */
crc = afx->crc;
idx = afx->idx;
idx2 = afx->idx2;
for(i=0; i < idx; i++ )
radbuf[i] = afx->radbuf[i];
if( idx ) {
c = bintoasc[(*radbuf>>2)&077];
iobuf_put(a, c);
if( idx == 1 ) {
c = bintoasc[((*radbuf << 4) & 060) & 077];
iobuf_put(a, c);
iobuf_put(a, '=');
iobuf_put(a, '=');
}
else { /* 2 */
c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
iobuf_put(a, c);
c = bintoasc[((radbuf[1] << 2) & 074) & 077];
iobuf_put(a, c);
iobuf_put(a, '=');
}
if( ++idx2 >= (64/4) )
{ /* pgp doesn't like 72 here */
iobuf_writestr(a,afx->eol);
idx2=0;
}
}
/* may need a linefeed */
if( idx2 )
iobuf_writestr(a,afx->eol);
/* write the CRC */
iobuf_put(a, '=');
radbuf[0] = crc >>16;
radbuf[1] = crc >> 8;
radbuf[2] = crc;
c = bintoasc[(*radbuf >> 2) & 077];
iobuf_put(a, c);
c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
iobuf_put(a, c);
c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
iobuf_put(a, c);
c = bintoasc[radbuf[2]&077];
iobuf_put(a, c);
iobuf_writestr(a,afx->eol);
/* and the the trailer */
if( afx->what >= DIM(tail_strings) )
log_bug("afx->what=%d", afx->what);
iobuf_writestr(a, "-----");
iobuf_writestr(a, tail_strings[afx->what] );
iobuf_writestr(a, "-----" );
iobuf_writestr(a,afx->eol);
}
else if( !afx->any_data && !afx->inp_bypass ) {
log_error(_("no valid OpenPGP data found.\n"));
afx->no_openpgp_data = 1;
write_status_text( STATUS_NODATA, "1" );
}
if( afx->truncated )
log_info(_("invalid armor: line longer than %d characters\n"),
MAX_LINELEN );
/* issue an error to enforce dissemination of correct software */
if( afx->qp_detected )
log_error(_("quoted printable character in armor - "
"probably a buggy MTA has been used\n") );
xfree( afx->buffer );
afx->buffer = NULL;
release_armor_context (afx);
}
else if( control == IOBUFCTRL_DESC )
- *(char**)buf = "armor_filter";
+ mem2str (buf, "armor_filter", *ret_len);
return rc;
}
/****************
* create a radix64 encoded string.
*/
char *
make_radix64_string( const byte *data, size_t len )
{
char *buffer, *p;
buffer = p = xmalloc( (len+2)/3*4 + 1 );
for( ; len >= 3 ; len -= 3, data += 3 ) {
*p++ = bintoasc[(data[0] >> 2) & 077];
*p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
*p++ = bintoasc[(((data[1]<<2)&074)|((data[2]>>6)&03))&077];
*p++ = bintoasc[data[2]&077];
}
if( len == 2 ) {
*p++ = bintoasc[(data[0] >> 2) & 077];
*p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
*p++ = bintoasc[((data[1]<<2)&074)];
}
else if( len == 1 ) {
*p++ = bintoasc[(data[0] >> 2) & 077];
*p++ = bintoasc[(data[0] <<4)&060];
}
*p = 0;
return buffer;
}
/***********************************************
* For the pipemode command we can't use the armor filter for various
* reasons, so we use this new unarmor_pump stuff to remove the armor
*/
enum unarmor_state_e {
STA_init = 0,
STA_bypass,
STA_wait_newline,
STA_wait_dash,
STA_first_dash,
STA_compare_header,
STA_found_header_wait_newline,
STA_skip_header_lines,
STA_skip_header_lines_non_ws,
STA_read_data,
STA_wait_crc,
STA_read_crc,
STA_ready
};
struct unarmor_pump_s {
enum unarmor_state_e state;
byte val;
int checkcrc;
int pos; /* counts from 0..3 */
u32 crc;
u32 mycrc; /* the one store in the data */
};
UnarmorPump
unarmor_pump_new (void)
{
UnarmorPump x;
if( !is_initialized )
initialize();
x = xmalloc_clear (sizeof *x);
return x;
}
void
unarmor_pump_release (UnarmorPump x)
{
xfree (x);
}
/*
* Get the next character from the ascii armor taken from the IOBUF
* created earlier by unarmor_pump_new().
* Return: c = Character
* 256 = ignore this value
* -1 = End of current armor
* -2 = Premature EOF (not used)
* -3 = Invalid armor
*/
int
unarmor_pump (UnarmorPump x, int c)
{
int rval = 256; /* default is to ignore the return value */
switch (x->state) {
case STA_init:
{
byte tmp[1];
tmp[0] = c;
if ( is_armored (tmp) )
x->state = c == '-'? STA_first_dash : STA_wait_newline;
else {
x->state = STA_bypass;
return c;
}
}
break;
case STA_bypass:
return c; /* return here to avoid crc calculation */
case STA_wait_newline:
if (c == '\n')
x->state = STA_wait_dash;
break;
case STA_wait_dash:
x->state = c == '-'? STA_first_dash : STA_wait_newline;
break;
case STA_first_dash: /* just need for initalization */
x->pos = 0;
x->state = STA_compare_header;
case STA_compare_header:
if ( "-----BEGIN PGP SIGNATURE-----"[++x->pos] == c ) {
if ( x->pos == 28 )
x->state = STA_found_header_wait_newline;
}
else
x->state = c == '\n'? STA_wait_dash : STA_wait_newline;
break;
case STA_found_header_wait_newline:
/* to make CR,LF issues easier we simply allow for white space
behind the 5 dashes */
if ( c == '\n' )
x->state = STA_skip_header_lines;
else if ( c != '\r' && c != ' ' && c != '\t' )
x->state = STA_wait_dash; /* garbage after the header line */
break;
case STA_skip_header_lines:
/* i.e. wait for one empty line */
if ( c == '\n' ) {
x->state = STA_read_data;
x->crc = CRCINIT;
x->val = 0;
x->pos = 0;
}
else if ( c != '\r' && c != ' ' && c != '\t' )
x->state = STA_skip_header_lines_non_ws;
break;
case STA_skip_header_lines_non_ws:
/* like above but we already encountered non white space */
if ( c == '\n' )
x->state = STA_skip_header_lines;
break;
case STA_read_data:
/* fixme: we don't check for the trailing dash lines but rely
* on the armor stop characters */
if( c == '\n' || c == ' ' || c == '\r' || c == '\t' )
break; /* skip all kind of white space */
if( c == '=' ) { /* pad character: stop */
if( x->pos == 1 ) /* in this case val has some value */
rval = x->val;
x->state = STA_wait_crc;
break;
}
{
int c2;
if( (c = asctobin[(c2=c)]) == 255 ) {
log_error(_("invalid radix64 character %02X skipped\n"), c2);
break;
}
}
switch(x->pos) {
case 0:
x->val = c << 2;
break;
case 1:
x->val |= (c>>4)&3;
rval = x->val;
x->val = (c<<4)&0xf0;
break;
case 2:
x->val |= (c>>2)&15;
rval = x->val;
x->val = (c<<6)&0xc0;
break;
case 3:
x->val |= c&0x3f;
rval = x->val;
break;
}
x->pos = (x->pos+1) % 4;
break;
case STA_wait_crc:
if( c == '\n' || c == ' ' || c == '\r' || c == '\t' || c == '=' )
break; /* skip ws and pad characters */
/* assume that we are at the next line */
x->state = STA_read_crc;
x->pos = 0;
x->mycrc = 0;
case STA_read_crc:
if( (c = asctobin[c]) == 255 ) {
rval = -1; /* ready */
if( x->crc != x->mycrc ) {
log_info (_("CRC error; %06lX - %06lX\n"),
(ulong)x->crc, (ulong)x->mycrc);
if ( invalid_crc() )
rval = -3;
}
x->state = STA_ready; /* not sure whether this is correct */
break;
}
switch(x->pos) {
case 0:
x->val = c << 2;
break;
case 1:
x->val |= (c>>4)&3;
x->mycrc |= x->val << 16;
x->val = (c<<4)&0xf0;
break;
case 2:
x->val |= (c>>2)&15;
x->mycrc |= x->val << 8;
x->val = (c<<6)&0xc0;
break;
case 3:
x->val |= c&0x3f;
x->mycrc |= x->val;
break;
}
x->pos = (x->pos+1) % 4;
break;
case STA_ready:
rval = -1;
break;
}
if ( !(rval & ~255) ) { /* compute the CRC */
x->crc = (x->crc << 8) ^ crc_table[((x->crc >> 16)&0xff) ^ rval];
x->crc &= 0x00ffffff;
}
return rval;
}
diff --git a/g10/cipher.c b/g10/cipher.c
index 0c51100cc..45e1963f5 100644
--- a/g10/cipher.c
+++ b/g10/cipher.c
@@ -1,151 +1,151 @@
/* cipher.c - En-/De-ciphering filter
* Copyright (C) 1998, 1999, 2000, 2001 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 .
*/
#include
#include
#include
#include
#include
#include
#include "errors.h"
#include "iobuf.h"
#include "memory.h"
#include "util.h"
#include "filter.h"
#include "packet.h"
#include "options.h"
#include "main.h"
#include "status.h"
#define MIN_PARTIAL_SIZE 512
static void
write_header( cipher_filter_context_t *cfx, IOBUF a )
{
PACKET pkt;
PKT_encrypted ed;
byte temp[18];
unsigned blocksize;
unsigned nprefix;
blocksize = cipher_get_blocksize( cfx->dek->algo );
if( blocksize < 8 || blocksize > 16 )
log_fatal("unsupported blocksize %u\n", blocksize );
memset( &ed, 0, sizeof ed );
ed.len = cfx->datalen;
ed.extralen = blocksize+2;
ed.new_ctb = !ed.len && !RFC1991;
if( cfx->dek->use_mdc ) {
ed.mdc_method = DIGEST_ALGO_SHA1;
cfx->mdc_hash = md_open( DIGEST_ALGO_SHA1, 0 );
if ( DBG_HASHING )
md_start_debug( cfx->mdc_hash, "creatmdc" );
}
{
char buf[20];
sprintf (buf, "%d %d", ed.mdc_method, cfx->dek->algo);
write_status_text (STATUS_BEGIN_ENCRYPTION, buf);
}
init_packet( &pkt );
pkt.pkttype = cfx->dek->use_mdc? PKT_ENCRYPTED_MDC : PKT_ENCRYPTED;
pkt.pkt.encrypted = &ed;
if( build_packet( a, &pkt ))
log_bug("build_packet(ENCR_DATA) failed\n");
nprefix = blocksize;
randomize_buffer( temp, nprefix, 1 );
temp[nprefix] = temp[nprefix-2];
temp[nprefix+1] = temp[nprefix-1];
print_cipher_algo_note( cfx->dek->algo );
cfx->cipher_hd = cipher_open( cfx->dek->algo,
cfx->dek->use_mdc? CIPHER_MODE_CFB
: CIPHER_MODE_AUTO_CFB, 1 );
/* log_hexdump( "thekey", cfx->dek->key, cfx->dek->keylen );*/
cipher_setkey( cfx->cipher_hd, cfx->dek->key, cfx->dek->keylen );
cipher_setiv( cfx->cipher_hd, NULL, 0 );
/* log_hexdump( "prefix", temp, nprefix+2 ); */
if( cfx->mdc_hash ) /* hash the "IV" */
md_write( cfx->mdc_hash, temp, nprefix+2 );
cipher_encrypt( cfx->cipher_hd, temp, temp, nprefix+2);
cipher_sync( cfx->cipher_hd );
iobuf_write(a, temp, nprefix+2);
cfx->header=1;
}
/****************
* This filter is used to en/de-cipher data with a conventional algorithm
*/
int
cipher_filter( void *opaque, int control,
IOBUF a, byte *buf, size_t *ret_len)
{
size_t size = *ret_len;
cipher_filter_context_t *cfx = opaque;
int rc=0;
if( control == IOBUFCTRL_UNDERFLOW ) { /* decrypt */
rc = -1; /* not yet used */
}
else if( control == IOBUFCTRL_FLUSH ) { /* encrypt */
assert(a);
if( !cfx->header ) {
write_header( cfx, a );
}
if( cfx->mdc_hash )
md_write( cfx->mdc_hash, buf, size );
cipher_encrypt( cfx->cipher_hd, buf, buf, size);
if( iobuf_write( a, buf, size ) )
rc = G10ERR_WRITE_FILE;
}
else if( control == IOBUFCTRL_FREE ) {
if( cfx->mdc_hash ) {
byte *hash;
int hashlen = md_digest_length( md_get_algo( cfx->mdc_hash ) );
byte temp[22];
assert( hashlen == 20 );
/* we must hash the prefix of the MDC packet here */
temp[0] = 0xd3;
temp[1] = 0x14;
md_putc( cfx->mdc_hash, temp[0] );
md_putc( cfx->mdc_hash, temp[1] );
md_final( cfx->mdc_hash );
hash = md_read( cfx->mdc_hash, 0 );
memcpy(temp+2, hash, 20);
cipher_encrypt( cfx->cipher_hd, temp, temp, 22 );
md_close( cfx->mdc_hash ); cfx->mdc_hash = NULL;
if( iobuf_write( a, temp, 22 ) )
log_error("writing MDC packet failed\n" );
}
cipher_close(cfx->cipher_hd);
}
else if( control == IOBUFCTRL_DESC ) {
- *(char**)buf = "cipher_filter";
+ mem2str (buf, "cipher_filter", *ret_len);
}
return rc;
}
diff --git a/g10/compress-bz2.c b/g10/compress-bz2.c
index baef92f3f..6c5bd66b3 100644
--- a/g10/compress-bz2.c
+++ b/g10/compress-bz2.c
@@ -1,252 +1,252 @@
/* compress.c - bzip2 compress filter
* Copyright (C) 2003, 2004 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 .
*/
#include
#include
#include /* Early versions of bzlib (1.0) require stdio.h */
#include
#include "util.h"
#include "memory.h"
#include "packet.h"
#include "filter.h"
#include "main.h"
#include "options.h"
/* Note that the code in compress.c is nearly identical to the code
here, so if you fix a bug here, look there to see if a matching bug
needs to be fixed. I tried to have one set of functions that could
do ZIP, ZLIB, and BZIP2, but it became dangerously unreadable with
#ifdefs and if(algo) -dshaw */
static void
init_compress( compress_filter_context_t *zfx, bz_stream *bzs )
{
int rc;
int level;
if( opt.bz2_compress_level >= 1 && opt.bz2_compress_level <= 9 )
level = opt.bz2_compress_level;
else if( opt.bz2_compress_level == -1 )
level = 6; /* no particular reason, but it seems reasonable */
else
{
log_error("invalid compression level; using default level\n");
level = 6;
}
if((rc=BZ2_bzCompressInit(bzs,level,0,0))!=BZ_OK)
log_fatal("bz2lib problem: %d\n",rc);
zfx->outbufsize = 8192;
zfx->outbuf = xmalloc( zfx->outbufsize );
}
static int
do_compress(compress_filter_context_t *zfx, bz_stream *bzs, int flush, IOBUF a)
{
int zrc;
unsigned n;
do
{
bzs->next_out = zfx->outbuf;
bzs->avail_out = zfx->outbufsize;
if( DBG_FILTER )
log_debug("enter bzCompress: avail_in=%u, avail_out=%u, flush=%d\n",
(unsigned)bzs->avail_in, (unsigned)bzs->avail_out, flush );
zrc = BZ2_bzCompress( bzs, flush );
if( zrc == BZ_STREAM_END && flush == BZ_FINISH )
;
else if( zrc != BZ_RUN_OK && zrc != BZ_FINISH_OK )
log_fatal("bz2lib deflate problem: rc=%d\n", zrc );
n = zfx->outbufsize - bzs->avail_out;
if( DBG_FILTER )
log_debug("leave bzCompress:"
" avail_in=%u, avail_out=%u, n=%u, zrc=%d\n",
(unsigned)bzs->avail_in, (unsigned)bzs->avail_out,
(unsigned)n, zrc );
if( iobuf_write( a, zfx->outbuf, n ) )
{
log_debug("bzCompress: iobuf_write failed\n");
return G10ERR_WRITE_FILE;
}
}
while( bzs->avail_in || (flush == BZ_FINISH && zrc != BZ_STREAM_END) );
return 0;
}
static void
init_uncompress( compress_filter_context_t *zfx, bz_stream *bzs )
{
int rc;
if((rc=BZ2_bzDecompressInit(bzs,0,opt.bz2_decompress_lowmem))!=BZ_OK)
log_fatal("bz2lib problem: %d\n",rc);
zfx->inbufsize = 2048;
zfx->inbuf = xmalloc( zfx->inbufsize );
bzs->avail_in = 0;
}
static int
do_uncompress( compress_filter_context_t *zfx, bz_stream *bzs,
IOBUF a, size_t *ret_len )
{
int zrc;
int rc=0;
size_t n;
int nread, count;
int refill = !bzs->avail_in;
int eofseen = 0;
if( DBG_FILTER )
log_debug("begin bzDecompress: avail_in=%u, avail_out=%u, inbuf=%u\n",
(unsigned)bzs->avail_in, (unsigned)bzs->avail_out,
(unsigned)zfx->inbufsize );
do
{
if( bzs->avail_in < zfx->inbufsize && refill )
{
n = bzs->avail_in;
if( !n )
bzs->next_in = zfx->inbuf;
count = zfx->inbufsize - n;
nread = iobuf_read( a, zfx->inbuf + n, count );
if( nread == -1 )
{
eofseen = 1;
nread = 0;
}
n += nread;
bzs->avail_in = n;
}
if (!eofseen)
refill = 1;
if( DBG_FILTER )
log_debug("enter bzDecompress: avail_in=%u, avail_out=%u\n",
(unsigned)bzs->avail_in, (unsigned)bzs->avail_out);
zrc=BZ2_bzDecompress(bzs);
if( DBG_FILTER )
log_debug("leave bzDecompress: avail_in=%u, avail_out=%u, zrc=%d\n",
(unsigned)bzs->avail_in, (unsigned)bzs->avail_out, zrc);
if( zrc == BZ_STREAM_END )
rc = -1; /* eof */
else if( zrc != BZ_OK && zrc != BZ_PARAM_ERROR )
log_fatal("bz2lib inflate problem: rc=%d\n", zrc );
else if (zrc == BZ_OK && eofseen
&& !bzs->avail_in && bzs->avail_out > 0)
{
log_error ("unexpected EOF in bz2lib\n");
rc = G10ERR_READ_FILE;
break;
}
}
while( bzs->avail_out && zrc != BZ_STREAM_END && zrc != BZ_PARAM_ERROR );
/* I'm not completely happy with the two uses of BZ_PARAM_ERROR
here. The corresponding zlib function is Z_BUF_ERROR, which
covers a narrower scope than BZ_PARAM_ERROR. -dshaw */
*ret_len = zfx->outbufsize - bzs->avail_out;
if( DBG_FILTER )
log_debug("do_uncompress: returning %u bytes\n", (unsigned)*ret_len );
return rc;
}
int
compress_filter_bz2( void *opaque, int control,
IOBUF a, byte *buf, size_t *ret_len)
{
size_t size = *ret_len;
compress_filter_context_t *zfx = opaque;
bz_stream *bzs = zfx->opaque;
int rc=0;
if( control == IOBUFCTRL_UNDERFLOW )
{
if( !zfx->status )
{
bzs = zfx->opaque = xmalloc_clear( sizeof *bzs );
init_uncompress( zfx, bzs );
zfx->status = 1;
}
bzs->next_out = buf;
bzs->avail_out = size;
zfx->outbufsize = size; /* needed only for calculation */
rc = do_uncompress( zfx, bzs, a, ret_len );
}
else if( control == IOBUFCTRL_FLUSH )
{
if( !zfx->status )
{
PACKET pkt;
PKT_compressed cd;
if( zfx->algo != COMPRESS_ALGO_BZIP2 )
BUG();
memset( &cd, 0, sizeof cd );
cd.len = 0;
cd.algorithm = zfx->algo;
init_packet( &pkt );
pkt.pkttype = PKT_COMPRESSED;
pkt.pkt.compressed = &cd;
if( build_packet( a, &pkt ))
log_bug("build_packet(PKT_COMPRESSED) failed\n");
bzs = zfx->opaque = xmalloc_clear( sizeof *bzs );
init_compress( zfx, bzs );
zfx->status = 2;
}
bzs->next_in = buf;
bzs->avail_in = size;
rc = do_compress( zfx, bzs, BZ_RUN, a );
}
else if( control == IOBUFCTRL_FREE )
{
if( zfx->status == 1 )
{
BZ2_bzDecompressEnd(bzs);
xfree(bzs);
zfx->opaque = NULL;
xfree(zfx->outbuf); zfx->outbuf = NULL;
}
else if( zfx->status == 2 )
{
bzs->next_in = buf;
bzs->avail_in = 0;
do_compress( zfx, bzs, BZ_FINISH, a );
BZ2_bzCompressEnd(bzs);
xfree(bzs);
zfx->opaque = NULL;
xfree(zfx->outbuf); zfx->outbuf = NULL;
}
if (zfx->release)
zfx->release (zfx);
}
else if( control == IOBUFCTRL_DESC )
- *(char**)buf = "compress_filter";
+ mem2str (buf, "compress_filter", *ret_len);
return rc;
}
diff --git a/g10/compress.c b/g10/compress.c
index 07c9e5e9c..4598affff 100644
--- a/g10/compress.c
+++ b/g10/compress.c
@@ -1,367 +1,367 @@
/* compress.c - compress filter
* Copyright (C) 1998, 1999, 2000, 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 .
*/
/* Note that the code in compress-bz2.c is nearly identical to the
code here, so if you fix a bug here, look there to see if a
matching bug needs to be fixed. I tried to have one set of
functions that could do ZIP, ZLIB, and BZIP2, but it became
dangerously unreadable with #ifdefs and if(algo) -dshaw */
#include
#include
#include
#include
#include
#include
#include
#include
#if defined(__riscos__) && defined(USE_ZLIBRISCOS)
# include "zlib-riscos.h"
#endif
#include "util.h"
#include "memory.h"
#include "packet.h"
#include "filter.h"
#include "main.h"
#include "options.h"
int compress_filter_bz2( void *opaque, int control,
IOBUF a, byte *buf, size_t *ret_len);
static void
init_compress( compress_filter_context_t *zfx, z_stream *zs )
{
int rc;
int level;
#if defined(__riscos__) && defined(USE_ZLIBRISCOS)
static int zlib_initialized = 0;
if (!zlib_initialized)
zlib_initialized = riscos_load_module("ZLib", zlib_path, 1);
#endif
if( opt.compress_level >= 1 && opt.compress_level <= 9 )
level = opt.compress_level;
else if( opt.compress_level == -1 )
level = Z_DEFAULT_COMPRESSION;
else {
log_error("invalid compression level; using default level\n");
level = Z_DEFAULT_COMPRESSION;
}
if( (rc = zfx->algo == 1? deflateInit2( zs, level, Z_DEFLATED,
-13, 8, Z_DEFAULT_STRATEGY)
: deflateInit( zs, level )
) != Z_OK ) {
log_fatal("zlib problem: %s\n", zs->msg? zs->msg :
rc == Z_MEM_ERROR ? "out of core" :
rc == Z_VERSION_ERROR ? "invalid lib version" :
"unknown error" );
}
zfx->outbufsize = 8192;
zfx->outbuf = xmalloc( zfx->outbufsize );
}
static int
do_compress( compress_filter_context_t *zfx, z_stream *zs, int flush, IOBUF a )
{
int zrc;
unsigned n;
do {
#ifndef __riscos__
zs->next_out = zfx->outbuf;
#else /* __riscos__ */
zs->next_out = (Bytef *) zfx->outbuf;
#endif /* __riscos__ */
zs->avail_out = zfx->outbufsize;
if( DBG_FILTER )
log_debug("enter deflate: avail_in=%u, avail_out=%u, flush=%d\n",
(unsigned)zs->avail_in, (unsigned)zs->avail_out, flush );
zrc = deflate( zs, flush );
if( zrc == Z_STREAM_END && flush == Z_FINISH )
;
else if( zrc != Z_OK ) {
if( zs->msg )
log_fatal("zlib deflate problem: %s\n", zs->msg );
else
log_fatal("zlib deflate problem: rc=%d\n", zrc );
}
n = zfx->outbufsize - zs->avail_out;
if( DBG_FILTER )
log_debug("leave deflate: "
"avail_in=%u, avail_out=%u, n=%u, zrc=%d\n",
(unsigned)zs->avail_in, (unsigned)zs->avail_out,
(unsigned)n, zrc );
if( iobuf_write( a, zfx->outbuf, n ) ) {
log_debug("deflate: iobuf_write failed\n");
return G10ERR_WRITE_FILE;
}
} while( zs->avail_in || (flush == Z_FINISH && zrc != Z_STREAM_END) );
return 0;
}
static void
init_uncompress( compress_filter_context_t *zfx, z_stream *zs )
{
int rc;
/****************
* PGP uses a windowsize of 13 bits. Using a negative value for
* it forces zlib not to expect a zlib header. This is a
* undocumented feature Peter Gutmann told me about.
*
* We must use 15 bits for the inflator because CryptoEx uses 15
* bits thus the output would get scrambled w/o error indication
* if we would use 13 bits. For the uncompressing this does not
* matter at all.
*/
if( (rc = zfx->algo == 1? inflateInit2( zs, -15)
: inflateInit( zs )) != Z_OK ) {
log_fatal("zlib problem: %s\n", zs->msg? zs->msg :
rc == Z_MEM_ERROR ? "out of core" :
rc == Z_VERSION_ERROR ? "invalid lib version" :
"unknown error" );
}
zfx->inbufsize = 2048;
zfx->inbuf = xmalloc( zfx->inbufsize );
zs->avail_in = 0;
}
static int
do_uncompress( compress_filter_context_t *zfx, z_stream *zs,
IOBUF a, size_t *ret_len )
{
int zrc;
int rc = 0;
int leave = 0;
size_t n;
int nread, count;
int refill = !zs->avail_in;
if( DBG_FILTER )
log_debug("begin inflate: avail_in=%u, avail_out=%u, inbuf=%u\n",
(unsigned)zs->avail_in, (unsigned)zs->avail_out,
(unsigned)zfx->inbufsize );
do {
if( zs->avail_in < zfx->inbufsize && refill ) {
n = zs->avail_in;
if( !n )
#ifndef __riscos__
zs->next_in = zfx->inbuf;
#else /* __riscos__ */
zs->next_in = (Bytef *) zfx->inbuf;
#endif /* __riscos__ */
count = zfx->inbufsize - n;
nread = iobuf_read( a, zfx->inbuf + n, count );
if( nread == -1 )
nread = 0;
n += nread;
/* Algo 1 has no zlib header which requires us to to give
* inflate an extra dummy byte to read. To be on the safe
* side we allow for up to 4 ff bytes. */
if( nread < count && zfx->algo == 1 && zfx->algo1hack < 4) {
*(zfx->inbuf + n) = 0xFF;
zfx->algo1hack++;
n++;
leave = 1;
}
zs->avail_in = n;
}
refill = 1;
if( DBG_FILTER )
log_debug("enter inflate: avail_in=%u, avail_out=%u\n",
(unsigned)zs->avail_in, (unsigned)zs->avail_out);
#ifdef Z_SYNC_FLUSH
zrc = inflate( zs, Z_SYNC_FLUSH );
#else
zrc = inflate( zs, Z_PARTIAL_FLUSH );
#endif
if( DBG_FILTER )
log_debug("leave inflate: avail_in=%u, avail_out=%u, zrc=%d\n",
(unsigned)zs->avail_in, (unsigned)zs->avail_out, zrc);
if( zrc == Z_STREAM_END )
rc = -1; /* 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 );
}
} while (zs->avail_out && zrc != Z_STREAM_END && zrc != Z_BUF_ERROR
&& !leave);
*ret_len = zfx->outbufsize - zs->avail_out;
if( DBG_FILTER )
log_debug("do_uncompress: returning %u bytes\n", (unsigned)*ret_len );
return rc;
}
static int
compress_filter( void *opaque, int control,
IOBUF a, byte *buf, size_t *ret_len)
{
size_t size = *ret_len;
compress_filter_context_t *zfx = opaque;
z_stream *zs = zfx->opaque;
int rc=0;
if( control == IOBUFCTRL_UNDERFLOW ) {
if( !zfx->status ) {
zs = zfx->opaque = xmalloc_clear( sizeof *zs );
init_uncompress( zfx, zs );
zfx->status = 1;
}
#ifndef __riscos__
zs->next_out = buf;
#else /* __riscos__ */
zs->next_out = (Bytef *) buf;
#endif /* __riscos__ */
zs->avail_out = size;
zfx->outbufsize = size; /* needed only for calculation */
rc = do_uncompress( zfx, zs, a, ret_len );
}
else if( control == IOBUFCTRL_FLUSH ) {
if( !zfx->status ) {
PACKET pkt;
PKT_compressed cd;
if(zfx->algo != COMPRESS_ALGO_ZIP
&& zfx->algo != COMPRESS_ALGO_ZLIB)
BUG();
memset( &cd, 0, sizeof cd );
cd.len = 0;
cd.algorithm = zfx->algo;
init_packet( &pkt );
pkt.pkttype = PKT_COMPRESSED;
pkt.pkt.compressed = &cd;
if( build_packet( a, &pkt ))
log_bug("build_packet(PKT_COMPRESSED) failed\n");
zs = zfx->opaque = xmalloc_clear( sizeof *zs );
init_compress( zfx, zs );
zfx->status = 2;
}
#ifndef __riscos__
zs->next_in = buf;
#else /* __riscos__ */
zs->next_in = (Bytef *) buf;
#endif /* __riscos__ */
zs->avail_in = size;
rc = do_compress( zfx, zs, Z_NO_FLUSH, a );
}
else if( control == IOBUFCTRL_FREE ) {
if( zfx->status == 1 ) {
inflateEnd(zs);
xfree(zs);
zfx->opaque = NULL;
xfree(zfx->outbuf); zfx->outbuf = NULL;
}
else if( zfx->status == 2 ) {
#ifndef __riscos__
zs->next_in = buf;
#else /* __riscos__ */
zs->next_in = (Bytef *) buf;
#endif /* __riscos__ */
zs->avail_in = 0;
do_compress( zfx, zs, Z_FINISH, a );
deflateEnd(zs);
xfree(zs);
zfx->opaque = NULL;
xfree(zfx->outbuf); zfx->outbuf = NULL;
}
if (zfx->release)
zfx->release (zfx);
}
else if( control == IOBUFCTRL_DESC )
- *(char**)buf = "compress_filter";
+ mem2str (buf, "compress_filter", *ret_len);
return rc;
}
static void
release_context (compress_filter_context_t *ctx)
{
xfree (ctx);
}
/****************
* Handle a compressed packet
*/
int
handle_compressed( void *procctx, PKT_compressed *cd,
int (*callback)(IOBUF, void *), void *passthru )
{
compress_filter_context_t *cfx;
int rc;
if(check_compress_algo(cd->algorithm))
return G10ERR_COMPR_ALGO;
cfx = xmalloc_clear (sizeof *cfx);
cfx->release = release_context;
cfx->algo = cd->algorithm;
push_compress_filter(cd->buf,cfx,cd->algorithm);
if( callback )
rc = callback(cd->buf, passthru );
else
rc = proc_packets(procctx, cd->buf);
cd->buf = NULL;
return rc;
}
void
push_compress_filter(IOBUF out,compress_filter_context_t *zfx,int algo)
{
push_compress_filter2(out,zfx,algo,0);
}
void
push_compress_filter2(IOBUF out,compress_filter_context_t *zfx,
int algo,int rel)
{
if(algo>=0)
zfx->algo=algo;
else
zfx->algo=DEFAULT_COMPRESS_ALGO;
switch(zfx->algo)
{
case COMPRESS_ALGO_NONE:
break;
case COMPRESS_ALGO_ZIP:
case COMPRESS_ALGO_ZLIB:
iobuf_push_filter2(out,compress_filter,zfx,rel);
break;
#ifdef HAVE_BZIP2
case COMPRESS_ALGO_BZIP2:
iobuf_push_filter2(out,compress_filter_bz2,zfx,rel);
break;
#endif
default:
BUG();
}
}
diff --git a/g10/encode.c b/g10/encode.c
index a579c428d..93c70a70d 100644
--- a/g10/encode.c
+++ b/g10/encode.c
@@ -1,861 +1,861 @@
/* encode.c - encode data
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
* 2006 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 .
*/
#include
#include
#include
#include
#include
#include
#include "options.h"
#include "packet.h"
#include "errors.h"
#include "iobuf.h"
#include "keydb.h"
#include "memory.h"
#include "util.h"
#include "main.h"
#include "filter.h"
#include "trustdb.h"
#include "i18n.h"
#include "status.h"
static int encode_simple( const char *filename, int mode, int use_seskey );
static int write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out );
/****************
* Encode FILENAME with only the symmetric cipher. Take input from
* stdin if FILENAME is NULL.
*/
int
encode_symmetric( const char *filename )
{
return encode_simple( filename, 1, 0 );
}
/****************
* Encode FILENAME as a literal data packet only. Take input from
* stdin if FILENAME is NULL.
*/
int
encode_store( const char *filename )
{
return encode_simple( filename, 0, 0 );
}
static void
encode_seskey( DEK *dek, DEK **seskey, byte *enckey )
{
CIPHER_HANDLE hd;
byte buf[33];
assert ( dek->keylen <= 32 );
if(!*seskey)
{
*seskey=xmalloc_clear(sizeof(DEK));
(*seskey)->keylen=dek->keylen;
(*seskey)->algo=dek->algo;
make_session_key(*seskey);
/*log_hexdump( "thekey", c->key, c->keylen );*/
}
buf[0] = (*seskey)->algo;
memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen );
hd = cipher_open( dek->algo, CIPHER_MODE_CFB, 1 );
cipher_setkey( hd, dek->key, dek->keylen );
cipher_setiv( hd, NULL, 0 );
cipher_encrypt( hd, buf, buf, (*seskey)->keylen + 1 );
cipher_close( hd );
memcpy( enckey, buf, (*seskey)->keylen + 1 );
wipememory( buf, sizeof buf ); /* burn key */
}
/* We try very hard to use a MDC */
static int
use_mdc(PK_LIST pk_list,int algo)
{
/* RFC-1991 and 2440 don't have MDC */
if(RFC1991 || RFC2440)
return 0;
/* --force-mdc overrides --disable-mdc */
if(opt.force_mdc)
return 1;
if(opt.disable_mdc)
return 0;
/* Do the keys really support MDC? */
if(select_mdc_from_pklist(pk_list))
return 1;
/* The keys don't support MDC, so now we do a bit of a hack - if any
of the AESes or TWOFISH are in the prefs, we assume that the user
can handle a MDC. This is valid for PGP 7, which can handle MDCs
though it will not generate them. 2440bis allows this, by the
way. */
if(select_algo_from_prefs(pk_list,PREFTYPE_SYM,
CIPHER_ALGO_AES,NULL)==CIPHER_ALGO_AES)
return 1;
if(select_algo_from_prefs(pk_list,PREFTYPE_SYM,
CIPHER_ALGO_AES192,NULL)==CIPHER_ALGO_AES192)
return 1;
if(select_algo_from_prefs(pk_list,PREFTYPE_SYM,
CIPHER_ALGO_AES256,NULL)==CIPHER_ALGO_AES256)
return 1;
if(select_algo_from_prefs(pk_list,PREFTYPE_SYM,
CIPHER_ALGO_TWOFISH,NULL)==CIPHER_ALGO_TWOFISH)
return 1;
/* Last try. Use MDC for the modern ciphers. */
if(cipher_get_blocksize(algo)!=8)
return 1;
return 0; /* No MDC */
}
/* We don't want to use use_seskey yet because older gnupg versions
can't handle it, and there isn't really any point unless we're
making a message that can be decrypted by a public key or
passphrase. */
static int
encode_simple( const char *filename, int mode, int use_seskey )
{
IOBUF inp, out;
PACKET pkt;
PKT_plaintext *pt = NULL;
STRING2KEY *s2k = NULL;
byte enckey[33];
int rc = 0;
int seskeylen = 0;
u32 filesize;
cipher_filter_context_t cfx;
armor_filter_context_t afx;
compress_filter_context_t zfx;
text_filter_context_t tfx;
progress_filter_context_t pfx;
int do_compress = !RFC1991 && default_compress_algo();
memset( &cfx, 0, sizeof cfx);
memset( &afx, 0, sizeof afx);
memset( &zfx, 0, sizeof zfx);
memset( &tfx, 0, sizeof tfx);
init_packet(&pkt);
/* prepare iobufs */
inp = iobuf_open(filename);
if (inp)
iobuf_ioctl (inp,3,1,NULL); /* disable fd caching */
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
errno = EPERM;
}
if( !inp ) {
log_error(_("can't open `%s': %s\n"), filename? filename: "[stdin]",
strerror(errno) );
return G10ERR_OPEN_FILE;
}
handle_progress (&pfx, inp, filename);
if( opt.textmode )
iobuf_push_filter( inp, text_filter, &tfx );
/* Due the the fact that we use don't use an IV to encrypt the
session key we can't use the new mode with RFC1991 because
it has no S2K salt. RFC1991 always uses simple S2K. */
if ( RFC1991 && use_seskey )
use_seskey = 0;
cfx.dek = NULL;
if( mode ) {
s2k = xmalloc_clear( sizeof *s2k );
s2k->mode = RFC1991? 0:opt.s2k_mode;
s2k->hash_algo=S2K_DIGEST_ALGO;
cfx.dek = passphrase_to_dek( NULL, 0,
default_cipher_algo(), s2k, 2,
NULL, NULL);
if( !cfx.dek || !cfx.dek->keylen ) {
rc = G10ERR_PASSPHRASE;
xfree(cfx.dek);
xfree(s2k);
iobuf_close(inp);
log_error(_("error creating passphrase: %s\n"), g10_errstr(rc) );
return rc;
}
if (use_seskey && s2k->mode != 1 && s2k->mode != 3) {
use_seskey = 0;
log_info (_("can't use a symmetric ESK packet "
"due to the S2K mode\n"));
}
if ( use_seskey )
{
DEK *dek = NULL;
seskeylen = cipher_get_keylen( default_cipher_algo() ) / 8;
encode_seskey( cfx.dek, &dek, enckey );
xfree( cfx.dek ); cfx.dek = dek;
}
if(opt.verbose)
log_info(_("using cipher %s\n"),
cipher_algo_to_string(cfx.dek->algo));
cfx.dek->use_mdc=use_mdc(NULL,cfx.dek->algo);
}
if (do_compress && cfx.dek && cfx.dek->use_mdc
&& is_file_compressed(filename, &rc))
{
if (opt.verbose)
log_info(_("`%s' already compressed\n"), filename);
do_compress = 0;
}
if( rc || (rc = open_outfile( filename, opt.armor? 1:0, &out )) ) {
iobuf_cancel(inp);
xfree(cfx.dek);
xfree(s2k);
return rc;
}
if( opt.armor )
iobuf_push_filter( out, armor_filter, &afx );
if( s2k && !RFC1991 ) {
PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc + seskeylen + 1 );
enc->version = 4;
enc->cipher_algo = cfx.dek->algo;
enc->s2k = *s2k;
if ( use_seskey && seskeylen ) {
enc->seskeylen = seskeylen + 1; /* algo id */
memcpy( enc->seskey, enckey, seskeylen + 1 );
}
pkt.pkttype = PKT_SYMKEY_ENC;
pkt.pkt.symkey_enc = enc;
if( (rc = build_packet( out, &pkt )) )
log_error("build symkey packet failed: %s\n", g10_errstr(rc) );
xfree(enc);
}
if (!opt.no_literal)
pt=setup_plaintext_name(filename,inp);
/* Note that PGP 5 has problems decrypting symmetrically encrypted
data if the file length is in the inner packet. It works when
only partial length headers are use. In the past, we always
used partial body length here, but since PGP 2, PGP 6, and PGP
7 need the file length, and nobody should be using PGP 5
nowadays anyway, this is now set to the file length. Note also
that this only applies to the RFC-1991 style symmetric
messages, and not the RFC-2440 style. PGP 6 and 7 work with
either partial length or fixed length with the new style
messages. */
if ( !iobuf_is_pipe_filename (filename) && *filename && !opt.textmode )
{
off_t tmpsize;
int overflow;
if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
&& !overflow && opt.verbose)
log_info(_("WARNING: `%s' is an empty file\n"), filename );
/* We can't encode the length of very large files because
OpenPGP uses only 32 bit for file sizes. So if the the
size of a file is larger than 2^32 minus some bytes for
packet headers, we switch to partial length encoding. */
if ( tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
filesize = tmpsize;
else
filesize = 0;
}
else
filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */
if (!opt.no_literal) {
pt->timestamp = make_timestamp();
pt->mode = opt.textmode? 't' : 'b';
pt->len = filesize;
pt->new_ctb = !pt->len && !RFC1991;
pt->buf = inp;
pkt.pkttype = PKT_PLAINTEXT;
pkt.pkt.plaintext = pt;
cfx.datalen = filesize && !do_compress ? calc_packet_length( &pkt ) : 0;
}
else
{
cfx.datalen = filesize && !do_compress ? filesize : 0;
pkt.pkttype = 0;
pkt.pkt.generic = NULL;
}
/* register the cipher filter */
if( mode )
iobuf_push_filter( out, cipher_filter, &cfx );
/* register the compress filter */
if( do_compress )
{
if (cfx.dek && cfx.dek->use_mdc)
zfx.new_ctb = 1;
push_compress_filter(out,&zfx,default_compress_algo());
}
/* do the work */
if (!opt.no_literal) {
if( (rc = build_packet( out, &pkt )) )
log_error("build_packet failed: %s\n", g10_errstr(rc) );
}
else {
/* user requested not to create a literal packet,
* so we copy the plain data */
byte copy_buffer[4096];
int bytes_copied;
while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1)
if (iobuf_write(out, copy_buffer, bytes_copied) == -1) {
rc = G10ERR_WRITE_FILE;
log_error("copying input to output failed: %s\n", g10_errstr(rc) );
break;
}
wipememory(copy_buffer, 4096); /* burn buffer */
}
/* finish the stuff */
iobuf_close(inp);
if (rc)
iobuf_cancel(out);
else {
iobuf_close(out); /* fixme: check returncode */
if (mode)
write_status( STATUS_END_ENCRYPTION );
}
if (pt)
pt->buf = NULL;
free_packet(&pkt);
xfree(cfx.dek);
xfree(s2k);
return rc;
}
int
setup_symkey(STRING2KEY **symkey_s2k,DEK **symkey_dek)
{
*symkey_s2k=xmalloc_clear(sizeof(STRING2KEY));
(*symkey_s2k)->mode = opt.s2k_mode;
(*symkey_s2k)->hash_algo = S2K_DIGEST_ALGO;
*symkey_dek=passphrase_to_dek(NULL,0,opt.s2k_cipher_algo,
*symkey_s2k,2,NULL,NULL);
if(!*symkey_dek || !(*symkey_dek)->keylen)
{
xfree(*symkey_dek);
xfree(*symkey_s2k);
return G10ERR_PASSPHRASE;
}
return 0;
}
static int
write_symkey_enc(STRING2KEY *symkey_s2k,DEK *symkey_dek,DEK *dek,IOBUF out)
{
int rc,seskeylen=cipher_get_keylen(dek->algo)/8;
PKT_symkey_enc *enc;
byte enckey[33];
PACKET pkt;
enc=xmalloc_clear(sizeof(PKT_symkey_enc)+seskeylen+1);
encode_seskey(symkey_dek,&dek,enckey);
enc->version = 4;
enc->cipher_algo = opt.s2k_cipher_algo;
enc->s2k = *symkey_s2k;
enc->seskeylen = seskeylen + 1; /* algo id */
memcpy( enc->seskey, enckey, seskeylen + 1 );
pkt.pkttype = PKT_SYMKEY_ENC;
pkt.pkt.symkey_enc = enc;
if((rc=build_packet(out,&pkt)))
log_error("build symkey_enc packet failed: %s\n",g10_errstr(rc));
xfree(enc);
return rc;
}
/****************
* Encrypt the file with the given userids (or ask if none
* is supplied).
*/
int
encode_crypt( const char *filename, STRLIST remusr, int use_symkey )
{
IOBUF inp = NULL, out = NULL;
PACKET pkt;
PKT_plaintext *pt = NULL;
DEK *symkey_dek = NULL;
STRING2KEY *symkey_s2k = NULL;
int rc = 0, rc2 = 0;
u32 filesize;
cipher_filter_context_t cfx;
armor_filter_context_t afx;
compress_filter_context_t zfx;
text_filter_context_t tfx;
progress_filter_context_t pfx;
PK_LIST pk_list,work_list;
int do_compress = opt.compress_algo && !RFC1991;
memset( &cfx, 0, sizeof cfx);
memset( &afx, 0, sizeof afx);
memset( &zfx, 0, sizeof zfx);
memset( &tfx, 0, sizeof tfx);
init_packet(&pkt);
if(use_symkey
&& (rc=setup_symkey(&symkey_s2k,&symkey_dek)))
return rc;
if( (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC)) )
return rc;
if(PGP2) {
for(work_list=pk_list; work_list; work_list=work_list->next)
if(!(is_RSA(work_list->pk->pubkey_algo) &&
nbits_from_pk(work_list->pk)<=2048))
{
log_info(_("you can only encrypt to RSA keys of 2048 bits or "
"less in --pgp2 mode\n"));
compliance_failure();
break;
}
}
/* prepare iobufs */
inp = iobuf_open(filename);
if (inp)
iobuf_ioctl (inp,3,1,NULL); /* disable fd caching */
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
errno = EPERM;
}
if( !inp ) {
log_error(_("can't open `%s': %s\n"), filename? filename: "[stdin]",
strerror(errno) );
rc = G10ERR_OPEN_FILE;
goto leave;
}
else if( opt.verbose )
log_info(_("reading from `%s'\n"), filename? filename: "[stdin]");
handle_progress (&pfx, inp, filename);
if( opt.textmode )
iobuf_push_filter( inp, text_filter, &tfx );
if( (rc = open_outfile( filename, opt.armor? 1:0, &out )) )
goto leave;
if( opt.armor )
iobuf_push_filter( out, armor_filter, &afx );
/* create a session key */
cfx.dek = xmalloc_secure_clear (sizeof *cfx.dek);
if( !opt.def_cipher_algo ) { /* try to get it from the prefs */
cfx.dek->algo = select_algo_from_prefs(pk_list,PREFTYPE_SYM,-1,NULL);
/* The only way select_algo_from_prefs can fail here is when
mixing v3 and v4 keys, as v4 keys have an implicit
preference entry for 3DES, and the pk_list cannot be empty.
In this case, use 3DES anyway as it's the safest choice -
perhaps the v3 key is being used in an OpenPGP
implementation and we know that the implementation behind
any v4 key can handle 3DES. */
if( cfx.dek->algo == -1 ) {
cfx.dek->algo = CIPHER_ALGO_3DES;
if( PGP2 ) {
log_info(_("unable to use the IDEA cipher for all of the keys "
"you are encrypting to.\n"));
compliance_failure();
}
}
}
else {
if(!opt.expert &&
select_algo_from_prefs(pk_list,PREFTYPE_SYM,
opt.def_cipher_algo,NULL)!=opt.def_cipher_algo)
log_info(_("WARNING: forcing symmetric cipher %s (%d)"
" violates recipient preferences\n"),
cipher_algo_to_string(opt.def_cipher_algo),
opt.def_cipher_algo);
cfx.dek->algo = opt.def_cipher_algo;
}
cfx.dek->use_mdc=use_mdc(pk_list,cfx.dek->algo);
/* Only do the is-file-already-compressed check if we are using a
MDC. This forces compressed files to be re-compressed if we do
not have a MDC to give some protection against chosen
ciphertext attacks. */
if (do_compress && cfx.dek->use_mdc && is_file_compressed(filename, &rc2) )
{
if (opt.verbose)
log_info(_("`%s' already compressed\n"), filename);
do_compress = 0;
}
if (rc2)
{
rc = rc2;
goto leave;
}
make_session_key( cfx.dek );
if( DBG_CIPHER )
log_hexdump("DEK is: ", cfx.dek->key, cfx.dek->keylen );
rc = write_pubkey_enc_from_list( pk_list, cfx.dek, out );
if( rc )
goto leave;
/* We put the passphrase (if any) after any public keys as this
seems to be the most useful on the recipient side - there is no
point in prompting a user for a passphrase if they have the
secret key needed to decrypt. */
if(use_symkey && (rc=write_symkey_enc(symkey_s2k,symkey_dek,cfx.dek,out)))
goto leave;
if (!opt.no_literal)
pt=setup_plaintext_name(filename,inp);
if (!iobuf_is_pipe_filename (filename) && *filename && !opt.textmode )
{
off_t tmpsize;
int overflow;
if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
&& !overflow && opt.verbose)
log_info(_("WARNING: `%s' is an empty file\n"), filename );
/* We can't encode the length of very large files because
OpenPGP uses only 32 bit for file sizes. So if the the
size of a file is larger than 2^32 minus some bytes for
packet headers, we switch to partial length encoding. */
if (tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
filesize = tmpsize;
else
filesize = 0;
}
else
filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */
if (!opt.no_literal) {
pt->timestamp = make_timestamp();
pt->mode = opt.textmode ? 't' : 'b';
pt->len = filesize;
pt->new_ctb = !pt->len && !RFC1991;
pt->buf = inp;
pkt.pkttype = PKT_PLAINTEXT;
pkt.pkt.plaintext = pt;
cfx.datalen = filesize && !do_compress? calc_packet_length( &pkt ) : 0;
}
else
cfx.datalen = filesize && !do_compress ? filesize : 0;
/* register the cipher filter */
iobuf_push_filter( out, cipher_filter, &cfx );
/* register the compress filter */
if( do_compress ) {
int compr_algo = opt.compress_algo;
if(compr_algo==-1)
{
if((compr_algo=
select_algo_from_prefs(pk_list,PREFTYPE_ZIP,-1,NULL))==-1)
compr_algo=DEFAULT_COMPRESS_ALGO;
/* Theoretically impossible to get here since uncompressed
is implicit. */
}
else if(!opt.expert &&
select_algo_from_prefs(pk_list,PREFTYPE_ZIP,
compr_algo,NULL)!=compr_algo)
log_info(_("WARNING: forcing compression algorithm %s (%d)"
" violates recipient preferences\n"),
compress_algo_to_string(compr_algo),compr_algo);
/* algo 0 means no compression */
if( compr_algo )
{
if (cfx.dek && cfx.dek->use_mdc)
zfx.new_ctb = 1;
push_compress_filter(out,&zfx,compr_algo);
}
}
/* do the work */
if (!opt.no_literal) {
if( (rc = build_packet( out, &pkt )) )
log_error("build_packet failed: %s\n", g10_errstr(rc) );
}
else {
/* user requested not to create a literal packet, so we copy
the plain data */
byte copy_buffer[4096];
int bytes_copied;
while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1)
if (iobuf_write(out, copy_buffer, bytes_copied) == -1) {
rc = G10ERR_WRITE_FILE;
log_error("copying input to output failed: %s\n",
g10_errstr(rc) );
break;
}
wipememory(copy_buffer, 4096); /* burn buffer */
}
/* finish the stuff */
leave:
iobuf_close(inp);
if( rc )
iobuf_cancel(out);
else {
iobuf_close(out); /* fixme: check returncode */
write_status( STATUS_END_ENCRYPTION );
}
if( pt )
pt->buf = NULL;
free_packet(&pkt);
xfree(cfx.dek);
xfree(symkey_dek);
xfree(symkey_s2k);
release_pk_list( pk_list );
return rc;
}
/****************
* Filter to do a complete public key encryption.
*/
int
encrypt_filter( void *opaque, int control,
IOBUF a, byte *buf, size_t *ret_len)
{
size_t size = *ret_len;
encrypt_filter_context_t *efx = opaque;
int rc=0;
if( control == IOBUFCTRL_UNDERFLOW ) { /* decrypt */
BUG(); /* not used */
}
else if( control == IOBUFCTRL_FLUSH ) { /* encrypt */
if( !efx->header_okay ) {
efx->cfx.dek = xmalloc_secure_clear( sizeof *efx->cfx.dek );
if( !opt.def_cipher_algo ) { /* try to get it from the prefs */
efx->cfx.dek->algo =
select_algo_from_prefs(efx->pk_list,PREFTYPE_SYM,-1,NULL);
if( efx->cfx.dek->algo == -1 ) {
/* because 3DES is implicitly in the prefs, this can only
* happen if we do not have any public keys in the list */
efx->cfx.dek->algo = DEFAULT_CIPHER_ALGO;
}
}
else {
if(!opt.expert &&
select_algo_from_prefs(efx->pk_list,PREFTYPE_SYM,
opt.def_cipher_algo,
NULL)!=opt.def_cipher_algo)
log_info(_("forcing symmetric cipher %s (%d) "
"violates recipient preferences\n"),
cipher_algo_to_string(opt.def_cipher_algo),
opt.def_cipher_algo);
efx->cfx.dek->algo = opt.def_cipher_algo;
}
efx->cfx.dek->use_mdc = use_mdc(efx->pk_list,efx->cfx.dek->algo);
make_session_key( efx->cfx.dek );
if( DBG_CIPHER )
log_hexdump("DEK is: ",
efx->cfx.dek->key, efx->cfx.dek->keylen );
rc = write_pubkey_enc_from_list( efx->pk_list, efx->cfx.dek, a );
if( rc )
return rc;
if(efx->symkey_s2k && efx->symkey_dek)
{
rc=write_symkey_enc(efx->symkey_s2k,efx->symkey_dek,
efx->cfx.dek,a);
if(rc)
return rc;
}
iobuf_push_filter( a, cipher_filter, &efx->cfx );
efx->header_okay = 1;
}
rc = iobuf_write( a, buf, size );
}
else if( control == IOBUFCTRL_FREE )
{
xfree(efx->symkey_dek);
xfree(efx->symkey_s2k);
}
else if( control == IOBUFCTRL_DESC ) {
- *(char**)buf = "encrypt_filter";
+ mem2str (buf, "encrypt_filter", *ret_len);
}
return rc;
}
/****************
* Write pubkey-enc packets from the list of PKs to OUT.
*/
static int
write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out )
{
PACKET pkt;
PKT_public_key *pk;
PKT_pubkey_enc *enc;
int rc;
for( ; pk_list; pk_list = pk_list->next ) {
MPI frame;
pk = pk_list->pk;
print_pubkey_algo_note( pk->pubkey_algo );
enc = xmalloc_clear( sizeof *enc );
enc->pubkey_algo = pk->pubkey_algo;
keyid_from_pk( pk, enc->keyid );
enc->throw_keyid = (opt.throw_keyid || (pk_list->flags&1));
if(opt.throw_keyid && (PGP2 || PGP6 || PGP7 || PGP8))
{
log_info(_("you may not use %s while in %s mode\n"),
"--throw-keyid",compliance_option_string());
compliance_failure();
}
/* Okay, what's going on: We have the session key somewhere in
* the structure DEK and want to encode this session key in
* an integer value of n bits. pubkey_nbits gives us the
* number of bits we have to use. We then encode the session
* key in some way and we get it back in the big intger value
* FRAME. Then we use FRAME, the public key PK->PKEY and the
* algorithm number PK->PUBKEY_ALGO and pass it to pubkey_encrypt
* which returns the encrypted value in the array ENC->DATA.
* This array has a size which depends on the used algorithm
* (e.g. 2 for Elgamal). We don't need frame anymore because we
* have everything now in enc->data which is the passed to
* build_packet()
*/
frame = encode_session_key( dek, pubkey_nbits( pk->pubkey_algo,
pk->pkey ) );
rc = pubkey_encrypt( pk->pubkey_algo, enc->data, frame, pk->pkey );
mpi_free( frame );
if( rc )
log_error("pubkey_encrypt failed: %s\n", g10_errstr(rc) );
else {
if( opt.verbose ) {
char *ustr = get_user_id_string_native (enc->keyid);
log_info(_("%s/%s encrypted for: \"%s\"\n"),
pubkey_algo_to_string(enc->pubkey_algo),
cipher_algo_to_string(dek->algo), ustr );
xfree(ustr);
}
/* and write it */
init_packet(&pkt);
pkt.pkttype = PKT_PUBKEY_ENC;
pkt.pkt.pubkey_enc = enc;
rc = build_packet( out, &pkt );
if( rc )
log_error("build_packet(pubkey_enc) failed: %s\n", g10_errstr(rc));
}
free_pubkey_enc(enc);
if( rc )
return rc;
}
return 0;
}
void
encode_crypt_files(int nfiles, char **files, STRLIST remusr)
{
int rc = 0;
if (opt.outfile)
{
log_error(_("--output doesn't work for this command\n"));
return;
}
if (!nfiles)
{
char line[2048];
unsigned int lno = 0;
while ( fgets(line, DIM(line), stdin) )
{
lno++;
if (!*line || line[strlen(line)-1] != '\n')
{
log_error("input line %u too long or missing LF\n", lno);
return;
}
line[strlen(line)-1] = '\0';
print_file_status(STATUS_FILE_START, line, 2);
if ( (rc = encode_crypt(line, remusr, 0)) )
log_error("encryption of `%s' failed: %s\n",
print_fname_stdin(line), g10_errstr(rc) );
write_status( STATUS_FILE_DONE );
iobuf_ioctl( NULL, 2, 0, NULL); /* Invalidate entire cache. */
}
}
else
{
while (nfiles--)
{
print_file_status(STATUS_FILE_START, *files, 2);
if ( (rc = encode_crypt(*files, remusr, 0)) )
log_error("encryption of `%s' failed: %s\n",
print_fname_stdin(*files), g10_errstr(rc) );
write_status( STATUS_FILE_DONE );
iobuf_ioctl( NULL, 2, 0, NULL); /* Invalidate entire cache. */
files++;
}
}
}
diff --git a/g10/encr-data.c b/g10/encr-data.c
index c65aa11b5..baa0606fe 100644
--- a/g10/encr-data.c
+++ b/g10/encr-data.c
@@ -1,336 +1,336 @@
/* encr-data.c - process an encrypted data packet
* Copyright (C) 1998, 1999, 2000, 2001, 2005,
* 2006 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 .
*/
#include
#include
#include
#include
#include
#include "util.h"
#include "memory.h"
#include "packet.h"
#include "mpi.h"
#include "cipher.h"
#include "options.h"
#include "i18n.h"
#include "status.h"
static int mdc_decode_filter( void *opaque, int control, IOBUF a,
byte *buf, size_t *ret_len);
static int decode_filter( void *opaque, int control, IOBUF a,
byte *buf, size_t *ret_len);
typedef struct {
CIPHER_HANDLE cipher_hd;
MD_HANDLE mdc_hash;
char defer[22];
int defer_filled;
int eof_seen;
int refcount;
} *decode_filter_ctx_t;
/* Helper to release the decode context. */
static void
release_dfx_context (decode_filter_ctx_t dfx)
{
if (!dfx)
return;
assert (dfx->refcount);
if ( !--dfx->refcount )
{
cipher_close (dfx->cipher_hd);
dfx->cipher_hd = NULL;
md_close (dfx->mdc_hash);
dfx->mdc_hash = NULL;
xfree (dfx);
}
}
/****************
* Decrypt the data, specified by ED with the key DEK.
*/
int
decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
{
decode_filter_ctx_t dfx;
byte *p;
int rc=0, c, i;
byte temp[32];
unsigned blocksize;
unsigned nprefix;
dfx = xcalloc (1, sizeof *dfx);
dfx->refcount = 1;
if( opt.verbose && !dek->algo_info_printed ) {
const char *s = cipher_algo_to_string( dek->algo );
if( s )
log_info(_("%s encrypted data\n"), s );
else
log_info(_("encrypted with unknown algorithm %d\n"), dek->algo );
dek->algo_info_printed = 1;
}
{
char buf[20];
snprintf (buf, sizeof buf, "%d %d", ed->mdc_method, dek->algo);
write_status_text (STATUS_DECRYPTION_INFO, buf);
}
if (opt.show_session_key)
{
char *buf = xmalloc (dek->keylen*2 + 20);
sprintf (buf, "%d:", dek->algo);
for (i=0; i < dek->keylen; i++ )
sprintf(buf+strlen(buf), "%02X", dek->key[i] );
log_info ("session key: `%s'\n", buf);
write_status_text (STATUS_SESSION_KEY, buf);
}
if( (rc=check_cipher_algo(dek->algo)) )
goto leave;
blocksize = cipher_get_blocksize(dek->algo);
if( !blocksize || blocksize > 16 )
log_fatal("unsupported blocksize %u\n", blocksize );
nprefix = blocksize;
if( ed->len && ed->len < (nprefix+2) )
BUG();
if( ed->mdc_method ) {
dfx->mdc_hash = md_open ( ed->mdc_method, 0 );
if ( DBG_HASHING )
md_start_debug (dfx->mdc_hash, "checkmdc");
}
dfx->cipher_hd = cipher_open ( dek->algo,
ed->mdc_method? CIPHER_MODE_CFB
: CIPHER_MODE_AUTO_CFB, 1 );
/* log_hexdump( "thekey", dek->key, dek->keylen );*/
rc = cipher_setkey ( dfx->cipher_hd, dek->key, dek->keylen );
if( rc == G10ERR_WEAK_KEY )
{
log_info(_("WARNING: message was encrypted with"
" a weak key in the symmetric cipher.\n"));
rc=0;
}
else if( rc )
{
log_error("key setup failed: %s\n", g10_errstr(rc) );
goto leave;
}
if (!ed->buf) {
log_error(_("problem handling encrypted packet\n"));
goto leave;
}
cipher_setiv ( dfx->cipher_hd, NULL, 0 );
if( ed->len ) {
for(i=0; i < (nprefix+2) && ed->len; i++, ed->len-- ) {
if( (c=iobuf_get(ed->buf)) == -1 )
break;
else
temp[i] = c;
}
}
else {
for(i=0; i < (nprefix+2); i++ )
if( (c=iobuf_get(ed->buf)) == -1 )
break;
else
temp[i] = c;
}
cipher_decrypt ( dfx->cipher_hd, temp, temp, nprefix+2);
cipher_sync ( dfx->cipher_hd );
p = temp;
/* log_hexdump( "prefix", temp, nprefix+2 ); */
if(dek->symmetric
&& (p[nprefix-2] != p[nprefix] || p[nprefix-1] != p[nprefix+1]) )
{
rc = G10ERR_BAD_KEY;
goto leave;
}
if ( dfx->mdc_hash )
md_write ( dfx->mdc_hash, temp, nprefix+2 );
dfx->refcount++;
if ( ed->mdc_method )
iobuf_push_filter( ed->buf, mdc_decode_filter, dfx );
else
iobuf_push_filter( ed->buf, decode_filter, dfx );
proc_packets( procctx, ed->buf );
ed->buf = NULL;
if( ed->mdc_method && dfx->eof_seen == 2 )
rc = G10ERR_INVALID_PACKET;
else if( ed->mdc_method ) { /* check the mdc */
/* We used to let parse-packet.c handle the MDC packet but
this turned out to be a problem with compressed packets:
With old style packets there is no length information
available and the decompressor uses an implicit end.
However we can't know this implicit end beforehand (:-) and
thus may feed the decompressor with more bytes than
actually needed. It would be possible to unread the extra
bytes but due to our weird iobuf system any unread is non
reliable due to filters already popped off. The easy and
sane solution is to care about the MDC packet only here and
never pass it to the packet parser. Fortunatley the
OpenPGP spec requires a strict format for the MDC packet so
that we know that 22 bytes are appended. */
int datalen = md_digest_length( ed->mdc_method );
assert (dfx->cipher_hd);
assert (dfx->mdc_hash);
cipher_decrypt ( dfx->cipher_hd, dfx->defer, dfx->defer, 22);
md_write ( dfx->mdc_hash, dfx->defer, 2);
md_final ( dfx->mdc_hash );
if ( dfx->defer[0] != '\xd3'
|| dfx->defer[1] != '\x14'
|| datalen != 20
|| memcmp (md_read (dfx->mdc_hash, 0 ), dfx->defer+2, datalen))
rc = G10ERR_BAD_SIGN;
/*log_hexdump("MDC calculated:",md_read( dfx->mdc_hash, 0), datalen);*/
/*log_hexdump("MDC message :", dfx->defer, 20);*/
}
leave:
release_dfx_context (dfx);
return rc;
}
/* I think we should merge this with cipher_filter */
static int
mdc_decode_filter( void *opaque, int control, IOBUF a,
byte *buf, size_t *ret_len)
{
decode_filter_ctx_t dfx = opaque;
size_t n, size = *ret_len;
int rc = 0;
int c;
if( control == IOBUFCTRL_UNDERFLOW && dfx->eof_seen ) {
*ret_len = 0;
rc = -1;
}
else if( control == IOBUFCTRL_UNDERFLOW ) {
assert(a);
assert( size > 44 );
/* get at least 20 bytes and put it somewhere ahead in the buffer */
for(n=22; n < 44 ; n++ ) {
if( (c = iobuf_get(a)) == -1 )
break;
buf[n] = c;
}
if( n == 44 ) {
/* we have enough stuff - flush the deferred stuff */
/* (we have asserted that the buffer is large enough) */
if( !dfx->defer_filled ) { /* the first time */
memcpy(buf, buf+22, 22 );
n = 22;
}
else {
memcpy(buf, dfx->defer, 22 );
}
/* now fill up */
for(; n < size; n++ ) {
if( (c = iobuf_get(a)) == -1 )
break;
buf[n] = c;
}
/* Move the last 22 bytes back to the defer buffer. */
/* (okay, we are wasting 22 bytes of supplied buffer) */
n -= 22;
memcpy( dfx->defer, buf+n, 22 );
dfx->defer_filled = 1;
}
else if( !dfx->defer_filled ) { /* eof seen buf empty defer */
/* this is bad because there is an incomplete hash */
n -= 22;
memcpy(buf, buf+22, n );
dfx->eof_seen = 2; /* eof with incomplete hash */
}
else { /* eof seen */
memcpy (buf, dfx->defer, 22 );
n -= 22;
memcpy( dfx->defer, buf+n, 22 );
dfx->eof_seen = 1; /* normal eof */
}
if( n ) {
if (dfx->cipher_hd)
cipher_decrypt( dfx->cipher_hd, buf, buf, n);
if (dfx->mdc_hash)
md_write( dfx->mdc_hash, buf, n );
}
else {
assert( dfx->eof_seen );
rc = -1; /* eof */
}
*ret_len = n;
}
else if ( control == IOBUFCTRL_FREE ) {
release_dfx_context (dfx);
}
else if( control == IOBUFCTRL_DESC ) {
- *(char**)buf = "mdc_decode_filter";
+ mem2str (buf, "mdc_decode_filter", *ret_len);
}
return rc;
}
static int
decode_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len)
{
decode_filter_ctx_t fc = opaque;
size_t n, size = *ret_len;
int rc = 0;
if( control == IOBUFCTRL_UNDERFLOW ) {
assert(a);
n = iobuf_read( a, buf, size );
if (n == (size_t)(-1))
n = 0;
if( n ) {
if (fc->cipher_hd)
cipher_decrypt( fc->cipher_hd, buf, buf, n);
}
else
rc = -1; /* eof */
*ret_len = n;
}
else if ( control == IOBUFCTRL_FREE ) {
release_dfx_context (fc);
}
else if( control == IOBUFCTRL_DESC ) {
- *(char**)buf = "decode_filter";
+ mem2str (buf, "decode_filter", *ret_len);
}
return rc;
}
diff --git a/g10/mdfilter.c b/g10/mdfilter.c
index 9c0059e05..bb47f98cd 100644
--- a/g10/mdfilter.c
+++ b/g10/mdfilter.c
@@ -1,75 +1,75 @@
/* mdfilter.c - filter data and calculate a message digest
* Copyright (C) 1998, 1999, 2000, 2001 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 .
*/
#include
#include
#include
#include
#include
#include
#include "errors.h"
#include "iobuf.h"
#include "memory.h"
#include "util.h"
#include "filter.h"
/****************
* This filter is used to collect a message digest
*/
int
md_filter( void *opaque, int control,
IOBUF a, byte *buf, size_t *ret_len)
{
size_t size = *ret_len;
md_filter_context_t *mfx = opaque;
int i, rc=0;
if( control == IOBUFCTRL_UNDERFLOW ) {
if( mfx->maxbuf_size && size > mfx->maxbuf_size )
size = mfx->maxbuf_size;
i = iobuf_read( a, buf, size );
if( i == -1 ) i = 0;
if( i ) {
md_write(mfx->md, buf, i );
if( mfx->md2 )
md_write(mfx->md2, buf, i );
}
else
rc = -1; /* eof */
*ret_len = i;
}
else if( control == IOBUFCTRL_DESC )
- *(char**)buf = "md_filter";
+ mem2str (buf, "md_filter", *ret_len);
return rc;
}
void
free_md_filter_context( md_filter_context_t *mfx )
{
md_close(mfx->md);
md_close(mfx->md2);
mfx->md = NULL;
mfx->md2 = NULL;
mfx->maxbuf_size = 0;
}
diff --git a/g10/pipemode.c b/g10/pipemode.c
index 077f967cd..60c802060 100644
--- a/g10/pipemode.c
+++ b/g10/pipemode.c
@@ -1,315 +1,315 @@
/* pipemode.c - pipemode handler
* Copyright (C) 1998, 1990, 2000, 2001 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 .
*/
#include
#include
#include
#include
#include
#include
#include "options.h"
#include "packet.h"
#include "errors.h"
#include "iobuf.h"
#include "keydb.h"
#include "memory.h"
#include "util.h"
#include "main.h"
#include "status.h"
#include "filter.h"
#define CONTROL_PACKET_SPACE 30
#define FAKED_LITERAL_PACKET_SPACE (9+2+2)
enum pipemode_state_e {
STX_init = 0,
STX_wait_operation,
STX_begin,
STX_text,
STX_detached_signature,
STX_detached_signature_wait_text,
STX_signed_data,
STX_wait_init
};
struct pipemode_context_s {
enum pipemode_state_e state;
int operation;
int stop;
int block_mode;
UnarmorPump unarmor_ctx;
};
static size_t
make_control ( byte *buf, int code, int operation )
{
const byte *sesmark;
size_t sesmarklen, n=0;;
sesmark = get_session_marker( &sesmarklen );
if ( sesmarklen > 20 )
BUG();
buf[n++] = 0xff; /* new format, type 63, 1 length byte */
n++; /* length will fixed below */
memcpy(buf+n, sesmark, sesmarklen ); n+= sesmarklen;
buf[n++] = CTRLPKT_PIPEMODE;
buf[n++] = code;
buf[n++] = operation;
buf[1] = n-2;
return n;
}
static int
pipemode_filter( void *opaque, int control,
IOBUF a, byte *buf, size_t *ret_len)
{
size_t size = *ret_len;
struct pipemode_context_s *stx = opaque;
int rc=0;
size_t n = 0;
int esc = 0;
if( control == IOBUFCTRL_UNDERFLOW ) {
*ret_len = 0;
/* reserve some space for one control packet */
if ( size <= CONTROL_PACKET_SPACE+FAKED_LITERAL_PACKET_SPACE )
BUG();
size -= CONTROL_PACKET_SPACE+FAKED_LITERAL_PACKET_SPACE;
if ( stx->block_mode ) {
/* reserve 2 bytes for the block length */
buf[n++] = 0;
buf[n++] = 0;
}
while ( n < size ) {
/* FIXME: we have to make sure that we have a large enough
* buffer for a control packet even after we already read
* something. The easest way to do this is probably by ungetting
* the control sequence and returning the buffer we have
* already assembled */
int c = iobuf_get (a);
if (c == -1) {
if ( stx->state != STX_init ) {
log_error ("EOF encountered at wrong state\n");
stx->stop = 1;
return -1;
}
break;
}
if ( esc ) {
switch (c) {
case '@':
if ( stx->state == STX_text ) {
buf[n++] = c;
break;
}
else if ( stx->state == STX_detached_signature ) {
esc = 0;
goto do_unarmor; /* not a very elegant solution */
}
else if ( stx->state == STX_detached_signature_wait_text) {
esc = 0;
break; /* just ignore it in this state */
}
log_error ("@@ not allowed in current state\n");
return -1;
case '<': /* begin of stream part */
if ( stx->state != STX_init ) {
log_error ("nested begin of stream\n");
stx->stop = 1;
return -1;
}
stx->state = STX_wait_operation;
stx->block_mode = 0;
unarmor_pump_release (stx->unarmor_ctx);
stx->unarmor_ctx = NULL;
break;
case '>': /* end of stream part */
if ( stx->state != STX_wait_init ) {
log_error ("invalid state for @>\n");
stx->stop = 1;
return -1;
}
stx->state = STX_init;
break;
case 'V': /* operation = verify */
case 'E': /* operation = encrypt */
case 'S': /* operation = sign */
case 'B': /* operation = detach sign */
case 'C': /* operation = clearsign */
case 'D': /* operation = decrypt */
if ( stx->state != STX_wait_operation ) {
log_error ("invalid state for operation code\n");
stx->stop = 1;
return -1;
}
stx->operation = c;
if ( stx->operation == 'B') {
stx->state = STX_detached_signature;
if ( !opt.no_armor )
stx->unarmor_ctx = unarmor_pump_new ();
}
else
stx->state = STX_begin;
n += make_control ( buf+n, 1, stx->operation );
/* must leave after a control packet */
goto leave;
case 't': /* plaintext text follows */
if ( stx->state == STX_detached_signature_wait_text )
stx->state = STX_detached_signature;
if ( stx->state == STX_detached_signature ) {
if ( stx->operation != 'B' ) {
log_error ("invalid operation for this state\n");
stx->stop = 1;
return -1;
}
stx->state = STX_signed_data;
n += make_control ( buf+n, 2, 'B' );
/* and now we fake a literal data packet much the same
* as in armor.c */
buf[n++] = 0xaf; /* old packet format, type 11,
var length */
buf[n++] = 0; /* set the length header */
buf[n++] = 6;
buf[n++] = 'b'; /* we ignore it anyway */
buf[n++] = 0; /* namelength */
memset(buf+n, 0, 4); /* timestamp */
n += 4;
/* and return now so that we are sure to have
* more space in the bufer for the next control
* packet */
stx->block_mode = 1;
goto leave2;
}
else {
log_error ("invalid state for @t\n");
stx->stop = 1;
return -1;
}
break;
case '.': /* ready */
if ( stx->state == STX_signed_data ) {
if (stx->block_mode) {
buf[0] = (n-2) >> 8;
buf[1] = (n-2);
if ( buf[0] || buf[1] ) {
/* end of blocks marker */
buf[n++] = 0;
buf[n++] = 0;
}
stx->block_mode = 0;
}
n += make_control ( buf+n, 3, 'B' );
}
else {
log_error ("invalid state for @.\n");
stx->stop = 1;
return -1;
}
stx->state = STX_wait_init;
goto leave;
default:
log_error ("invalid escape sequence 0x%02x in stream\n",
c);
stx->stop = 1;
return -1;
}
esc = 0;
}
else if (c == '@')
esc = 1;
else if (stx->unarmor_ctx) {
do_unarmor: /* used to handle a @@ */
c = unarmor_pump (stx->unarmor_ctx, c);
if ( !(c & ~255) )
buf[n++] = c;
else if ( c < 0 ) {
/* end of armor or error - we don't care becuase
the armor can be modified anyway. The unarmored
stuff should stand for itself. */
unarmor_pump_release (stx->unarmor_ctx);
stx->unarmor_ctx = NULL;
stx->state = STX_detached_signature_wait_text;
}
}
else if (stx->state == STX_detached_signature_wait_text)
; /* just wait */
else
buf[n++] = c;
}
leave:
if ( !n ) {
stx->stop = 1;
rc = -1; /* eof */
}
if ( stx->block_mode ) {
/* fixup the block length */
buf[0] = (n-2) >> 8;
buf[1] = (n-2);
}
leave2:
/*log_hexdump ("pipemode:", buf, n );*/
*ret_len = n;
}
else if( control == IOBUFCTRL_DESC )
- *(char**)buf = "pipemode_filter";
+ mem2str (buf, "pipemode_filter", *ret_len);
return rc;
}
void
run_in_pipemode(void)
{
IOBUF fp;
armor_filter_context_t afx;
struct pipemode_context_s stx;
memset( &afx, 0, sizeof afx);
memset( &stx, 0, sizeof stx);
fp = iobuf_open("-");
iobuf_push_filter (fp, pipemode_filter, &stx );
do {
write_status (STATUS_BEGIN_STREAM);
proc_packets( NULL, fp );
write_status (STATUS_END_STREAM);
} while ( !stx.stop );
}
diff --git a/g10/progress.c b/g10/progress.c
index 8c8265f36..cea080c0e 100644
--- a/g10/progress.c
+++ b/g10/progress.c
@@ -1,118 +1,118 @@
/* progress.c
* Copyright (C) 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
#include
#include
#include "iobuf.h"
#include "filter.h"
#include "status.h"
#include "util.h"
#include "options.h"
/****************
* The filter is used to report progress to the user.
*/
int
progress_filter (void *opaque, int control,
IOBUF a, byte *buf, size_t *ret_len)
{
int rc = 0;
progress_filter_context_t *pfx = opaque;
if (control == IOBUFCTRL_INIT)
{
char buffer[50];
pfx->last = 0;
pfx->offset = 0;
pfx->last_time = make_timestamp ();
sprintf (buffer, "%.20s ? %lu %lu",
pfx->what? pfx->what : "?",
pfx->offset,
pfx->total);
write_status_text (STATUS_PROGRESS, buffer);
}
else if (control == IOBUFCTRL_UNDERFLOW)
{
u32 timestamp = make_timestamp ();
int len = iobuf_read (a, buf, *ret_len);
if (len >= 0)
{
pfx->offset += len;
*ret_len = len;
}
else
{
*ret_len = 0;
rc = -1;
}
if ((len == -1 && pfx->offset != pfx->last)
|| timestamp - pfx->last_time > 0)
{
char buffer[50];
sprintf (buffer, "%.20s ? %lu %lu",
pfx->what? pfx->what : "?",
pfx->offset,
pfx->total);
write_status_text (STATUS_PROGRESS, buffer);
pfx->last = pfx->offset;
pfx->last_time = timestamp;
}
}
else if (control == IOBUFCTRL_FREE)
{
/* Note, that we must always dealloc resources of a filter
within the filter handler and not anywhere else. (We set it
to NULL and check all uses just in case.) */
xfree (pfx->what);
pfx->what = NULL;
}
else if (control == IOBUFCTRL_DESC)
- *(char**)buf = "progress_filter";
+ mem2str (buf, "progress_filter", *ret_len);
return rc;
}
void
handle_progress (progress_filter_context_t *pfx, IOBUF inp, const char *name)
{
off_t filesize = 0;
if (!opt.enable_progress_filter)
return;
if (!is_status_enabled ())
return;
if ( !iobuf_is_pipe_filename (name) && *name )
filesize = iobuf_get_filelength (inp, NULL);
else if (opt.set_filesize)
filesize = opt.set_filesize;
/* register the progress filter */
pfx->what = xstrdup (name ? name : "stdin");
pfx->total = filesize;
iobuf_push_filter (inp, progress_filter, pfx);
}
diff --git a/g10/textfilter.c b/g10/textfilter.c
index dc72a56fc..79f2f67dc 100644
--- a/g10/textfilter.c
+++ b/g10/textfilter.c
@@ -1,249 +1,249 @@
/* textfilter.c
* Copyright (C) 1998, 1999, 2000, 2001, 2004 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 .
*/
#include
#include
#include
#include
#include
#include
#include "errors.h"
#include "iobuf.h"
#include "memory.h"
#include "util.h"
#include "filter.h"
#include "i18n.h"
#include "options.h"
#include "status.h"
#ifdef HAVE_DOSISH_SYSTEM
#define LF "\r\n"
#else
#define LF "\n"
#endif
#define MAX_LINELEN 19995 /* a little bit smaller than in armor.c */
/* to make sure that a warning is displayed while */
/* creating a message */
static unsigned
len_without_trailing_chars( byte *line, unsigned len, const char *trimchars )
{
byte *p, *mark;
unsigned n;
for(mark=NULL, p=line, n=0; n < len; n++, p++ ) {
if( strchr( trimchars, *p ) ) {
if( !mark )
mark = p;
}
else
mark = NULL;
}
return mark? (mark - line) : len;
}
static int
standard( text_filter_context_t *tfx, IOBUF a,
byte *buf, size_t size, size_t *ret_len)
{
int rc=0;
size_t len = 0;
unsigned maxlen;
assert( size > 10 );
size -= 2; /* reserve 2 bytes to append CR,LF */
while( !rc && len < size ) {
int lf_seen;
while( len < size && tfx->buffer_pos < tfx->buffer_len )
buf[len++] = tfx->buffer[tfx->buffer_pos++];
if( len >= size )
continue;
/* read the next line */
maxlen = MAX_LINELEN;
tfx->buffer_pos = 0;
tfx->buffer_len = iobuf_read_line( a, &tfx->buffer,
&tfx->buffer_size, &maxlen );
if( !maxlen )
tfx->truncated++;
if( !tfx->buffer_len ) {
if( !len )
rc = -1; /* eof */
break;
}
lf_seen = tfx->buffer[tfx->buffer_len-1] == '\n';
/* The story behind this is that 2440 says that textmode
hashes should canonicalize line endings to CRLF and remove
spaces and tabs. 2440bis-12 says to just canonicalize to
CRLF. 1.4.0 was released using the bis-12 behavior, but it
was discovered that many mail clients do not canonicalize
PGP/MIME signature text appropriately (and were relying on
GnuPG to handle trailing spaces). So, we default to the
2440 behavior, but use the 2440bis-12 behavior if the user
specifies --no-rfc2440-text. The default will be changed
at some point in the future when the mail clients have been
upgraded. Aside from PGP/MIME and broken mail clients,
this makes no difference to any signatures in the real
world except for a textmode detached signature. PGP always
used the 2440bis-12 behavior (ignoring 2440 itself), so
this actually makes us compatible with PGP textmode
detached signatures for the first time. */
if(opt.rfc2440_text)
tfx->buffer_len=trim_trailing_chars(tfx->buffer,tfx->buffer_len,
" \t\r\n");
else
tfx->buffer_len=trim_trailing_chars(tfx->buffer,tfx->buffer_len,
"\r\n");
if( lf_seen ) {
tfx->buffer[tfx->buffer_len++] = '\r';
tfx->buffer[tfx->buffer_len++] = '\n';
}
}
*ret_len = len;
return rc;
}
/****************
* The filter is used to make canonical text: Lines are terminated by
* CR, LF, trailing white spaces are removed.
*/
int
text_filter( void *opaque, int control,
IOBUF a, byte *buf, size_t *ret_len)
{
size_t size = *ret_len;
text_filter_context_t *tfx = opaque;
int rc=0;
if( control == IOBUFCTRL_UNDERFLOW ) {
rc = standard( tfx, a, buf, size, ret_len );
}
else if( control == IOBUFCTRL_FREE ) {
if( tfx->truncated )
log_error(_("can't handle text lines longer than %d characters\n"),
MAX_LINELEN );
xfree( tfx->buffer );
tfx->buffer = NULL;
}
else if( control == IOBUFCTRL_DESC )
- *(char**)buf = "text_filter";
+ mem2str (buf, "text_filter", *ret_len);
return rc;
}
/****************
* Copy data from INP to OUT and do some escaping if requested.
* md is updated as required by rfc2440
*/
int
copy_clearsig_text( IOBUF out, IOBUF inp, MD_HANDLE md,
int escape_dash, int escape_from, int pgp2mode )
{
unsigned maxlen;
byte *buffer = NULL; /* malloced buffer */
unsigned bufsize; /* and size of this buffer */
unsigned n;
int truncated = 0;
int pending_lf = 0;
if( !opt.pgp2_workarounds )
pgp2mode = 0;
if( !escape_dash )
escape_from = 0;
write_status (STATUS_BEGIN_SIGNING);
for(;;) {
maxlen = MAX_LINELEN;
n = iobuf_read_line( inp, &buffer, &bufsize, &maxlen );
if( !maxlen )
truncated++;
if( !n )
break; /* read_line has returned eof */
/* update the message digest */
if( escape_dash ) {
if( pending_lf ) {
md_putc( md, '\r' );
md_putc( md, '\n' );
}
md_write( md, buffer,
len_without_trailing_chars( buffer, n,
pgp2mode? " \r\n":" \t\r\n"));
}
else
md_write( md, buffer, n );
pending_lf = buffer[n-1] == '\n';
/* write the output */
if( ( escape_dash && *buffer == '-')
|| ( escape_from && n > 4 && !memcmp(buffer, "From ", 5 ) ) ) {
iobuf_put( out, '-' );
iobuf_put( out, ' ' );
}
#if 0 /*defined(HAVE_DOSISH_SYSTEM)*/
/* We don't use this anymore because my interpretation of rfc2440 7.1
* is that there is no conversion needed. If one decides to
* clearsign a unix file on a DOS box he will get a mixed line endings.
* If at some point it turns out, that a conversion is a nice feature
* we can make an option out of it.
*/
/* make sure the lines do end in CR,LF */
if( n > 1 && ( (buffer[n-2] == '\r' && buffer[n-1] == '\n' )
|| (buffer[n-2] == '\n' && buffer[n-1] == '\r'))) {
iobuf_write( out, buffer, n-2 );
iobuf_put( out, '\r');
iobuf_put( out, '\n');
}
else if( n && buffer[n-1] == '\n' ) {
iobuf_write( out, buffer, n-1 );
iobuf_put( out, '\r');
iobuf_put( out, '\n');
}
else
iobuf_write( out, buffer, n );
#else
iobuf_write( out, buffer, n );
#endif
}
/* at eof */
if( !pending_lf ) { /* make sure that the file ends with a LF */
iobuf_writestr( out, LF );
if( !escape_dash )
md_putc( md, '\n' );
}
if( truncated )
log_info(_("input line longer than %d characters\n"), MAX_LINELEN );
return 0; /* okay */
}
diff --git a/include/iobuf.h b/include/iobuf.h
index 9515a0e10..030f8c8e9 100644
--- a/include/iobuf.h
+++ b/include/iobuf.h
@@ -1,161 +1,160 @@
/* iobuf.h - I/O buffer
* Copyright (C) 1998, 1999, 2000, 2001, 2004 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 .
*/
#ifndef G10_IOBUF_H
#define G10_IOBUF_H
#include "types.h"
#define DBG_IOBUF iobuf_debug_mode
#define IOBUFCTRL_INIT 1
#define IOBUFCTRL_FREE 2
#define IOBUFCTRL_UNDERFLOW 3
#define IOBUFCTRL_FLUSH 4
#define IOBUFCTRL_DESC 5
#define IOBUFCTRL_CANCEL 6
#define IOBUFCTRL_USER 16
typedef struct iobuf_struct *IOBUF;
typedef struct iobuf_struct *iobuf_t;
/* fixme: we should hide most of this stuff */
struct iobuf_struct {
int use; /* 1 input , 2 output, 3 temp */
off_t nlimit;
off_t nbytes; /* used together with nlimit */
off_t ntotal; /* total bytes read (position of stream) */
int nofast; /* used by the iobuf_get() */
void *directfp;
struct {
size_t size; /* allocated size */
size_t start; /* number of invalid bytes at the begin of the buffer */
size_t len; /* currently filled to this size */
byte *buf;
} d;
int filter_eof;
int error;
int (*filter)( void *opaque, int control,
IOBUF chain, byte *buf, size_t *len);
void *filter_ov; /* value for opaque */
int filter_ov_owner;
char *real_fname;
IOBUF chain; /* next iobuf used for i/o if any (passed to filter) */
int no, subno;
- const char *desc;
void *opaque; /* can be used to hold any information */
/* this value is copied to all instances */
struct {
size_t size; /* allocated size */
size_t start; /* number of invalid bytes at the begin of the buffer */
size_t len; /* currently filled to this size */
byte *buf;
} unget;
};
#ifndef EXTERN_UNLESS_MAIN_MODULE
#if defined (__riscos__) && !defined (INCLUDED_BY_MAIN_MODULE)
#define EXTERN_UNLESS_MAIN_MODULE extern
#else
#define EXTERN_UNLESS_MAIN_MODULE
#endif
#endif
EXTERN_UNLESS_MAIN_MODULE int iobuf_debug_mode;
void iobuf_enable_special_filenames ( int yes );
int iobuf_is_pipe_filename (const char *fname);
IOBUF iobuf_alloc(int use, size_t bufsize);
IOBUF iobuf_temp(void);
IOBUF iobuf_temp_with_content( const char *buffer, size_t length );
IOBUF iobuf_open( const char *fname );
IOBUF iobuf_fdopen( int fd, const char *mode );
IOBUF iobuf_sockopen( int fd, const char *mode );
IOBUF iobuf_create( const char *fname );
IOBUF iobuf_append( const char *fname );
IOBUF iobuf_openrw( const char *fname );
int iobuf_ioctl ( IOBUF a, int cmd, int intval, void *ptrval );
int iobuf_close( IOBUF iobuf );
int iobuf_cancel( IOBUF iobuf );
int iobuf_push_filter( IOBUF a, int (*f)(void *opaque, int control,
IOBUF chain, byte *buf, size_t *len), void *ov );
int iobuf_push_filter2( IOBUF a,
int (*f)(void *opaque, int control,
IOBUF chain, byte *buf, size_t *len),
void *ov, int rel_ov );
int iobuf_flush(IOBUF a);
void iobuf_clear_eof(IOBUF a);
#define iobuf_set_error(a) do { (a)->error = 1; } while(0)
#define iobuf_error(a) ((a)->error)
void iobuf_set_limit( IOBUF a, off_t nlimit );
off_t iobuf_tell( IOBUF a );
int iobuf_seek( IOBUF a, off_t newpos );
int iobuf_readbyte(IOBUF a);
int iobuf_read(IOBUF a, byte *buf, unsigned buflen );
unsigned iobuf_read_line( IOBUF a, byte **addr_of_buffer,
unsigned *length_of_buffer, unsigned *max_length );
int iobuf_peek(IOBUF a, byte *buf, unsigned buflen );
int iobuf_writebyte(IOBUF a, unsigned c);
int iobuf_write(IOBUF a, byte *buf, unsigned buflen );
int iobuf_writestr(IOBUF a, const char *buf );
void iobuf_flush_temp( IOBUF temp );
int iobuf_write_temp( IOBUF a, IOBUF temp );
size_t iobuf_temp_to_buffer( IOBUF a, byte *buffer, size_t buflen );
void iobuf_unget_and_close_temp( IOBUF a, IOBUF temp );
int iobuf_get_fd (IOBUF a);
off_t iobuf_get_filelength (IOBUF a, int *overflow);
#define IOBUF_FILELENGTH_LIMIT 0xffffffff
const char *iobuf_get_real_fname( IOBUF a );
const char *iobuf_get_fname( IOBUF a );
void iobuf_set_partial_block_mode( IOBUF a, size_t len );
int iobuf_translate_file_handle ( int fd, int for_write );
/* Get a byte form the iobuf; must check for eof prior to this function.
* This function returns values in the range 0 .. 255 or -1 to indicate EOF
* iobuf_get_noeof() does not return -1 to indicate EOF, but masks the
* returned value to be in the range 0..255.
*/
#define iobuf_get(a) \
( ((a)->nofast || (a)->d.start >= (a)->d.len )? \
iobuf_readbyte((a)) : ( (a)->nbytes++, (a)->d.buf[(a)->d.start++] ) )
#define iobuf_get_noeof(a) (iobuf_get((a))&0xff)
/* write a byte to the iobuf and return true on write error
* This macro does only write the low order byte
*/
#define iobuf_put(a,c) iobuf_writebyte(a,c)
#define iobuf_where(a) "[don't know]"
#define iobuf_id(a) ((a)->no)
#define iobuf_get_temp_buffer(a) ( (a)->d.buf )
#define iobuf_get_temp_length(a) ( (a)->d.len )
#define iobuf_is_temp(a) ( (a)->use == 3 )
void iobuf_skip_rest (IOBUF a, unsigned long n, int partial);
#endif /*G10_IOBUF_H*/
diff --git a/util/iobuf.c b/util/iobuf.c
index a330460d6..539356e3c 100644
--- a/util/iobuf.c
+++ b/util/iobuf.c
@@ -1,2356 +1,2372 @@
/* iobuf.c - file handling
* Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2008,
* 2009 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 .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef HAVE_DOSISH_SYSTEM
#include
#endif
#ifdef __riscos__
#include
#include
#endif /* __riscos__ */
#include "memory.h"
#include "util.h"
#include "dynload.h"
#include "iobuf.h"
#ifdef __VMS
# include "vms.h"
# define open open_vms
#endif /* def __VMS */
/* The size of the internal buffers.
NOTE: If you change this value you MUST also adjust the regression
test "armored_key_8192" and "nopad_armored_msg" in armor.test! */
#define IOBUF_BUFFER_SIZE 8192
#undef FILE_FILTER_USES_STDIO
/* To avoid a potential DoS with compression packets we better limit
the number of filters in a chain. */
#define MAX_NESTING_FILTER 64
#ifdef HAVE_DOSISH_SYSTEM
#define USE_SETMODE 1
#endif
#ifdef FILE_FILTER_USES_STDIO
#define my_fileno(a) fileno ((a))
#define my_fopen_ro(a,b) fopen ((a),(b))
#define my_fopen(a,b) fopen ((a),(b))
typedef FILE *FILEP_OR_FD;
#define INVALID_FP NULL
#define FILEP_OR_FD_FOR_STDIN (stdin)
#define FILEP_OR_FD_FOR_STDOUT (stdout)
typedef struct {
FILE *fp; /* open file handle */
int keep_open;
int no_cache;
int print_only_name; /* flags indicating that fname is not a real file*/
char fname[1]; /* name of the file */
} file_filter_ctx_t ;
#else
#define my_fileno(a) (a)
#define my_fopen_ro(a,b) fd_cache_open ((a),(b))
#define my_fopen(a,b) direct_open ((a),(b))
#ifdef HAVE_DOSISH_SYSTEM
typedef HANDLE FILEP_OR_FD;
#define INVALID_FP ((HANDLE)-1)
#define FILEP_OR_FD_FOR_STDIN (GetStdHandle (STD_INPUT_HANDLE))
#define FILEP_OR_FD_FOR_STDOUT (GetStdHandle (STD_OUTPUT_HANDLE))
#undef USE_SETMODE
#else
typedef int FILEP_OR_FD;
#define INVALID_FP (-1)
#define FILEP_OR_FD_FOR_STDIN (0)
#define FILEP_OR_FD_FOR_STDOUT (1)
#endif
typedef struct {
FILEP_OR_FD fp; /* open file handle */
int keep_open;
int no_cache;
int eof_seen;
int print_only_name; /* flags indicating that fname is not a real file*/
char fname[1]; /* name of the file */
} file_filter_ctx_t ;
struct close_cache_s {
struct close_cache_s *next;
FILEP_OR_FD fp;
char fname[1];
};
typedef struct close_cache_s *CLOSE_CACHE;
static CLOSE_CACHE close_cache;
#endif
#ifdef _WIN32
typedef struct {
int sock;
int keep_open;
int no_cache;
int eof_seen;
int print_only_name; /* flags indicating that fname is not a real file*/
char fname[1]; /* name of the file */
} sock_filter_ctx_t ;
#endif /*_WIN32*/
/* The first partial length header block must be of size 512
* to make it easier (and efficienter) we use a min. block size of 512
* for all chunks (but the last one) */
#define OP_MIN_PARTIAL_CHUNK 512
#define OP_MIN_PARTIAL_CHUNK_2POW 9
typedef struct {
int use;
size_t size;
size_t count;
int partial; /* 1 = partial header, 2 in last partial packet */
char *buffer; /* used for partial header */
size_t buflen; /* used size of buffer */
int first_c; /* of partial header (which is > 0)*/
int eof;
} block_filter_ctx_t;
static int special_names_enabled;
static int underflow(IOBUF a);
static int translate_file_handle ( int fd, int for_write );
#ifndef FILE_FILTER_USES_STDIO
/* This is a replacement for strcmp. Under W32 it does not
distinguish between backslash and slash. */
static int
fd_cache_strcmp (const char *a, const char *b)
{
#ifdef HAVE_DOSISH_SYSTEM
for (; *a && *b; a++, b++)
{
if (*a != *b && !((*a == '/' && *b == '\\')
|| (*a == '\\' && *b == '/')) )
break;
}
return *(const unsigned char *)a - *(const unsigned char *)b;
#else
return strcmp (a, b);
#endif
}
/*
* Invalidate (i.e. close) a cached iobuf or all iobufs if NULL is
* used for FNAME.
*/
static int
fd_cache_invalidate (const char *fname)
{
CLOSE_CACHE cc;
int err=0;
if (!fname) {
if( DBG_IOBUF )
log_debug ("fd_cache_invalidate (all)\n");
for (cc=close_cache; cc; cc = cc->next ) {
if ( cc->fp != INVALID_FP ) {
#ifdef HAVE_DOSISH_SYSTEM
CloseHandle (cc->fp);
#else
close(cc->fp);
#endif
cc->fp = INVALID_FP;
}
}
return err;
}
if( DBG_IOBUF )
log_debug ("fd_cache_invalidate (%s)\n", fname);
for (cc=close_cache; cc; cc = cc->next ) {
if ( cc->fp != INVALID_FP && !fd_cache_strcmp (cc->fname, fname) ) {
if( DBG_IOBUF )
log_debug (" did (%s)\n", cc->fname);
#ifdef HAVE_DOSISH_SYSTEM
if(CloseHandle (cc->fp)==0)
err=-1;
#else
err=close(cc->fp);
#endif
cc->fp = INVALID_FP;
}
}
return err;
}
static int
fd_cache_synchronize(const char *fname)
{
int err=0;
#ifndef HAVE_DOSISH_SYSTEM
CLOSE_CACHE cc;
if( DBG_IOBUF )
log_debug ("fd_cache_synchronize (%s)\n", fname);
for (cc=close_cache; cc; cc = cc->next )
{
if ( cc->fp != INVALID_FP && !fd_cache_strcmp (cc->fname, fname) )
{
if( DBG_IOBUF )
log_debug (" did (%s)\n", cc->fname);
err=fsync(cc->fp);
}
}
#endif
return err;
}
static FILEP_OR_FD
direct_open (const char *fname, const char *mode)
{
#ifdef HAVE_DOSISH_SYSTEM
unsigned long da, cd, sm;
HANDLE hfile;
/* Note, that we do not handle all mode combinations */
/* According to the ReactOS source it seems that open() of the
* standard MSW32 crt does open the file in share mode which is
* something new for MS applications ;-)
*/
if ( strchr (mode, '+') ) {
fd_cache_invalidate (fname);
da = GENERIC_READ|GENERIC_WRITE;
cd = OPEN_EXISTING;
sm = FILE_SHARE_READ | FILE_SHARE_WRITE;
}
else if ( strchr (mode, 'w') ) {
fd_cache_invalidate (fname);
da = GENERIC_WRITE;
cd = CREATE_ALWAYS;
sm = FILE_SHARE_WRITE;
}
else {
da = GENERIC_READ;
cd = OPEN_EXISTING;
sm = FILE_SHARE_READ;
}
hfile = CreateFile (fname, da, sm, NULL, cd, FILE_ATTRIBUTE_NORMAL, NULL);
return hfile;
#else
int oflag;
int cflag = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
/* Note, that we do not handle all mode combinations */
if ( strchr (mode, '+') ) {
fd_cache_invalidate (fname);
oflag = O_RDWR;
}
else if ( strchr (mode, 'w') ) {
fd_cache_invalidate (fname);
oflag = O_WRONLY | O_CREAT | O_TRUNC;
}
else {
oflag = O_RDONLY;
}
#ifdef O_BINARY
if (strchr (mode, 'b'))
oflag |= O_BINARY;
#endif
#ifndef __riscos__
return open (fname, oflag, cflag );
#else
{
struct stat buf;
int rc = stat( fname, &buf );
/* Don't allow iobufs on directories */
if( !rc && S_ISDIR(buf.st_mode) && !S_ISREG(buf.st_mode) )
return __set_errno( EISDIR );
else
return open( fname, oflag, cflag );
}
#endif
#endif
}
/*
* Instead of closing an FD we keep it open and cache it for later reuse
* Note that this caching strategy only works if the process does not chdir.
*/
static void
fd_cache_close (const char *fname, FILEP_OR_FD fp)
{
CLOSE_CACHE cc;
assert (fp);
if ( !fname || !*fname ) {
#ifdef HAVE_DOSISH_SYSTEM
CloseHandle (fp);
#else
close(fp);
#endif
if( DBG_IOBUF )
log_debug ("fd_cache_close (%d) real\n", (int)fp);
return;
}
/* try to reuse a slot */
for (cc=close_cache; cc; cc = cc->next ) {
if ( cc->fp == INVALID_FP && !fd_cache_strcmp (cc->fname, fname) ) {
cc->fp = fp;
if( DBG_IOBUF )
log_debug ("fd_cache_close (%s) used existing slot\n", fname);
return;
}
}
/* add a new one */
if( DBG_IOBUF )
log_debug ("fd_cache_close (%s) new slot created\n", fname);
cc = xmalloc_clear (sizeof *cc + strlen (fname));
strcpy (cc->fname, fname);
cc->fp = fp;
cc->next = close_cache;
close_cache = cc;
}
/*
* Do an direct_open on FNAME but first try to reuse one from the fd_cache
*/
static FILEP_OR_FD
fd_cache_open (const char *fname, const char *mode)
{
CLOSE_CACHE cc;
assert (fname);
for (cc=close_cache; cc; cc = cc->next ) {
if ( cc->fp != INVALID_FP && !fd_cache_strcmp (cc->fname, fname) ) {
FILEP_OR_FD fp = cc->fp;
cc->fp = INVALID_FP;
if( DBG_IOBUF )
log_debug ("fd_cache_open (%s) using cached fp\n", fname);
#ifdef HAVE_DOSISH_SYSTEM
if (SetFilePointer (fp, 0, NULL, FILE_BEGIN) == 0xffffffff ) {
log_error ("rewind file failed on handle %p: %s\n",
fp, w32_strerror (errno));
fp = INVALID_FP;
}
#else
if ( lseek (fp, 0, SEEK_SET) == (off_t)-1 ) {
log_error("can't rewind fd %d: %s\n", fp, strerror(errno) );
fp = INVALID_FP;
}
#endif
return fp;
}
}
if( DBG_IOBUF )
log_debug ("fd_cache_open (%s) not cached\n", fname);
return direct_open (fname, mode);
}
#endif /*FILE_FILTER_USES_STDIO*/
/****************
* Read data from a file into buf which has an allocated length of *LEN.
* return the number of read bytes in *LEN. OPAQUE is the FILE * of
* the stream. A is not used.
* control may be:
* IOBUFCTRL_INIT: called just before the function is linked into the
* list of function. This can be used to prepare internal
* data structures of the function.
* IOBUFCTRL_FREE: called just before the function is removed from the
* list of functions and can be used to release internal
* data structures or close a file etc.
* IOBUFCTRL_UNDERFLOW: called by iobuf_underflow to fill the buffer
* with new stuff. *RET_LEN is the available size of the
* buffer, and should be set to the number of bytes
* which were put into the buffer. The function
* returns 0 to indicate success, -1 on EOF and
* G10ERR_xxxxx for other errors.
*
* IOBUFCTRL_FLUSH: called by iobuf_flush() to write out the collected stuff.
* *RET_LAN is the number of bytes in BUF.
*
* IOBUFCTRL_CANCEL: send to all filters on behalf of iobuf_cancel. The
* filter may take appropriate action on this message.
*/
static int
file_filter(void *opaque, int control, IOBUF chain, byte *buf, size_t *ret_len)
{
file_filter_ctx_t *a = opaque;
FILEP_OR_FD f = a->fp;
size_t size = *ret_len;
size_t nbytes = 0;
int rc = 0;
#ifdef FILE_FILTER_USES_STDIO
if( control == IOBUFCTRL_UNDERFLOW ) {
assert( size ); /* need a buffer */
if ( feof(f)) { /* On terminals you could easiely read as many EOFs as you call */
rc = -1; /* fread() or fgetc() repeatly. Every call will block until you press */
*ret_len = 0; /* CTRL-D. So we catch this case before we call fread() again. */
}
else {
clearerr( f );
nbytes = fread( buf, 1, size, f );
if( feof(f) && !nbytes ) {
rc = -1; /* okay: we can return EOF now. */
}
else if( ferror(f) && errno != EPIPE ) {
log_error("%s: read error: %s\n",
a->fname, strerror(errno));
rc = G10ERR_READ_FILE;
}
*ret_len = nbytes;
}
}
else if( control == IOBUFCTRL_FLUSH ) {
if( size ) {
clearerr( f );
nbytes = fwrite( buf, 1, size, f );
if( ferror(f) ) {
log_error("%s: write error: %s\n", a->fname, strerror(errno));
rc = G10ERR_WRITE_FILE;
}
}
*ret_len = nbytes;
}
else if( control == IOBUFCTRL_INIT ) {
a->keep_open = a->no_cache = 0;
}
else if( control == IOBUFCTRL_DESC ) {
- *(char**)buf = "file_filter";
+ mem2str (buf, "file_filter", *ret_len);
}
else if( control == IOBUFCTRL_FREE ) {
if( f != stdin && f != stdout ) {
if( DBG_IOBUF )
log_debug("%s: close fd %d\n", a->fname, fileno(f) );
if (!a->keep_open)
fclose(f);
}
f = NULL;
xfree(a); /* we can free our context now */
}
#else /* !stdio implementation */
if( control == IOBUFCTRL_UNDERFLOW ) {
assert( size ); /* need a buffer */
if ( a->eof_seen) {
rc = -1;
*ret_len = 0;
}
else {
#ifdef HAVE_DOSISH_SYSTEM
unsigned long nread;
nbytes = 0;
if ( !ReadFile ( f, buf, size, &nread, NULL ) ) {
if ((int)GetLastError () != ERROR_BROKEN_PIPE) {
log_error ("%s: read error: %s\n", a->fname,
w32_strerror (0));
rc = G10ERR_READ_FILE;
}
}
else if ( !nread ) {
a->eof_seen = 1;
rc = -1;
}
else {
nbytes = nread;
}
#else
int n;
nbytes = 0;
do {
n = read ( f, buf, size );
} while (n == -1 && errno == EINTR );
if ( n == -1 ) { /* error */
if (errno != EPIPE) {
log_error("%s: read error: %s\n",
a->fname, strerror(errno));
rc = G10ERR_READ_FILE;
}
}
else if ( !n ) { /* eof */
a->eof_seen = 1;
rc = -1;
}
else {
nbytes = n;
}
#endif
*ret_len = nbytes;
}
}
else if( control == IOBUFCTRL_FLUSH ) {
if( size ) {
#ifdef HAVE_DOSISH_SYSTEM
byte *p = buf;
unsigned long n;
nbytes = size;
do {
if (size && !WriteFile (f, p, nbytes, &n, NULL)) {
log_error ("%s: write error: %s\n", a->fname,
w32_strerror (0));
rc = G10ERR_WRITE_FILE;
break;
}
p += n;
nbytes -= n;
} while ( nbytes );
nbytes = p - buf;
#else
byte *p = buf;
int n;
nbytes = size;
do {
do {
n = write ( f, p, nbytes );
} while ( n == -1 && errno == EINTR );
if ( n > 0 ) {
p += n;
nbytes -= n;
}
} while ( n != -1 && nbytes );
if( n == -1 ) {
log_error("%s: write error: %s\n", a->fname, strerror(errno));
rc = G10ERR_WRITE_FILE;
}
nbytes = p - buf;
#endif
}
*ret_len = nbytes;
}
else if ( control == IOBUFCTRL_INIT ) {
a->eof_seen = 0;
a->keep_open = 0;
a->no_cache = 0;
}
else if ( control == IOBUFCTRL_DESC ) {
- *(char**)buf = "file_filter(fd)";
+ mem2str (buf, "file_filter(fd)", *ret_len);
}
else if ( control == IOBUFCTRL_FREE ) {
#ifdef HAVE_DOSISH_SYSTEM
if ( f != FILEP_OR_FD_FOR_STDIN && f != FILEP_OR_FD_FOR_STDOUT ) {
if( DBG_IOBUF )
log_debug("%s: close handle %p\n", a->fname, f );
if (!a->keep_open)
fd_cache_close (a->no_cache?NULL:a->fname, f);
}
#else
if ( (int)f != 0 && (int)f != 1 ) {
if( DBG_IOBUF )
log_debug("%s: close fd %d\n", a->fname, f );
if (!a->keep_open)
fd_cache_close (a->no_cache?NULL:a->fname, f);
}
f = INVALID_FP;
#endif
xfree (a); /* we can free our context now */
}
#endif /* !stdio implementation */
return rc;
}
#ifdef _WIN32
/* Becuase sockets are an special object under Lose32 we have to
* use a special filter */
static int
sock_filter (void *opaque, int control, IOBUF chain, byte *buf, size_t *ret_len)
{
sock_filter_ctx_t *a = opaque;
size_t size = *ret_len;
size_t nbytes = 0;
int rc = 0;
if( control == IOBUFCTRL_UNDERFLOW ) {
assert( size ); /* need a buffer */
if ( a->eof_seen) {
rc = -1;
*ret_len = 0;
}
else {
int nread;
nread = recv ( a->sock, buf, size, 0 );
if ( nread == SOCKET_ERROR ) {
int ec = (int)WSAGetLastError ();
log_error("socket read error: ec=%d\n", ec);
rc = G10ERR_READ_FILE;
}
else if ( !nread ) {
a->eof_seen = 1;
rc = -1;
}
else {
nbytes = nread;
}
*ret_len = nbytes;
}
}
else if( control == IOBUFCTRL_FLUSH ) {
if( size ) {
byte *p = buf;
int n;
nbytes = size;
do {
n = send (a->sock, p, nbytes, 0);
if ( n == SOCKET_ERROR ) {
int ec = (int)WSAGetLastError ();
log_error("socket write error: ec=%d\n", ec);
rc = G10ERR_WRITE_FILE;
break;
}
p += n;
nbytes -= n;
} while ( nbytes );
nbytes = p - buf;
}
*ret_len = nbytes;
}
else if ( control == IOBUFCTRL_INIT ) {
a->eof_seen = 0;
a->keep_open = 0;
a->no_cache = 0;
}
else if ( control == IOBUFCTRL_DESC ) {
- *(char**)buf = "sock_filter";
+ mem2str (buf, "sock_filter", *ret_len);
}
else if ( control == IOBUFCTRL_FREE ) {
if (!a->keep_open)
closesocket (a->sock);
xfree (a); /* we can free our context now */
}
return rc;
}
#endif /*_WIN32*/
/****************
* This is used to implement the block write mode.
* Block reading is done on a byte by byte basis in readbyte(),
* without a filter
*/
static int
block_filter(void *opaque, int control, IOBUF chain, byte *buf, size_t *ret_len)
{
block_filter_ctx_t *a = opaque;
size_t size = *ret_len;
int c, needed, rc = 0;
char *p;
if( control == IOBUFCTRL_UNDERFLOW ) {
size_t n=0;
p = buf;
assert( size ); /* need a buffer */
if( a->eof ) /* don't read any further */
rc = -1;
while( !rc && size ) {
if( !a->size ) { /* get the length bytes */
if( a->partial == 2 ) {
a->eof = 1;
if( !n )
rc = -1;
break;
}
else if( a->partial ) {
/* These OpenPGP introduced huffman like encoded length
* bytes are really a mess :-( */
if( a->first_c ) {
c = a->first_c;
a->first_c = 0;
}
else if( (c = iobuf_get(chain)) == -1 ) {
log_error("block_filter: 1st length byte missing\n");
rc = G10ERR_READ_FILE;
break;
}
if( c < 192 ) {
a->size = c;
a->partial = 2;
if( !a->size ) {
a->eof = 1;
if( !n )
rc = -1;
break;
}
}
else if( c < 224 ) {
a->size = (c - 192) * 256;
if( (c = iobuf_get(chain)) == -1 ) {
log_error("block_filter: 2nd length byte missing\n");
rc = G10ERR_READ_FILE;
break;
}
a->size += c + 192;
a->partial = 2;
if( !a->size ) {
a->eof = 1;
if( !n )
rc = -1;
break;
}
}
else if( c == 255 ) {
a->size = iobuf_get(chain) << 24;
a->size |= iobuf_get(chain) << 16;
a->size |= iobuf_get(chain) << 8;
if( (c = iobuf_get(chain)) == -1 ) {
log_error("block_filter: invalid 4 byte length\n");
rc = G10ERR_READ_FILE;
break;
}
a->size |= c;
a->partial = 2;
if( !a->size ) {
a->eof = 1;
if( !n )
rc = -1;
break;
}
}
else { /* next partial body length */
a->size = 1 << (c & 0x1f);
}
/* log_debug("partial: ctx=%p c=%02x size=%u\n", a, c, a->size);*/
}
else
BUG();
}
while( !rc && size && a->size ) {
needed = size < a->size ? size : a->size;
c = iobuf_read( chain, p, needed );
if( c < needed ) {
if( c == -1 ) c = 0;
log_error("block_filter %p: read error (size=%lu,a->size=%lu)\n",
a, (ulong)size+c, (ulong)a->size+c);
rc = G10ERR_READ_FILE;
}
else {
size -= c;
a->size -= c;
p += c;
n += c;
}
}
}
*ret_len = n;
}
else if( control == IOBUFCTRL_FLUSH ) {
if( a->partial ) { /* the complicated openpgp scheme */
size_t blen, n, nbytes = size + a->buflen;
assert( a->buflen <= OP_MIN_PARTIAL_CHUNK );
if( nbytes < OP_MIN_PARTIAL_CHUNK ) {
/* not enough to write a partial block out; so we store it*/
if( !a->buffer )
a->buffer = xmalloc( OP_MIN_PARTIAL_CHUNK );
memcpy( a->buffer + a->buflen, buf, size );
a->buflen += size;
}
else { /* okay, we can write out something */
/* do this in a loop to use the most efficient block lengths */
p = buf;
do {
/* find the best matching block length - this is limited
* by the size of the internal buffering */
for( blen=OP_MIN_PARTIAL_CHUNK*2,
c=OP_MIN_PARTIAL_CHUNK_2POW+1; blen <= nbytes;
blen *=2, c++ )
;
blen /= 2; c--;
/* write the partial length header */
assert( c <= 0x1f ); /*;-)*/
c |= 0xe0;
iobuf_put( chain, c );
if( (n=a->buflen) ) { /* write stuff from the buffer */
assert( n == OP_MIN_PARTIAL_CHUNK);
if( iobuf_write(chain, a->buffer, n ) )
rc = G10ERR_WRITE_FILE;
a->buflen = 0;
nbytes -= n;
}
if( (n = nbytes) > blen )
n = blen;
if( n && iobuf_write(chain, p, n ) )
rc = G10ERR_WRITE_FILE;
p += n;
nbytes -= n;
} while( !rc && nbytes >= OP_MIN_PARTIAL_CHUNK );
/* store the rest in the buffer */
if( !rc && nbytes ) {
assert( !a->buflen );
assert( nbytes < OP_MIN_PARTIAL_CHUNK );
if( !a->buffer )
a->buffer = xmalloc( OP_MIN_PARTIAL_CHUNK );
memcpy( a->buffer, p, nbytes );
a->buflen = nbytes;
}
}
}
else
BUG();
}
else if( control == IOBUFCTRL_INIT ) {
if( DBG_IOBUF )
log_debug("init block_filter %p\n", a );
if( a->partial )
a->count = 0;
else if( a->use == 1 )
a->count = a->size = 0;
else
a->count = a->size; /* force first length bytes */
a->eof = 0;
a->buffer = NULL;
a->buflen = 0;
}
else if( control == IOBUFCTRL_DESC ) {
- *(char**)buf = "block_filter";
+ mem2str (buf, "block_filter", *ret_len);
}
else if( control == IOBUFCTRL_FREE ) {
if( a->use == 2 ) { /* write the end markers */
if( a->partial ) {
u32 len;
/* write out the remaining bytes without a partial header
* the length of this header may be 0 - but if it is
* the first block we are not allowed to use a partial header
* and frankly we can't do so, because this length must be
* a power of 2. This is _really_ complicated because we
* have to check the possible length of a packet prior
* to it's creation: a chain of filters becomes complicated
* and we need a lot of code to handle compressed packets etc.
* :-(((((((
*/
/* construct header */
len = a->buflen;
/*log_debug("partial: remaining length=%u\n", len );*/
if( len < 192 )
rc = iobuf_put(chain, len );
else if( len < 8384 ) {
if( !(rc=iobuf_put( chain, ((len-192) / 256) + 192)) )
rc = iobuf_put( chain, ((len-192) % 256));
}
else { /* use a 4 byte header */
if( !(rc=iobuf_put( chain, 0xff )) )
if( !(rc=iobuf_put( chain, (len >> 24)&0xff )) )
if( !(rc=iobuf_put( chain, (len >> 16)&0xff )) )
if( !(rc=iobuf_put( chain, (len >> 8)&0xff )))
rc=iobuf_put( chain, len & 0xff );
}
if( !rc && len )
rc = iobuf_write(chain, a->buffer, len );
if( rc ) {
log_error("block_filter: write error: %s\n",strerror(errno));
rc = G10ERR_WRITE_FILE;
}
xfree( a->buffer ); a->buffer = NULL; a->buflen = 0;
}
else
BUG();
}
else if( a->size ) {
log_error("block_filter: pending bytes!\n");
}
if( DBG_IOBUF )
log_debug("free block_filter %p\n", a );
xfree(a); /* we can free our context now */
}
return rc;
}
+#define MAX_IOBUF_DESC 32
+/*
+ * Fill the buffer by the description of iobuf A.
+ * The buffer size should be MAX_IOBUF_DESC (or larger).
+ * Returns BUF as (const char *).
+ */
+static const char *
+iobuf_desc (iobuf_t a, byte *buf)
+{
+ size_t len = MAX_IOBUF_DESC;
+
+ if (! a || ! a->filter)
+ memcpy (buf, "?", 2);
+ else
+ a->filter (a->filter_ov, IOBUFCTRL_DESC, NULL, buf, &len);
+
+ return buf;
+}
static void
print_chain( IOBUF a )
{
if( !DBG_IOBUF )
return;
for(; a; a = a->chain ) {
- size_t dummy_len = 0;
- const char *desc = "[none]";
-
- if( a->filter )
- a->filter( a->filter_ov, IOBUFCTRL_DESC, NULL,
- (byte*)&desc, &dummy_len );
+ byte desc[MAX_IOBUF_DESC];
log_debug("iobuf chain: %d.%d `%s' filter_eof=%d start=%d len=%d\n",
- a->no, a->subno, desc?desc:"?", a->filter_eof,
- (int)a->d.start, (int)a->d.len );
+ a->no, a->subno, iobuf_desc (a, desc), a->filter_eof,
+ (int)a->d.start, (int)a->d.len );
}
}
int
iobuf_print_chain( IOBUF a )
{
print_chain(a);
return 0;
}
/****************
* Allocate a new io buffer, with no function assigned.
* Use is the desired usage: 1 for input, 2 for output, 3 for temp buffer
* BUFSIZE is a suggested buffer size.
*/
IOBUF
iobuf_alloc(int use, size_t bufsize)
{
IOBUF a;
static int number=0;
a = xmalloc_clear(sizeof *a);
a->use = use;
a->d.buf = xmalloc( bufsize );
a->d.size = bufsize;
a->no = ++number;
a->subno = 0;
a->opaque = NULL;
a->real_fname = NULL;
return a;
}
int
iobuf_close ( IOBUF a )
{
IOBUF a2;
size_t dummy_len=0;
int rc=0;
if( a && a->directfp ) {
fclose( a->directfp );
xfree( a->real_fname );
if( DBG_IOBUF )
log_debug("iobuf_close -> %p\n", a->directfp );
return 0;
}
for( ; a && !rc ; a = a2 ) {
+ byte desc[MAX_IOBUF_DESC];
a2 = a->chain;
if( a->use == 2 && (rc=iobuf_flush(a)) )
log_error("iobuf_flush failed on close: %s\n", g10_errstr(rc));
if( DBG_IOBUF )
log_debug("iobuf-%d.%d: close `%s'\n", a->no, a->subno,
- a->desc?a->desc:"?");
+ iobuf_desc (a, desc));
if( a->filter && (rc = a->filter(a->filter_ov, IOBUFCTRL_FREE,
a->chain, NULL, &dummy_len)) )
log_error("IOBUFCTRL_FREE failed on close: %s\n", g10_errstr(rc) );
xfree(a->real_fname);
if (a->d.buf) {
memset (a->d.buf, 0, a->d.size); /* erase the buffer */
xfree(a->d.buf);
}
xfree(a);
}
return rc;
}
int
iobuf_cancel( IOBUF a )
{
const char *s;
IOBUF a2;
int rc;
#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
char *remove_name = NULL;
#endif
if( a && a->use == 2 ) {
s = iobuf_get_real_fname(a);
if( s && *s ) {
#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
remove_name = xstrdup ( s );
#else
remove(s);
#endif
}
}
/* send a cancel message to all filters */
for( a2 = a; a2 ; a2 = a2->chain ) {
size_t dummy;
if( a2->filter )
a2->filter( a2->filter_ov, IOBUFCTRL_CANCEL, a2->chain,
NULL, &dummy );
}
rc = iobuf_close(a);
#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
if ( remove_name ) {
/* Argg, MSDOS does not allow to remove open files. So
* we have to do it here */
remove ( remove_name );
xfree ( remove_name );
}
#endif
return rc;
}
/****************
* create a temporary iobuf, which can be used to collect stuff
* in an iobuf and later be written by iobuf_write_temp() to another
* iobuf.
*/
IOBUF
iobuf_temp()
{
IOBUF a;
a = iobuf_alloc(3, IOBUF_BUFFER_SIZE );
return a;
}
IOBUF
iobuf_temp_with_content( const char *buffer, size_t length )
{
IOBUF a;
a = iobuf_alloc(3, length );
memcpy( a->d.buf, buffer, length );
a->d.len = length;
return a;
}
void
iobuf_enable_special_filenames ( int yes )
{
special_names_enabled = yes;
}
/*
* see whether the filename has the for "-&nnnn", where n is a
* non-zero number.
* Returns this number or -1 if it is not the case.
*/
static int
check_special_filename ( const char *fname )
{
if ( special_names_enabled
&& fname && *fname == '-' && fname[1] == '&' ) {
int i;
fname += 2;
for (i=0; digitp (fname+i); i++ )
;
if ( !fname[i] )
return atoi (fname);
}
return -1;
}
/* This fucntion returns true if FNAME indicates a PIPE (stdout or
stderr) or a special file name if those are enabled. */
int
iobuf_is_pipe_filename (const char *fname)
{
if (!fname || (*fname=='-' && !fname[1]) )
return 1;
return check_special_filename (fname) != -1;
}
/****************
* Create a head iobuf for reading from a file
* returns: NULL if an error occures and sets errno
*/
IOBUF
iobuf_open( const char *fname )
{
IOBUF a;
FILEP_OR_FD fp;
file_filter_ctx_t *fcx;
size_t len = 0;
int print_only = 0;
int fd;
if( !fname || (*fname=='-' && !fname[1]) ) {
fp = FILEP_OR_FD_FOR_STDIN;
#ifdef USE_SETMODE
setmode ( my_fileno(fp) , O_BINARY );
#endif
fname = "[stdin]";
print_only = 1;
}
else if ( (fd = check_special_filename ( fname )) != -1 )
return iobuf_fdopen ( translate_file_handle (fd,0), "rb" );
else if( (fp = my_fopen_ro(fname, "rb")) == INVALID_FP )
return NULL;
a = iobuf_alloc(1, IOBUF_BUFFER_SIZE );
fcx = xmalloc( sizeof *fcx + strlen(fname) );
fcx->fp = fp;
fcx->print_only_name = print_only;
strcpy(fcx->fname, fname );
if( !print_only )
a->real_fname = xstrdup( fname );
a->filter = file_filter;
a->filter_ov = fcx;
- file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len );
file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len );
if( DBG_IOBUF )
log_debug("iobuf-%d.%d: open `%s' fd=%d\n",
a->no, a->subno, fname, (int)my_fileno(fcx->fp) );
return a;
}
/****************
* Create a head iobuf for reading from a file
* returns: NULL if an error occures and sets errno
*/
IOBUF
iobuf_fdopen( int fd, const char *mode )
{
IOBUF a;
FILEP_OR_FD fp;
file_filter_ctx_t *fcx;
size_t len;
#ifdef FILE_FILTER_USES_STDIO
if( !(fp = fdopen(fd, mode)) )
return NULL;
#else
fp = (FILEP_OR_FD)fd;
#endif
a = iobuf_alloc( strchr( mode, 'w')? 2:1, IOBUF_BUFFER_SIZE );
fcx = xmalloc( sizeof *fcx + 20 );
fcx->fp = fp;
fcx->print_only_name = 1;
sprintf(fcx->fname, "[fd %d]", fd );
a->filter = file_filter;
a->filter_ov = fcx;
- file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len );
file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len );
if( DBG_IOBUF )
log_debug("iobuf-%d.%d: fdopen `%s'\n", a->no, a->subno, fcx->fname );
iobuf_ioctl (a,3,1,NULL); /* disable fd caching */
return a;
}
IOBUF
iobuf_sockopen ( int fd, const char *mode )
{
IOBUF a;
#ifdef _WIN32
sock_filter_ctx_t *scx;
size_t len;
a = iobuf_alloc( strchr( mode, 'w')? 2:1, IOBUF_BUFFER_SIZE );
scx = xmalloc( sizeof *scx + 25 );
scx->sock = fd;
scx->print_only_name = 1;
sprintf(scx->fname, "[sock %d]", fd );
a->filter = sock_filter;
a->filter_ov = scx;
- sock_filter( scx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len );
sock_filter( scx, IOBUFCTRL_INIT, NULL, NULL, &len );
if( DBG_IOBUF )
log_debug("iobuf-%d.%d: sockopen `%s'\n", a->no, a->subno, scx->fname);
iobuf_ioctl (a,3,1,NULL); /* disable fd caching */
#else
a = iobuf_fdopen (fd, mode);
#endif
return a;
}
/****************
* create an iobuf for writing to a file; the file will be created.
*/
IOBUF
iobuf_create( const char *fname )
{
IOBUF a;
FILEP_OR_FD fp;
file_filter_ctx_t *fcx;
size_t len;
int print_only = 0;
int fd;
+ byte desc[MAX_IOBUF_DESC];
if( !fname || (*fname=='-' && !fname[1]) ) {
fp = FILEP_OR_FD_FOR_STDOUT;
#ifdef USE_SETMODE
setmode ( my_fileno(fp) , O_BINARY );
#endif
fname = "[stdout]";
print_only = 1;
}
else if ( (fd = check_special_filename ( fname )) != -1 )
return iobuf_fdopen ( translate_file_handle (fd, 1), "wb" );
else if( (fp = my_fopen(fname, "wb")) == INVALID_FP )
return NULL;
a = iobuf_alloc(2, IOBUF_BUFFER_SIZE );
fcx = xmalloc( sizeof *fcx + strlen(fname) );
fcx->fp = fp;
fcx->print_only_name = print_only;
strcpy(fcx->fname, fname );
if( !print_only )
a->real_fname = xstrdup( fname );
a->filter = file_filter;
a->filter_ov = fcx;
- file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len );
file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len );
if( DBG_IOBUF )
log_debug("iobuf-%d.%d: create `%s'\n", a->no, a->subno,
- a->desc?a->desc:"?" );
+ iobuf_desc (a, desc));
return a;
}
/****************
* append to an iobuf; if the file does not exist, create it.
* cannot be used for stdout.
* Note: This is not used.
*/
#if 0 /* not used */
IOBUF
iobuf_append( const char *fname )
{
IOBUF a;
FILE *fp;
file_filter_ctx_t *fcx;
size_t len;
+ byte desc[MAX_IOBUF_DESC];
if( !fname )
return NULL;
else if( !(fp = my_fopen(fname, "ab")) )
return NULL;
a = iobuf_alloc(2, IOBUF_BUFFER_SIZE );
fcx = xmalloc( sizeof *fcx + strlen(fname) );
fcx->fp = fp;
strcpy(fcx->fname, fname );
a->real_fname = xstrdup( fname );
a->filter = file_filter;
a->filter_ov = fcx;
- file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len );
file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len );
if( DBG_IOBUF )
log_debug("iobuf-%d.%d: append `%s'\n", a->no, a->subno,
- a->desc?a->desc:"?" );
+ iobuf_desc (a, desc));
return a;
}
#endif
IOBUF
iobuf_openrw( const char *fname )
{
IOBUF a;
FILEP_OR_FD fp;
file_filter_ctx_t *fcx;
size_t len;
+ byte desc[MAX_IOBUF_DESC];
if( !fname )
return NULL;
else if( (fp = my_fopen(fname, "r+b")) == INVALID_FP )
return NULL;
a = iobuf_alloc(2, IOBUF_BUFFER_SIZE );
fcx = xmalloc( sizeof *fcx + strlen(fname) );
fcx->fp = fp;
strcpy(fcx->fname, fname );
a->real_fname = xstrdup( fname );
a->filter = file_filter;
a->filter_ov = fcx;
- file_filter( fcx, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &len );
file_filter( fcx, IOBUFCTRL_INIT, NULL, NULL, &len );
if( DBG_IOBUF )
log_debug("iobuf-%d.%d: openrw `%s'\n", a->no, a->subno,
- a->desc?a->desc:"?");
+ iobuf_desc (a, desc));
return a;
}
int
iobuf_ioctl ( IOBUF a, int cmd, int intval, void *ptrval )
{
+ byte desc[MAX_IOBUF_DESC];
+
if ( cmd == 1 ) { /* keep system filepointer/descriptor open */
if( DBG_IOBUF )
log_debug("iobuf-%d.%d: ioctl `%s' keep=%d\n",
a? a->no:-1, a?a->subno:-1,
- a&&a->desc?a->desc:"?", intval );
+ iobuf_desc (a, desc), intval );
for( ; a; a = a->chain )
if( !a->chain && a->filter == file_filter ) {
file_filter_ctx_t *b = a->filter_ov;
b->keep_open = intval;
return 0;
}
#ifdef _WIN32
else if( !a->chain && a->filter == sock_filter ) {
sock_filter_ctx_t *b = a->filter_ov;
b->keep_open = intval;
return 0;
}
#endif
}
else if ( cmd == 2 ) { /* invalidate cache */
if( DBG_IOBUF )
log_debug("iobuf-*.*: ioctl `%s' invalidate\n",
ptrval? (char*)ptrval:"[all]");
if ( !a && !intval ) {
#ifndef FILE_FILTER_USES_STDIO
return fd_cache_invalidate (ptrval);
#endif
return 0;
}
}
else if ( cmd == 3 ) { /* disallow/allow caching */
if( DBG_IOBUF )
log_debug("iobuf-%d.%d: ioctl `%s' no_cache=%d\n",
a? a->no:-1, a?a->subno:-1,
- a&&a->desc?a->desc:"?", intval );
+ iobuf_desc (a, desc), intval );
for( ; a; a = a->chain )
if( !a->chain && a->filter == file_filter ) {
file_filter_ctx_t *b = a->filter_ov;
b->no_cache = intval;
return 0;
}
#ifdef _WIN32
else if( !a->chain && a->filter == sock_filter ) {
sock_filter_ctx_t *b = a->filter_ov;
b->no_cache = intval;
return 0;
}
#endif
}
else if(cmd==4)
{
/* Do a fsync on the open fd and return any errors to the
caller of iobuf_ioctl */
if( DBG_IOBUF )
log_debug("iobuf-*.*: ioctl `%s' fsync\n",
ptrval? (char*)ptrval:"");
if(!a && !intval && ptrval)
{
#ifndef FILE_FILTER_USES_STDIO
return fd_cache_synchronize (ptrval);
#else
return 0;
#endif
}
}
return -1;
}
/****************
* Register an i/o filter.
*/
int
iobuf_push_filter( IOBUF a,
int (*f)(void *opaque, int control,
IOBUF chain, byte *buf, size_t *len), void *ov )
{
return iobuf_push_filter2( a, f, ov, 0 );
}
int
iobuf_push_filter2( IOBUF a,
int (*f)(void *opaque, int control,
IOBUF chain, byte *buf, size_t *len),
void *ov, int rel_ov )
{
IOBUF b;
size_t dummy_len=0;
int rc=0;
if( a->directfp )
BUG();
if( a->use == 2 && (rc=iobuf_flush(a)) )
return rc;
if (a->subno >= MAX_NESTING_FILTER) {
log_error ("i/o filter too deeply nested - corrupted data?\n");
return G10ERR_UNEXPECTED;
}
/* make a copy of the current stream, so that
* A is the new stream and B the original one.
* The contents of the buffers are transferred to the
* new stream.
*/
b = xmalloc(sizeof *b);
memcpy(b, a, sizeof *b );
/* fixme: it is stupid to keep a copy of the name at every level
* but we need the name somewhere because the name known by file_filter
* may have been released when we need the name of the file */
b->real_fname = a->real_fname? xstrdup(a->real_fname):NULL;
/* remove the filter stuff from the new stream */
a->filter = NULL;
a->filter_ov = NULL;
a->filter_ov_owner = 0;
a->filter_eof = 0;
if( a->use == 3 )
a->use = 2; /* make a write stream from a temp stream */
if( a->use == 2 ) { /* allocate a fresh buffer for the original stream */
b->d.buf = xmalloc( a->d.size );
b->d.len = 0;
b->d.start = 0;
}
else { /* allocate a fresh buffer for the new stream */
a->d.buf = xmalloc( a->d.size );
a->d.len = 0;
a->d.start = 0;
}
/* disable nlimit for the new stream */
a->ntotal = b->ntotal + b->nbytes;
a->nlimit = a->nbytes = 0;
a->nofast &= ~1;
/* make a link from the new stream to the original stream */
a->chain = b;
a->opaque = b->opaque;
/* setup the function on the new stream */
a->filter = f;
a->filter_ov = ov;
a->filter_ov_owner = rel_ov;
a->subno = b->subno + 1;
- f( ov, IOBUFCTRL_DESC, NULL, (byte*)&a->desc, &dummy_len );
if( DBG_IOBUF ) {
+ byte desc[MAX_IOBUF_DESC];
+
log_debug("iobuf-%d.%d: push `%s'\n", a->no, a->subno,
- a->desc?a->desc:"?" );
+ iobuf_desc (a, desc));
print_chain( a );
}
/* now we can initialize the new function if we have one */
if( a->filter && (rc = a->filter(a->filter_ov, IOBUFCTRL_INIT, a->chain,
NULL, &dummy_len)) )
log_error("IOBUFCTRL_INIT failed: %s\n", g10_errstr(rc) );
return rc;
}
/****************
* Remove an i/o filter.
*/
static int
pop_filter( IOBUF a, int (*f)(void *opaque, int control,
IOBUF chain, byte *buf, size_t *len), void *ov )
{
IOBUF b;
size_t dummy_len=0;
int rc=0;
+ byte desc[MAX_IOBUF_DESC];
if( a->directfp )
BUG();
if( DBG_IOBUF )
log_debug("iobuf-%d.%d: pop `%s'\n", a->no, a->subno,
- a->desc?a->desc:"?" );
+ iobuf_desc (a, desc));
if( !a->filter ) { /* this is simple */
b = a->chain;
assert(b);
xfree(a->d.buf);
xfree(a->real_fname);
memcpy(a,b, sizeof *a);
xfree(b);
return 0;
}
for(b=a ; b; b = b->chain )
if( b->filter == f && (!ov || b->filter_ov == ov) )
break;
if( !b )
log_bug("pop_filter(): filter function not found\n");
/* flush this stream if it is an output stream */
if( a->use == 2 && (rc=iobuf_flush(b)) ) {
log_error("iobuf_flush failed in pop_filter: %s\n", g10_errstr(rc));
return rc;
}
/* and tell the filter to free it self */
if( b->filter && (rc = b->filter(b->filter_ov, IOBUFCTRL_FREE, b->chain,
NULL, &dummy_len)) ) {
log_error("IOBUFCTRL_FREE failed: %s\n", g10_errstr(rc) );
return rc;
}
if( b->filter_ov && b->filter_ov_owner ) {
xfree( b->filter_ov );
b->filter_ov = NULL;
}
/* and see how to remove it */
if( a == b && !b->chain )
log_bug("can't remove the last filter from the chain\n");
else if( a == b ) { /* remove the first iobuf from the chain */
/* everything from b is copied to a. This is save because
* a flush has been done on the to be removed entry
*/
b = a->chain;
xfree(a->d.buf);
xfree(a->real_fname);
memcpy(a,b, sizeof *a);
xfree(b);
if( DBG_IOBUF )
log_debug("iobuf-%d.%d: popped filter\n", a->no, a->subno );
}
else if( !b->chain ) { /* remove the last iobuf from the chain */
log_bug("Ohh jeee, trying to remove a head filter\n");
}
else { /* remove an intermediate iobuf from the chain */
log_bug("Ohh jeee, trying to remove an intermediate filter\n");
}
return rc;
}
/****************
* read underflow: read more bytes into the buffer and return
* the first byte or -1 on EOF.
*/
static int
underflow(IOBUF a)
{
size_t len;
int rc;
assert( a->d.start == a->d.len );
if( a->use == 3 )
return -1; /* EOF because a temp buffer can't do an underflow */
if( a->filter_eof ) {
if( a->chain ) {
+ byte desc[MAX_IOBUF_DESC];
+
IOBUF b = a->chain;
if( DBG_IOBUF )
log_debug("iobuf-%d.%d: pop `%s' in underflow\n",
- a->no, a->subno, a->desc?a->desc:"?" );
+ a->no, a->subno, iobuf_desc (a, desc) );
xfree(a->d.buf);
xfree(a->real_fname);
memcpy(a, b, sizeof *a);
xfree(b);
print_chain(a);
}
else
a->filter_eof = 0; /* for the top level filter */
if( DBG_IOBUF )
log_debug("iobuf-%d.%d: underflow: eof (due to filter eof)\n",
a->no, a->subno );
return -1; /* return one(!) EOF */
}
if( a->error ) {
if( DBG_IOBUF )
log_debug("iobuf-%d.%d: error\n", a->no, a->subno );
return -1;
}
if( a->directfp ) {
FILE *fp = a->directfp;
len = fread( a->d.buf, 1, a->d.size, fp);
if( len < a->d.size ) {
if( ferror(fp) )
a->error = 1;
}
a->d.len = len;
a->d.start = 0;
return len? a->d.buf[a->d.start++] : -1;
}
if( a->filter ) {
len = a->d.size;
if( DBG_IOBUF )
log_debug("iobuf-%d.%d: underflow: req=%lu\n",
a->no, a->subno, (ulong)len );
rc = a->filter( a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain,
a->d.buf, &len );
if( DBG_IOBUF ) {
log_debug("iobuf-%d.%d: underflow: got=%lu rc=%d\n",
a->no, a->subno, (ulong)len, rc );
/* if( a->no == 1 ) */
/* log_hexdump (" data:", a->d.buf, len); */
}
if( a->use == 1 && rc == -1 ) { /* EOF: we can remove the filter */
size_t dummy_len=0;
/* and tell the filter to free itself */
if( (rc = a->filter(a->filter_ov, IOBUFCTRL_FREE, a->chain,
NULL, &dummy_len)) )
log_error("IOBUFCTRL_FREE failed: %s\n", g10_errstr(rc) );
if( a->filter_ov && a->filter_ov_owner ) {
xfree( a->filter_ov );
a->filter_ov = NULL;
}
a->filter = NULL;
- a->desc = NULL;
a->filter_ov = NULL;
a->filter_eof = 1;
if( !len && a->chain ) {
IOBUF b = a->chain;
if( DBG_IOBUF )
log_debug("iobuf-%d.%d: pop in underflow (!len)\n",
a->no, a->subno);
xfree(a->d.buf);
xfree(a->real_fname);
memcpy(a,b, sizeof *a);
xfree(b);
print_chain(a);
}
}
else if( rc )
a->error = 1;
if( !len ) {
if( DBG_IOBUF )
log_debug("iobuf-%d.%d: underflow: eof\n", a->no, a->subno );
return -1;
}
a->d.len = len;
a->d.start = 0;
return a->d.buf[a->d.start++];
}
else {
if( DBG_IOBUF )
log_debug("iobuf-%d.%d: underflow: eof (no filter)\n",
a->no, a->subno );
return -1; /* no filter; return EOF */
}
}
int
iobuf_flush(IOBUF a)
{
size_t len;
int rc;
if( a->directfp )
return 0;
if( a->use == 3 ) { /* increase the temp buffer */
char *newbuf;
size_t newsize = a->d.size + IOBUF_BUFFER_SIZE;
if( DBG_IOBUF )
log_debug("increasing temp iobuf from %lu to %lu\n",
(ulong)a->d.size, (ulong)newsize );
newbuf = xmalloc( newsize );
memcpy( newbuf, a->d.buf, a->d.len );
xfree(a->d.buf);
a->d.buf = newbuf;
a->d.size = newsize;
return 0;
}
else if( a->use != 2 )
log_bug("flush on non-output iobuf\n");
else if( !a->filter )
log_bug("iobuf_flush: no filter\n");
len = a->d.len;
rc = a->filter( a->filter_ov, IOBUFCTRL_FLUSH, a->chain, a->d.buf, &len );
if( !rc && len != a->d.len ) {
log_info("iobuf_flush did not write all!\n");
rc = G10ERR_WRITE_FILE;
}
else if( rc )
a->error = 1;
a->d.len = 0;
return rc;
}
/****************
* Read a byte from the iobuf; returns -1 on EOF
*/
int
iobuf_readbyte(IOBUF a)
{
int c;
/* nlimit does not work together with unget */
/* nbytes is also not valid! */
if( a->unget.buf ) {
if( a->unget.start < a->unget.len )
return a->unget.buf[a->unget.start++];
xfree(a->unget.buf);
a->unget.buf = NULL;
a->nofast &= ~2;
}
if( a->nlimit && a->nbytes >= a->nlimit )
return -1; /* forced EOF */
if( a->d.start < a->d.len ) {
c = a->d.buf[a->d.start++];
}
else if( (c=underflow(a)) == -1 )
return -1; /* EOF */
a->nbytes++;
return c;
}
int
iobuf_read(IOBUF a, byte *buf, unsigned buflen )
{
int c, n;
if( a->unget.buf || a->nlimit ) {
/* handle special cases */
for(n=0 ; n < buflen; n++ ) {
if( (c = iobuf_readbyte(a)) == -1 ) {
if( !n )
return -1; /* eof */
break;
}
else
if( buf ) *buf = c;
if( buf ) buf++;
}
return n;
}
n = 0;
do {
if( n < buflen && a->d.start < a->d.len ) {
unsigned size = a->d.len - a->d.start;
if( size > buflen - n )
size = buflen - n;
if( buf )
memcpy( buf, a->d.buf + a->d.start, size );
n += size;
a->d.start += size;
if( buf )
buf += size;
}
if( n < buflen ) {
if( (c=underflow(a)) == -1 ) {
a->nbytes += n;
return n? n : -1/*EOF*/;
}
if( buf )
*buf++ = c;
n++;
}
} while( n < buflen );
a->nbytes += n;
return n;
}
/****************
* Have a look at the iobuf.
* NOTE: This only works in special cases.
*/
int
iobuf_peek(IOBUF a, byte *buf, unsigned buflen )
{
int n=0;
if( a->filter_eof )
return -1;
if( !(a->d.start < a->d.len) ) {
if( underflow(a) == -1 )
return -1;
/* and unget this character */
assert(a->d.start == 1);
a->d.start = 0;
}
for(n=0 ; n < buflen && (a->d.start+n) < a->d.len ; n++, buf++ )
*buf = a->d.buf[n];
return n;
}
int
iobuf_writebyte(IOBUF a, unsigned c)
{
if( a->directfp )
BUG();
if( a->d.len == a->d.size )
if( iobuf_flush(a) )
return -1;
assert( a->d.len < a->d.size );
a->d.buf[a->d.len++] = c;
return 0;
}
int
iobuf_write(IOBUF a, byte *buf, unsigned buflen )
{
if( a->directfp )
BUG();
do {
if( buflen && a->d.len < a->d.size ) {
unsigned size = a->d.size - a->d.len;
if( size > buflen ) size = buflen;
memcpy( a->d.buf + a->d.len, buf, size );
buflen -= size;
buf += size;
a->d.len += size;
}
if( buflen ) {
if( iobuf_flush(a) )
return -1;
}
} while( buflen );
return 0;
}
int
iobuf_writestr(IOBUF a, const char *buf )
{
for( ; *buf; buf++ )
if( iobuf_writebyte(a, *buf) )
return -1;
return 0;
}
/****************
* copy the contents of TEMP to A.
*/
int
iobuf_write_temp( IOBUF a, IOBUF temp )
{
while( temp->chain )
pop_filter( temp, temp->filter, NULL );
return iobuf_write(a, temp->d.buf, temp->d.len );
}
/****************
* copy the contents of the temp io stream to BUFFER.
*/
size_t
iobuf_temp_to_buffer( IOBUF a, byte *buffer, size_t buflen )
{
size_t n = a->d.len;
if( n > buflen )
n = buflen;
memcpy( buffer, a->d.buf, n );
return n;
}
/****************
* Call this function to terminate processing of the temp stream
* without closing it. This removes all filters from the stream
* makes sure that iobuf_get_temp_{buffer,length}() returns correct
* values.
*/
void
iobuf_flush_temp( IOBUF temp )
{
while( temp->chain )
pop_filter( temp, temp->filter, NULL );
}
/****************
* Set a limit on how many bytes may be read from the input stream A.
* Setting the limit to 0 disables this feature.
*/
void
iobuf_set_limit( IOBUF a, off_t nlimit )
{
if( nlimit )
a->nofast |= 1;
else
a->nofast &= ~1;
a->nlimit = nlimit;
a->ntotal += a->nbytes;
a->nbytes = 0;
}
/* Return the length of an open file A. IF OVERFLOW is not NULL it
will be set to true if the file is larger than what off_t can cope
with. The function return 0 on error or on overflow condition. */
off_t
iobuf_get_filelength (IOBUF a, int *overflow )
{
struct stat st;
if (overflow)
*overflow = 0;
if (a->directfp)
{
FILE *fp = a->directfp;
#ifdef __VMS
/* 2009-02-19 SMS.
* On VMS, use a VMS-specific method to determine file size.
* For some non-UNIX-like file formats, the fstat() result
* will not agree with the C Standard I/O functions such as
* getc() and fread(), so these must be detected and handled
* specially. */
return vms_file_size (fileno( fp));
#else /*!__VMS */
if( !fstat(fileno(fp), &st) )
return st.st_size;
log_error("fstat() failed: %s\n", strerror(errno) );
return 0;
#endif /*!__VMS */
}
/* Hmmm: file_filter may have already been removed */
for( ; a; a = a->chain )
if( !a->chain && a->filter == file_filter ) {
file_filter_ctx_t *b = a->filter_ov;
FILEP_OR_FD fp = b->fp;
#if defined(HAVE_DOSISH_SYSTEM) && !defined(FILE_FILTER_USES_STDIO)
ulong size;
static int (* __stdcall get_file_size_ex)
(void *handle, LARGE_INTEGER *size);
static int get_file_size_ex_initialized;
if (!get_file_size_ex_initialized)
{
void *handle;
handle = dlopen ("kernel32.dll", RTLD_LAZY);
if (handle)
{
get_file_size_ex = dlsym (handle, "GetFileSizeEx");
if (!get_file_size_ex)
dlclose (handle);
}
get_file_size_ex_initialized = 1;
}
if (get_file_size_ex)
{
/* This is a newer system with GetFileSizeEx; we use
this then becuase it seem that GetFileSize won't
return a proper error in case a file is larger than
4GB. */
LARGE_INTEGER size;
if (get_file_size_ex (fp, &size))
{
if (!size.u.HighPart)
return size.u.LowPart;
if (overflow)
*overflow = 1;
return 0;
}
}
else
{
if ((size=GetFileSize (fp, NULL)) != 0xffffffff)
return size;
}
log_error ("GetFileSize for handle %p failed: %s\n",
fp, w32_strerror (0));
#elif defined(__VMS)
return vms_file_size (my_fileno (fp));
#else
if( !fstat(my_fileno(fp), &st) )
return st.st_size;
log_error("fstat() failed: %s\n", strerror(errno) );
#endif
break;
}
return 0;
}
/* Return the file descriptor of the underlying file or -1 if it is
not available. */
int
iobuf_get_fd (IOBUF a)
{
if (a->directfp)
return fileno ( (FILE*)a->directfp );
for ( ; a; a = a->chain )
if (!a->chain && a->filter == file_filter)
{
file_filter_ctx_t *b = a->filter_ov;
FILEP_OR_FD fp = b->fp;
return my_fileno (fp);
}
return -1;
}
/****************
* Tell the file position, where the next read will take place
*/
off_t
iobuf_tell( IOBUF a )
{
return a->ntotal + a->nbytes;
}
#if !defined(HAVE_FSEEKO) && !defined(fseeko)
#ifdef HAVE_LIMITS_H
# include
#endif
#ifndef LONG_MAX
# define LONG_MAX ((long) ((unsigned long) -1 >> 1))
#endif
#ifndef LONG_MIN
# define LONG_MIN (-1 - LONG_MAX)
#endif
/****************
* A substitute for fseeko, for hosts that don't have it.
*/
static int
fseeko( FILE *stream, off_t newpos, int whence )
{
while( newpos != (long) newpos ) {
long pos = newpos < 0 ? LONG_MIN : LONG_MAX;
if( fseek( stream, pos, whence ) != 0 )
return -1;
newpos -= pos;
whence = SEEK_CUR;
}
return fseek( stream, (long)newpos, whence );
}
#endif
/****************
* This is a very limited implementation. It simply discards all internal
* buffering and removes all filters but the first one.
*/
int
iobuf_seek( IOBUF a, off_t newpos )
{
file_filter_ctx_t *b = NULL;
if( a->directfp ) {
FILE *fp = a->directfp;
if( fseeko( fp, newpos, SEEK_SET ) ) {
log_error("can't seek: %s\n", strerror(errno) );
return -1;
}
clearerr(fp);
}
else {
for( ; a; a = a->chain ) {
if( !a->chain && a->filter == file_filter ) {
b = a->filter_ov;
break;
}
}
if( !a )
return -1;
#ifdef FILE_FILTER_USES_STDIO
if( fseeko( b->fp, newpos, SEEK_SET ) ) {
log_error("can't fseek: %s\n", strerror(errno) );
return -1;
}
#else
#ifdef HAVE_DOSISH_SYSTEM
if (SetFilePointer (b->fp, newpos, NULL, FILE_BEGIN) == 0xffffffff ) {
log_error ("SetFilePointer failed on handle %p: %s\n",
b->fp, w32_strerror (0));
return -1;
}
#else
if ( lseek (b->fp, newpos, SEEK_SET) == (off_t)-1 ) {
log_error("can't lseek: %s\n", strerror(errno) );
return -1;
}
#endif
#endif
}
a->d.len = 0; /* discard buffer */
a->d.start = 0;
a->nbytes = 0;
a->nlimit = 0;
a->nofast &= ~1;
a->ntotal = newpos;
a->error = 0;
/* remove filters, but the last */
if( a->chain )
log_debug("pop_filter called in iobuf_seek - please report\n");
while( a->chain )
pop_filter( a, a->filter, NULL );
return 0;
}
/****************
* Retrieve the real filename
*/
const char *
iobuf_get_real_fname( IOBUF a )
{
if( a->real_fname )
return a->real_fname;
/* the old solution */
for( ; a; a = a->chain )
if( !a->chain && a->filter == file_filter ) {
file_filter_ctx_t *b = a->filter_ov;
return b->print_only_name? NULL : b->fname;
}
return NULL;
}
/****************
* Retrieve the filename
*/
const char *
iobuf_get_fname( IOBUF a )
{
for( ; a; a = a->chain )
if( !a->chain && a->filter == file_filter ) {
file_filter_ctx_t *b = a->filter_ov;
return b->fname;
}
return NULL;
}
/****************
* enable partial block mode as described in the OpenPGP draft.
* LEN is the first length byte on read, but ignored on writes.
*/
void
iobuf_set_partial_block_mode( IOBUF a, size_t len )
{
block_filter_ctx_t *ctx = xmalloc_clear( sizeof *ctx );
assert( a->use == 1 || a->use == 2 );
ctx->use = a->use;
if( !len ) {
if( a->use == 1 )
log_debug("pop_filter called in set_partial_block_mode"
" - please report\n");
pop_filter(a, block_filter, NULL );
}
else {
ctx->partial = 1;
ctx->size = 0;
ctx->first_c = len;
iobuf_push_filter(a, block_filter, ctx );
}
}
/****************
* Same as fgets() but if the buffer is too short a larger one will
* be allocated up to some limit *max_length.
* A line is considered a byte stream ending in a LF.
* Returns the length of the line. EOF is indicated by a line of
* length zero. The last LF may be missing due to an EOF.
* is max_length is zero on return, the line has been truncated.
*
* Note: The buffer is allocated with enough space to append a CR,LF,EOL
*/
unsigned
iobuf_read_line( IOBUF a, byte **addr_of_buffer,
unsigned *length_of_buffer, unsigned *max_length )
{
int c;
char *buffer = *addr_of_buffer;
unsigned length = *length_of_buffer;
unsigned nbytes = 0;
unsigned maxlen = *max_length;
char *p;
if( !buffer ) { /* must allocate a new buffer */
length = 256;
buffer = xmalloc( length );
*addr_of_buffer = buffer;
*length_of_buffer = length;
}
length -= 3; /* reserve 3 bytes (cr,lf,eol) */
p = buffer;
while( (c=iobuf_get(a)) != -1 ) {
if( nbytes == length ) { /* increase the buffer */
if( length > maxlen ) { /* this is out limit */
/* skip the rest of the line */
while( c != '\n' && (c=iobuf_get(a)) != -1 )
;
*p++ = '\n'; /* always append a LF (we have reserved space) */
nbytes++;
*max_length = 0; /* indicate truncation */
break;
}
length += 3; /* correct for the reserved byte */
length += length < 1024? 256 : 1024;
buffer = xrealloc( buffer, length );
*addr_of_buffer = buffer;
*length_of_buffer = length;
length -= 3; /* and reserve again */
p = buffer + nbytes;
}
*p++ = c;
nbytes++;
if( c == '\n' )
break;
}
*p = 0; /* make sure the line is a string */
return nbytes;
}
/* This is the non iobuf specific function */
int
iobuf_translate_file_handle ( int fd, int for_write )
{
#ifdef _WIN32
{
int x;
if ( fd <= 2 )
return fd; /* do not do this for error, stdin, stdout, stderr */
x = _open_osfhandle ( fd, for_write? 1:0 );
if (x==-1 )
log_error ("failed to translate osfhandle %p\n", (void*)fd );
else {
/*log_info ("_open_osfhandle %p yields %d%s\n",
(void*)fd, x, for_write? " for writing":"" );*/
fd = x;
}
}
#endif
return fd;
}
static int
translate_file_handle ( int fd, int for_write )
{
#ifdef _WIN32
#ifdef FILE_FILTER_USES_STDIO
fd = iobuf_translate_file_handle (fd, for_write);
#else
{
int x;
if ( fd == 0 )
x = (int)GetStdHandle (STD_INPUT_HANDLE);
else if (fd == 1)
x = (int)GetStdHandle (STD_OUTPUT_HANDLE);
else if (fd == 2)
x = (int)GetStdHandle (STD_ERROR_HANDLE);
else
x = fd;
if (x == -1)
log_debug ("GetStdHandle(%d) failed: %s\n",
fd, w32_strerror (0));
fd = x;
}
#endif
#endif
return fd;
}
void
iobuf_skip_rest(IOBUF a, unsigned long n, int partial)
{
if ( partial ) {
for (;;) {
if (a->nofast || a->d.start >= a->d.len) {
if (iobuf_readbyte (a) == -1) {
break;
}
} else {
unsigned long count = a->d.len - a->d.start;
a->nbytes += count;
a->d.start = a->d.len;
}
}
} else {
unsigned long remaining = n;
while (remaining > 0) {
if (a->nofast || a->d.start >= a->d.len) {
if (iobuf_readbyte (a) == -1) {
break;
}
--remaining;
} else {
unsigned long count = a->d.len - a->d.start;
if (count > remaining) {
count = remaining;
}
a->nbytes += count;
a->d.start += count;
remaining -= count;
}
}
}
}