예제 #1
0
static int
i_stream_base64_try_encode_line(struct base64_encoder_istream *bstream)
{
	struct istream_private *stream = &bstream->istream;
	const unsigned char *data;
	size_t size, avail, buffer_avail;
	buffer_t buf;

	data = i_stream_get_data(stream->parent, &size);
	if (size == 0 || (size < 3 && !stream->parent->eof))
		return 0;

	if (bstream->cur_line_len == bstream->chars_per_line) {
		/* @UNSAFE: end of line, add newline */
		if (!i_stream_try_alloc(stream, bstream->crlf ? 2 : 1, &avail))
			return -2;

		if (bstream->crlf)
			stream->w_buffer[stream->pos++] = '\r';
		stream->w_buffer[stream->pos++] = '\n';
		bstream->cur_line_len = 0;
	}

	i_stream_try_alloc(stream, (size+2)/3*4, &avail);
	buffer_avail = stream->buffer_size - stream->pos;

	if ((size + 2) / 3 * 4 > buffer_avail) {
		/* can't fit everything to destination buffer.
		   write as much as we can. */
		size = (buffer_avail / 4) * 3;
		if (size == 0)
			return -2;
	} else if (!stream->parent->eof && size % 3 != 0) {
		/* encode 3 chars at a time, so base64_encode() doesn't
		   add '=' characters in the middle of the stream */
		size -= (size % 3);
	}
	i_assert(size != 0);

	if (bstream->cur_line_len + (size+2)/3*4 > bstream->chars_per_line) {
		size = (bstream->chars_per_line - bstream->cur_line_len)/4 * 3;
		i_assert(size != 0);
	}

	buffer_create_from_data(&buf, stream->w_buffer + stream->pos,
				buffer_avail);
	base64_encode(data, size, &buf);
	i_assert(buf.used > 0);

	bstream->cur_line_len += buf.used;
	i_assert(bstream->cur_line_len <= bstream->chars_per_line);
	stream->pos += buf.used;
	i_stream_skip(stream->parent, size);
	return 1;
}
예제 #2
0
파일: istream.c 프로젝트: bdraco/dovecot
void *i_stream_alloc(struct istream_private *stream, size_t size)
{
	size_t old_size, avail_size;

	i_stream_try_alloc(stream, size, &avail_size);
	if (avail_size < size) {
		old_size = stream->buffer_size;
		stream->buffer_size = nearest_power(stream->pos + size);
		stream->w_buffer = i_realloc(stream->w_buffer, old_size,
					     stream->buffer_size);
		stream->buffer = stream->w_buffer;
		i_stream_try_alloc(stream, size, &avail_size);
		i_assert(avail_size >= size);
	}
	return stream->w_buffer + stream->pos;
}
예제 #3
0
static int i_stream_dot_read_some(struct dot_istream *dstream)
{
	struct istream_private *stream = &dstream->istream;
	size_t size, avail;
	ssize_t ret;

	size = i_stream_get_data_size(stream->parent);
	if (size == 0) {
		ret = i_stream_read(stream->parent);
		if (ret <= 0 && (ret != -2 || stream->skip == 0)) {
			if (stream->parent->stream_errno != 0) {
				stream->istream.stream_errno =
					stream->parent->stream_errno;
			} else if (ret < 0 && stream->parent->eof) {
				/* we didn't see "." line */
				io_stream_set_error(&stream->iostream,
					"dot-input stream ends without '.' line");
				stream->istream.stream_errno = EPIPE;
			}
			return ret;
		}
		size = i_stream_get_data_size(stream->parent);
		i_assert(size != 0);
	}

	if (!i_stream_try_alloc(stream, size, &avail))
		return -2;
	return 1;
}
static ssize_t i_stream_nonuls_read(struct istream_private *stream)
{
	struct nonuls_istream *nstream = (struct nonuls_istream *)stream;
	const unsigned char *data, *p;
	size_t i, size, avail_size;
	int ret;

	if ((ret = i_stream_read_parent(stream)) <= 0)
		return ret;

	data = i_stream_get_data(stream->parent, &size);
	if (!i_stream_try_alloc(stream, size, &avail_size))
		return -2;
	if (size > avail_size)
		size = avail_size;
	i_assert(size > 0);

	p = memchr(data, '\0', size);
	if (p == NULL) {
		/* no NULs in this block */
		memcpy(stream->w_buffer+stream->pos, data, size);
	} else {
		i = p-data;
		memcpy(stream->w_buffer+stream->pos, data, i);
		for (; i < size; i++) {
			stream->w_buffer[stream->pos+i] = data[i] == '\0' ?
				nstream->replace_chr : data[i];
		}
	}
	stream->pos += size;
	i_stream_skip(stream->parent, size);
	return size;
}
예제 #5
0
static bool read_from_buffer(struct seekable_istream *sstream, ssize_t *ret_r)
{
	struct istream_private *stream = &sstream->istream;
	const unsigned char *data;
	size_t size, avail_size;

	if (stream->pos < sstream->buffer_peak) {
		/* This could be the first read() or we could have already
		   seeked backwards. */
		i_assert(stream->pos == 0 && stream->skip == 0);
		stream->skip = stream->istream.v_offset;
		stream->pos = sstream->buffer_peak;
		size = stream->pos - stream->skip;
	} else {
		/* need to read more */
		i_assert(stream->pos == sstream->buffer_peak);
		size = sstream->cur_input == NULL ? 0 :
			i_stream_get_data_size(sstream->cur_input);
		if (size == 0) {
			/* read more to buffer */
			*ret_r = read_more(sstream);
			if (*ret_r == 0 || *ret_r == -1)
				return TRUE;
		}

		/* we should have more now. */
		data = i_stream_get_data(sstream->cur_input, &size);
		i_assert(size > 0);

		/* change skip to 0 temporarily so i_stream_try_alloc() won't try to
		   compress the buffer. */
		size_t old_skip = stream->skip;
		stream->skip = 0;
		bool have_space = i_stream_try_alloc(stream, size, &avail_size);
		stream->skip = old_skip;
		if (!have_space)
			return FALSE;

		if (size > avail_size)
			size = avail_size;
		memcpy(stream->w_buffer + stream->pos, data, size);
		stream->pos += size;
		sstream->buffer_peak += size;
		i_stream_skip(sstream->cur_input, size);
	}

	*ret_r = size;
	i_assert(*ret_r > 0);
	return TRUE;
}
예제 #6
0
파일: istream.c 프로젝트: bdraco/dovecot
bool i_stream_add_data(struct istream *_stream, const unsigned char *data,
		       size_t size)
{
	struct istream_private *stream = _stream->real_stream;
	size_t size2;

	i_stream_try_alloc(stream, size, &size2);
	if (size > size2)
		return FALSE;

	memcpy(stream->w_buffer + stream->pos, data, size);
	stream->pos += size;
	return TRUE;
}
static int
i_stream_qp_try_decode_input(struct qp_decoder_istream *bstream, bool eof)
{
    struct istream_private *stream = &bstream->istream;
    const unsigned char *data;
    size_t size, avail, buffer_avail, pos;
    buffer_t buf;
    int ret;

    data = i_stream_get_data(stream->parent, &size);
    if (size == 0)
        return 0;

    /* normally the decoded quoted-printable content can't be larger than
       the encoded content, but because we always use CRLFs, it may use
       twice as much space by only converting LFs to CRLFs. */
    i_stream_try_alloc(stream, size, &avail);
    buffer_avail = stream->buffer_size - stream->pos;

    if (size > buffer_avail/2) {
        /* can't fit everything to destination buffer.
           write as much as we can. */
        size = buffer_avail/2;
        if (size == 0)
            return -2;
    }

    buffer_create_from_data(&buf, stream->w_buffer + stream->pos,
                            buffer_avail);
    ret = !eof ? quoted_printable_decode(data, size, &pos, &buf) :
          quoted_printable_decode_final(data, size, &pos, &buf);
    if (ret < 0) {
        io_stream_set_error(&stream->iostream,
                            "Invalid quoted-printable data: 0x%s",
                            binary_to_hex(data+pos, I_MAX(size-pos, 8)));
        stream->istream.stream_errno = EINVAL;
        return -1;
    }

    stream->pos += buf.used;
    i_stream_skip(stream->parent, pos);
    return pos > 0 ? 1 : 0;
}
예제 #8
0
static int
i_stream_qp_try_decode_input(struct qp_decoder_istream *bstream, bool eof)
{
	struct istream_private *stream = &bstream->istream;
	const unsigned char *data;
	size_t size, avail, buffer_avail, pos;
	buffer_t buf;
	int ret;

	data = i_stream_get_data(stream->parent, &size);
	if (size == 0)
		return 0;

	/* the decoded quoted-printable content can never be larger than the
	   encoded content. at worst they are equal. */
	i_stream_try_alloc(stream, size, &avail);
	buffer_avail = stream->buffer_size - stream->pos;

	if (size > buffer_avail) {
		/* can't fit everything to destination buffer.
		   write as much as we can. */
		size = buffer_avail;
		if (size == 0)
			return -2;
	}

	buffer_create_from_data(&buf, stream->w_buffer + stream->pos,
				buffer_avail);
	ret = !eof ? quoted_printable_decode(data, size, &pos, &buf) :
		quoted_printable_decode_final(data, size, &pos, &buf);
	if (ret < 0) {
		stream->istream.stream_errno = EINVAL;
		return -1;
	}

	stream->pos += buf.used;
	i_stream_skip(stream->parent, pos);
	return pos > 0 ? 1 : 0;
}
예제 #9
0
static int
i_stream_base64_try_decode_block(struct base64_decoder_istream *bstream)
{
	struct istream_private *stream = &bstream->istream;
	const unsigned char *data;
	size_t size, avail, buffer_avail, pos;
	buffer_t buf;

	data = i_stream_get_data(stream->parent, &size);
	if (size == 0)
		return 0;

	i_stream_try_alloc(stream, (size+3)/4*3, &avail);
	buffer_avail = stream->buffer_size - stream->pos;

	if ((size + 3) / 4 * 3 > buffer_avail) {
		/* can't fit everything to destination buffer.
		   write as much as we can. */
		size = (buffer_avail / 3) * 4;
		if (size == 0)
			return -2;
	}

	buffer_create_from_data(&buf, stream->w_buffer + stream->pos,
				buffer_avail);
	if (base64_decode(data, size, &pos, &buf) < 0) {
		io_stream_set_error(&stream->iostream,
			"Invalid base64 data: 0x%s",
			binary_to_hex(data+pos, I_MIN(size-pos, 8)));
		stream->istream.stream_errno = EINVAL;
		return -1;
	}

	stream->pos += buf.used;
	i_stream_skip(stream->parent, pos);
	return pos > 0 ? 1 : 0;
}
static ssize_t quoted_string_istream_read(struct istream_private *stream)
{
	struct quoted_string_istream *qsstream =
		(struct quoted_string_istream *)stream;
	const unsigned char *data;
	size_t i, dest, size, avail;
	ssize_t ret = 0;
	bool slash;

	if ( qsstream->finished ) {
		stream->istream.eof = TRUE;
		return -1;
	}

	/* Read from parent */
	data = i_stream_get_data(stream->parent, &size);
	if (size == 0) {
		ret = i_stream_read(stream->parent);
		if (ret <= 0 && (ret != -2 || stream->skip == 0)) {
			if ( stream->parent->eof && stream->parent->stream_errno == 0 ) {
				io_stream_set_error(&stream->iostream,
					"Quoted string ends without closing quotes");
				stream->istream.stream_errno = EINVAL;
				return -1;
			}

			stream->istream.stream_errno = stream->parent->stream_errno;
			stream->istream.eof = stream->parent->eof;
			return ret;
		}
		data = i_stream_get_data(stream->parent, &size);
		i_assert(size != 0);
	}

	/* Allocate buffer space */
	if (!i_stream_try_alloc(stream, size, &avail))
		return -2;

	/* Parse quoted string content */
	dest = stream->pos;
	slash = qsstream->pending_slash;
	ret = 0;
	for (i = 0; i < size && dest < stream->buffer_size; i++) {
		if ( data[i] == '"' ) {
			if ( !slash ) {
				qsstream->finished = TRUE;
				i++;
				break;
			}
			slash = FALSE;
		} else if ( data[i] == '\\' ) {
			if ( !slash ) {
				slash = TRUE;
				continue;
			}
			slash = FALSE;
		} else if ( slash ) {
			if ( !IS_QUOTED_SPECIAL(data[i]) ) {
				io_stream_set_error(&stream->iostream,
					"Escaped quoted-string character is not a QUOTED-SPECIAL");
				stream->istream.stream_errno = EINVAL;
				ret = -1;
				break;
			}
			slash = FALSE;
		}

		if ( (data[i] & 0x80) == 0 && ( data[i] == '\r' || data[i] == '\n' ) ) {
			io_stream_set_error(&stream->iostream,
				"Quoted string contains an invalid character");
			stream->istream.stream_errno = EINVAL;
			ret = -1;
			break;
		}

		stream->w_buffer[dest++] = data[i];
	}

	i_stream_skip(stream->parent, i);
	qsstream->pending_slash = slash;

	if ( ret < 0 ) {
		stream->pos = dest;
		return ret;
	}

	ret = dest - stream->pos;
	if (ret == 0) {
		if ( qsstream->finished ) {
			stream->istream.eof = TRUE;
			return -1;
		}
		i_assert(qsstream->pending_slash && size == 1);
		return quoted_string_istream_read(stream);
	}
	i_assert(ret > 0);
	stream->pos = dest;
	return ret;
}
예제 #11
0
static ssize_t i_stream_file_read(struct istream_private *stream)
{
	struct file_istream *fstream = (struct file_istream *) stream;
	uoff_t offset;
	size_t size;
	ssize_t ret;

	if (!i_stream_try_alloc(stream, 1, &size))
		return -2;

	if (stream->fd == -1) {
		if (i_stream_file_open(stream) < 0)
			return -1;
	}

	offset = stream->istream.v_offset + (stream->pos - stream->skip);
	do {
		if (fstream->file) {
			ret = pread(stream->fd, stream->w_buffer + stream->pos,
				    size, offset);
		} else if (fstream->seen_eof) {
			/* don't try to read() again. EOF from keyboard (^D)
			   requires this to work right. */
			ret = 0;
		} else {
			ret = read(stream->fd, stream->w_buffer + stream->pos,
				   size);
		}
	} while (unlikely(ret < 0 && errno == EINTR &&
			  stream->istream.blocking));

	if (ret == 0) {
		/* EOF */
		stream->istream.eof = TRUE;
		fstream->seen_eof = TRUE;
		return -1;
	}

	if (unlikely(ret < 0)) {
		if (errno == EINTR || errno == EAGAIN) {
			i_assert(!stream->istream.blocking);
			ret = 0;
		} else {
			i_assert(errno != 0);
			/* if we get EBADF for a valid fd, it means something's
			   really wrong and we'd better just crash. */
			i_assert(errno != EBADF);
			if (fstream->file) {
				io_stream_set_error(&stream->iostream,
					"pread(size=%"PRIuSIZE_T
					" offset=%"PRIuUOFF_T") failed: %m",
					size, offset);
			} else {
				io_stream_set_error(&stream->iostream,
					"read(size=%"PRIuSIZE_T") failed: %m",
					size);
			}
			stream->istream.stream_errno = errno;
			return -1;
		}
	}

	if (ret > 0 && fstream->skip_left > 0) {
		i_assert(!fstream->file);
		i_assert(stream->skip == stream->pos);

		if (fstream->skip_left >= (size_t)ret) {
			fstream->skip_left -= ret;
			ret = 0;
		} else {
			ret -= fstream->skip_left;
			stream->pos += fstream->skip_left;
			stream->skip += fstream->skip_left;
			fstream->skip_left = 0;
		}
	}

	stream->pos += ret;
	i_assert(ret != 0 || !fstream->file);
	i_assert(ret != -1);
	return ret;
}
예제 #12
0
static ssize_t i_stream_file_read(struct istream_private *stream)
{
	struct file_istream *fstream = (struct file_istream *) stream;
	size_t size;
	ssize_t ret;

	if (!i_stream_try_alloc(stream, 1, &size))
		return -2;

	if (stream->fd == -1) {
		if (i_stream_file_open(stream) < 0)
			return -1;
	}

	do {
		if (fstream->file) {
			ret = pread(stream->fd, stream->w_buffer + stream->pos,
				    size, stream->istream.v_offset +
				    (stream->pos - stream->skip));
		} else if (fstream->seen_eof) {
			/* don't try to read() again. EOF from keyboard (^D)
			   requires this to work right. */
			ret = 0;
		} else {
			ret = read(stream->fd, stream->w_buffer + stream->pos,
				   size);
		}
	} while (unlikely(ret < 0 && errno == EINTR &&
			  stream->istream.blocking));

	if (ret == 0) {
		/* EOF */
		stream->istream.eof = TRUE;
		fstream->seen_eof = TRUE;
		return -1;
	}

	if (unlikely(ret < 0)) {
		if (errno == EINTR || errno == EAGAIN) {
			i_assert(!stream->istream.blocking);
			ret = 0;
		} else {
			i_assert(errno != 0);
			stream->istream.stream_errno = errno;
			return -1;
		}
	}

	if (ret > 0 && fstream->skip_left > 0) {
		i_assert(!fstream->file);
		i_assert(stream->skip == stream->pos);

		if (fstream->skip_left >= (size_t)ret) {
			fstream->skip_left -= ret;
			ret = 0;
		} else {
			ret -= fstream->skip_left;
			stream->pos += fstream->skip_left;
			stream->skip += fstream->skip_left;
			fstream->skip_left = 0;
		}
	}

	stream->pos += ret;
	i_assert(ret != 0 || !fstream->file);
	i_assert(ret != -1);
	return ret;
}
예제 #13
0
static ssize_t
http_transfer_chunked_istream_read_data(
	struct http_transfer_chunked_istream *tcstream)
{
	struct istream_private *stream = &tcstream->istream;
	const unsigned char *data;
	size_t size, avail;
	ssize_t ret = 0;

	if (tcstream->chunk_pos >= tcstream->chunk_size) {
		tcstream->state = HTTP_CHUNKED_PARSE_STATE_DATA_READY;
		return 0;
	}

	// FIXME: is this even necessary?
	i_stream_seek(stream->parent, tcstream->chunk_v_offset + tcstream->chunk_pos);

	/* read from parent if necessary */
	data = i_stream_get_data(stream->parent, &size);
	if (size == 0) {
		ret = i_stream_read(stream->parent);
		if (ret <= 0 && (ret != -2 || stream->skip == 0)) {
			if ( stream->parent->eof && stream->parent->stream_errno == 0 ) {
				/* unexpected EOF */
				tcstream->error = "Unexpected end of payload";
				stream->istream.stream_errno = EIO;
			} else {
				/* parent stream error */
				tcstream->error = "Stream error";
				stream->istream.stream_errno = stream->parent->stream_errno;
			}
			return ret;
		}
		data = i_stream_get_data(stream->parent, &size);
		i_assert(size != 0);
	}

	size = size > (tcstream->chunk_size - tcstream->chunk_pos) ?
		(tcstream->chunk_size - tcstream->chunk_pos) : size;

	/* Allocate buffer space */
	if (!i_stream_try_alloc(stream, size, &avail))
		return -2;

	/* Copy payload */
	size = size > avail ? avail : size;
	memcpy(&stream->w_buffer[stream->pos], data, size);

	i_stream_skip(stream->parent, size);

	tcstream->chunk_pos += size;
	if (tcstream->chunk_pos >= tcstream->chunk_size)
		tcstream->state = HTTP_CHUNKED_PARSE_STATE_DATA_READY;

	if ( ret < 0 ) {
		stream->pos = stream->pos+size;
		return ret;
	}

	ret = size;
	stream->pos = stream->pos+size;
	return ret;
}
예제 #14
0
static ssize_t i_stream_ssl_read_real(struct istream_private *stream)
{
	struct ssl_istream *sstream = (struct ssl_istream *)stream;
	struct ssl_iostream *ssl_io = sstream->ssl_io;
	unsigned char buffer[IO_BLOCK_SIZE];
	size_t max_buffer_size = i_stream_get_max_buffer_size(&stream->istream);
	size_t orig_max_buffer_size = stream->max_buffer_size;
	size_t size;
	ssize_t ret, total_ret;

	if (sstream->seen_eof) {
		stream->istream.eof = TRUE;
		return -1;
	}

	if (stream->pos >= max_buffer_size) {
		i_stream_compress(stream);
		if (stream->pos >= max_buffer_size)
			return -2;
	}

	ret = openssl_iostream_more(ssl_io);
	if (ret <= 0) {
		if (ret < 0) {
			/* handshake failed */
			i_assert(errno != 0);
			io_stream_set_error(&stream->iostream,
					    "%s", ssl_io->last_error);
			stream->istream.stream_errno = errno;
		}
		return ret;
	}

	if (!i_stream_try_alloc(stream, 1, &size))
		i_unreached();
	if (stream->pos + size > max_buffer_size) {
		i_assert(max_buffer_size > stream->pos);
		size = max_buffer_size - stream->pos;
	}

	while ((ret = SSL_read(ssl_io->ssl,
			       stream->w_buffer + stream->pos, size)) <= 0) {
		/* failed to read anything */
		ret = openssl_iostream_handle_error(ssl_io, ret, "SSL_read");
		if (ret <= 0) {
			if (ret == 0)
				return 0;
			if (ssl_io->last_error != NULL) {
				io_stream_set_error(&stream->iostream,
						    "%s", ssl_io->last_error);
			}
			if (errno != EPIPE)
				stream->istream.stream_errno = errno;
			stream->istream.eof = TRUE;
			sstream->seen_eof = TRUE;
			return -1;
		}
		/* we did some BIO I/O, try reading again */
	}
	stream->pos += ret;
	total_ret = ret;

	/* now make sure that we read everything already buffered in OpenSSL
	   into the stream (without reading anything more). this makes I/O loop
	   behave similary for ssl-istream as file-istream. */
	sstream->ssl_io->input_handler = FALSE;
	stream->max_buffer_size = (size_t)-1;
	while ((ret = SSL_read(ssl_io->ssl, buffer, sizeof(buffer))) > 0) {
		memcpy(i_stream_alloc(stream, ret), buffer, ret);
		stream->pos += ret;
		total_ret += ret;
	}
	stream->max_buffer_size = orig_max_buffer_size;
	return total_ret;
}
예제 #15
0
static ssize_t i_stream_dot_read(struct istream_private *stream)
{
	/* @UNSAFE */
	struct dot_istream *dstream = (struct dot_istream *)stream;
	const unsigned char *data;
	size_t i, dest, size, avail;
	ssize_t ret, ret1;

	if (dstream->pending[0] != '\0') {
		if (!i_stream_try_alloc(stream, 1, &avail))
			return -2;
		dest = stream->pos;
		(void)flush_pending(dstream, &dest);
	} else {
		dest = stream->pos;
	}

	if (dstream->dot_eof) {
		stream->istream.eof = TRUE;
		return i_stream_dot_return(stream, dest, -1);
	}

	/* we have to update stream->pos before reading more data */
	ret1 = i_stream_dot_return(stream, dest, 0);
	if ((ret = i_stream_dot_read_some(dstream)) <= 0) {
		if (ret1 != 0)
			return ret1;
		dest = stream->pos;
		if (ret == -1 && dstream->state != 0)
			(void)flush_dot_state(dstream, &dest);
		return i_stream_dot_return(stream, dest, ret);
	}
	dest = stream->pos;

	data = i_stream_get_data(stream->parent, &size);
	for (i = 0; i < size && dest < stream->buffer_size; i++) {
		switch (dstream->state) {
		case 0:
			break;
		case 1:
			/* CR seen */
			if (data[i] == '\n')
				dstream->state++;
			else {
				if (!flush_dot_state(dstream, &dest))
					goto end;
			}
			break;
		case 2:
			/* [CR]LF seen */
			if (data[i] == '.')
				dstream->state++;
			else {
				if (!flush_dot_state(dstream, &dest))
					goto end;
			}
			break;
		case 3:
			/* [CR]LF. seen */
			if (data[i] == '\r')
				dstream->state++;
			else if (data[i] == '\n') {
				/* EOF */
				i_stream_dot_eof(dstream, &dest);
				i++;
				goto end;
			} else {
				/* drop the initial dot */
				if (!flush_dot_state(dstream, &dest))
					goto end;
			}
			break;
		case 4:
			/* [CR]LF.CR seen */
			if (data[i] == '\n') {
				/* EOF */
				i_stream_dot_eof(dstream, &dest);
				i++;
				goto end;
			} else {
				/* drop the initial dot */
				if (!flush_dot_state(dstream, &dest))
					goto end;
			}
		}
		if (dstream->state == 0) {
			if (data[i] == '\r') {
				dstream->state = 1;
				dstream->state_no_cr = FALSE;
			} else if (data[i] == '\n') {
				dstream->state = 2;
				dstream->state_no_cr = TRUE;
			} else {
				stream->w_buffer[dest++] = data[i];
			}
		}
	}
end:
	i_stream_skip(stream->parent, i);

	ret = i_stream_dot_return(stream, dest, 0) + ret1;
	if (ret == 0)
		return i_stream_dot_read(stream);
	i_assert(ret > 0);
	return ret;
}
예제 #16
0
파일: istream-zlib.c 프로젝트: bdraco/core
static ssize_t i_stream_zlib_read(struct istream_private *stream)
{
	struct zlib_istream *zstream = (struct zlib_istream *)stream;
	const unsigned char *data;
	uoff_t high_offset;
	size_t size, out_size;
	int ret;

	high_offset = stream->istream.v_offset + (stream->pos - stream->skip);
	if (zstream->eof_offset == high_offset) {
		i_assert(zstream->high_pos == 0 ||
			 zstream->high_pos == stream->pos);
		if (!zstream->trailer_read) {
			do {
				ret = i_stream_zlib_read_trailer(zstream);
			} while (ret == 0 && stream->istream.blocking);
			if (ret <= 0)
				return ret;
		}
		if (!zstream->gz || i_stream_read_eof(stream->parent)) {
			stream->istream.eof = TRUE;
			return -1;
		}
		zstream->starting_concated_output = TRUE;
	}
	if (zstream->starting_concated_output) {
		/* make sure there actually is something in parent stream.
		   we don't want to reset the stream unless we actually see
		   some concated output. */
		ret = i_stream_read_more(stream->parent, &data, &size);
		if (ret <= 0) {
			if (ret == 0)
				return 0;
			if (stream->parent->stream_errno != 0) {
				stream->istream.stream_errno =
					stream->parent->stream_errno;
			}
			stream->istream.eof = TRUE;
			return -1;
		}

		/* gzip file with concatenated content */
		zstream->eof_offset = (uoff_t)-1;
		zstream->stream_size = (uoff_t)-1;
		zstream->header_read = FALSE;
		zstream->trailer_read = FALSE;
		zstream->crc32 = 0;
		zstream->starting_concated_output = FALSE;

		(void)inflateEnd(&zstream->zs);
		i_stream_zlib_init(zstream);
	}

	if (!zstream->header_read) {
		do {
			ret = i_stream_zlib_read_header(stream);
		} while (ret == 0 && stream->istream.blocking);
		if (ret <= 0)
			return ret;
		zstream->header_read = TRUE;
	}

	if (stream->pos < zstream->high_pos) {
		/* we're here because we seeked back within the read buffer. */
		ret = zstream->high_pos - stream->pos;
		stream->pos = zstream->high_pos;
		zstream->high_pos = 0;
		if (zstream->trailer_read) {
			high_offset = stream->istream.v_offset +
				(stream->pos - stream->skip);
			i_assert(zstream->eof_offset == high_offset);
			stream->istream.eof = TRUE;
		}
		return ret;
	}
	zstream->high_pos = 0;

	if (!zstream->marked) {
		if (!i_stream_try_alloc(stream, CHUNK_SIZE, &out_size))
			return -2; /* buffer full */
	} else {
		/* try to avoid compressing, so we can quickly seek backwards */
		if (!i_stream_try_alloc_avoid_compress(stream, CHUNK_SIZE, &out_size))
			return -2; /* buffer full */
	}

	if (i_stream_read_more(stream->parent, &data, &size) < 0) {
		if (stream->parent->stream_errno != 0) {
			stream->istream.stream_errno =
				stream->parent->stream_errno;
		} else {
			i_assert(stream->parent->eof);
			zlib_read_error(zstream, "unexpected EOF");
			stream->istream.stream_errno = EPIPE;
		}
		return -1;
	}
	if (size == 0) {
		/* no more input */
		i_assert(!stream->istream.blocking);
		return 0;
	}

	zstream->zs.next_in = (void *)data;
	zstream->zs.avail_in = size;

	zstream->zs.next_out = stream->w_buffer + stream->pos;
	zstream->zs.avail_out = out_size;
	ret = inflate(&zstream->zs, Z_SYNC_FLUSH);

	out_size -= zstream->zs.avail_out;
	zstream->crc32 = crc32_data_more(zstream->crc32,
					 stream->w_buffer + stream->pos,
					 out_size);
	stream->pos += out_size;

	i_stream_skip(stream->parent, size - zstream->zs.avail_in);

	switch (ret) {
	case Z_OK:
		break;
	case Z_NEED_DICT:
		zlib_read_error(zstream, "can't read file without dict");
		stream->istream.stream_errno = EIO;
		return -1;
	case Z_DATA_ERROR:
		zlib_read_error(zstream, "corrupted data");
		stream->istream.stream_errno = EINVAL;
		return -1;
	case Z_MEM_ERROR:
		i_fatal_status(FATAL_OUTOFMEM, "zlib.read(%s): Out of memory",
			       i_stream_get_name(&stream->istream));
	case Z_STREAM_END:
		zstream->eof_offset = stream->istream.v_offset +
			(stream->pos - stream->skip);
		zstream->stream_size = zstream->eof_offset;
		zstream->zs.avail_in = 0;

		if (!zstream->trailer_read) {
			/* try to read and verify the trailer, we might not
			   be called again. */
			if (i_stream_zlib_read_trailer(zstream) < 0)
				return -1;
		}
		break;
	default:
		i_fatal("inflate() failed with %d", ret);
	}
	if (out_size == 0) {
		/* read more input */
		return i_stream_zlib_read(stream);
	}
	return out_size;
}