Example #1
0
static int metadata_header_read(struct metawrap_istream *mstream)
{
	char *line, *p;

	while ((line = i_stream_read_next_line(mstream->istream.parent)) != NULL) {
		if (*line == '\0') {
			mstream->callback(NULL, NULL, mstream->context);
			return 1;
		}
		p = strchr(line, ':');
		if (p == NULL) {
			io_stream_set_error(&mstream->istream.iostream,
				"Metadata header line is missing ':'");
			mstream->istream.istream.stream_errno = EINVAL;
			return -1;
		}
		*p++ = '\0';
		mstream->callback(line, p, mstream->context);
	}
	if (mstream->istream.parent->eof) {
		if (mstream->istream.parent->stream_errno != 0) {
			mstream->istream.istream.stream_errno =
				mstream->istream.parent->stream_errno;
		} else {
			io_stream_set_error(&mstream->istream.iostream,
				"Metadata header is missing ending line");
			mstream->istream.istream.stream_errno = EINVAL;
			return -1;
		}
		mstream->istream.istream.eof = TRUE;
		return -1;
	}
	i_assert(!mstream->istream.parent->blocking);
	return 0;
}
Example #2
0
static int
i_stream_file_stat(struct istream_private *stream, bool exact ATTR_UNUSED)
{
	struct file_istream *fstream = (struct file_istream *) stream;
	const char *name = i_stream_get_name(&stream->istream);

	if (!fstream->file) {
		/* return defaults */
	} else if (stream->fd != -1) {
		if (fstat(stream->fd, &stream->statbuf) < 0) {
			stream->istream.stream_errno = errno;
			io_stream_set_error(&stream->iostream,
				"file_istream.fstat(%s) failed: %m", name);
			i_error("%s", i_stream_get_error(&stream->istream));
			return -1;
		}
	} else {
		if (stat(name, &stream->statbuf) < 0) {
			stream->istream.stream_errno = errno;
			io_stream_set_error(&stream->iostream,
				"file_istream.stat(%s) failed: %m", name);
			i_error("%s", i_stream_get_error(&stream->istream));
			return -1;
		}
	}
	return 0;
}
Example #3
0
void i_stream_default_seek_nonseekable(struct istream_private *stream,
				       uoff_t v_offset, bool mark ATTR_UNUSED)
{
	size_t available;

	if (stream->istream.v_offset > v_offset)
		i_panic("stream %s doesn't support seeking backwards",
			i_stream_get_name(&stream->istream));

	while (stream->istream.v_offset < v_offset) {
		(void)i_stream_read(&stream->istream);

		available = stream->pos - stream->skip;
		if (available == 0) {
			if (stream->istream.stream_errno != 0) {
				/* read failed */
				return;
			}
			io_stream_set_error(&stream->iostream,
				"Can't seek to offset %"PRIuUOFF_T
				", because we have data only up to offset %"
				PRIuUOFF_T" (eof=%d)", v_offset,
				stream->istream.v_offset, stream->istream.eof ? 1 : 0);
			stream->istream.stream_errno = ESPIPE;
			return;
		}
		if (available <= v_offset - stream->istream.v_offset)
			i_stream_skip(&stream->istream, available);
		else {
			i_stream_skip(&stream->istream,
				      v_offset - stream->istream.v_offset);
		}
	}
}
Example #4
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;
}
Example #5
0
static ssize_t i_stream_base64_decoder_read(struct istream_private *stream)
{
	struct base64_decoder_istream *bstream =
		(struct base64_decoder_istream *)stream;
	const unsigned char *data;
	size_t pre_count, post_count, size;
	int ret;

	do {
		ret = i_stream_read_parent(stream);
		if (ret <= 0) {
			if (ret < 0 && stream->istream.stream_errno == 0 &&
			    i_stream_get_data_size(stream->parent) > 0) {
				/* base64 input with a partial block */
				data = i_stream_get_data(stream->parent, &size);
				io_stream_set_error(&stream->iostream,
					"base64 input ends with a partial block: 0x%s",
					binary_to_hex(data, size));
				stream->istream.stream_errno = EINVAL;
			}
			return ret;
		}

		/* encode as many blocks as fits into destination buffer */
		pre_count = stream->pos - stream->skip;
		while ((ret = i_stream_base64_try_decode_block(bstream)) > 0) ;
		post_count = stream->pos - stream->skip;
	} while (ret == 0 && pre_count == post_count);

	if (ret < 0 && pre_count == post_count)
		return ret;

	i_assert(post_count > pre_count);
	return post_count - pre_count;
}
Example #6
0
static int o_stream_ssl_flush(struct ostream_private *stream)
{
	struct ssl_ostream *sstream = (struct ssl_ostream *)stream;
	int ret;

	if ((ret = openssl_iostream_more(sstream->ssl_io)) < 0) {
		/* handshake failed */
		io_stream_set_error(&stream->iostream, "%s",
				    sstream->ssl_io->last_error);
		stream->ostream.stream_errno = errno;
	} else if (ret > 0 && sstream->buffer != NULL &&
		   sstream->buffer->used > 0) {
		/* we can try to send some of our buffered data */
		ret = o_stream_ssl_flush_buffer(sstream);
	}

	if (ret == 0 && sstream->ssl_io->want_read) {
		/* we need to read more data until we can continue. */
		o_stream_set_flush_pending(sstream->ssl_io->plain_output,
					   FALSE);
		sstream->ssl_io->ostream_flush_waiting_input = TRUE;
		ret = 1;
	}
	return ret;
}
Example #7
0
static int o_stream_ssl_flush_buffer(struct ssl_ostream *sstream)
{
	size_t pos = 0;
	int ret = 1;

	while (pos < sstream->buffer->used) {
		/* we're writing plaintext data to OpenSSL, which it encrypts
		   and writes to bio_int's buffer. ssl_iostream_bio_sync()
		   reads it from there and adds to plain_output stream. */
		ret = SSL_write(sstream->ssl_io->ssl,
				CONST_PTR_OFFSET(sstream->buffer->data, pos),
				sstream->buffer->used - pos);
		if (ret <= 0) {
			ret = openssl_iostream_handle_write_error(sstream->ssl_io,
								  ret, "SSL_write");
			if (ret < 0) {
				io_stream_set_error(&sstream->ostream.iostream,
					"%s", sstream->ssl_io->last_error);
				sstream->ostream.ostream.stream_errno = errno;
				break;
			}
			if (ret == 0)
				break;
		} else {
			pos += ret;
			(void)openssl_iostream_bio_sync(sstream->ssl_io);
		}
	}
	buffer_delete(sstream->buffer, 0, pos);
	return ret <= 0 ? ret : 1;
}
Example #8
0
char *i_stream_read_next_line(struct istream *stream)
{
	char *line;

	for (;;) {
		line = i_stream_next_line(stream);
		if (line != NULL)
			break;

		switch (i_stream_read(stream)) {
		case -2:
			io_stream_set_error(&stream->real_stream->iostream,
				"Line is too long (over %"PRIuSIZE_T
				" bytes at offset %"PRIuUOFF_T")",
				i_stream_get_data_size(stream), stream->v_offset);
			stream->stream_errno = errno = ENOBUFS;
			stream->eof = TRUE;
			return NULL;
		case -1:
			return i_stream_last_line(stream->real_stream);
		case 0:
			return NULL;
		}
	}
	return line;
}
Example #9
0
static void
i_stream_mail_set_size_corrupted(struct mail_istream *mstream, size_t size)
{
	uoff_t cur_size = mstream->istream.istream.v_offset + size;
	const char *str, *mail_id;
	char chr;

	if (mstream->expected_size < cur_size) {
		str = "smaller";
		chr = '<';
	} else {
		str = "larger";
		chr = '>';
	}

	mail_id = i_stream_mail_get_cached_mail_id(mstream);
	if (mail_id[0] != '\0')
		mail_id = t_strconcat(", cached ", mail_id, NULL);
	io_stream_set_error(&mstream->istream.iostream,
		"Cached message size %s than expected "
		"(%"PRIuUOFF_T" %c %"PRIuUOFF_T", box=%s, UID=%u%s)", str,
		mstream->expected_size, chr, cur_size,
		mailbox_get_vname(mstream->mail->box),
		mstream->mail->uid, mail_id);
	mail_set_cache_corrupted_reason(mstream->mail, MAIL_FETCH_PHYSICAL_SIZE,
		t_strdup_printf("read(%s) failed: %s",
				i_stream_get_name(&mstream->istream.istream),
				mstream->istream.iostream.error));
	mstream->istream.istream.stream_errno = EINVAL;
}
Example #10
0
static void lzma_read_error(struct lzma_istream *zstream, const char *error)
{
	io_stream_set_error(&zstream->istream.iostream,
			    "lzma.read(%s): %s at %"PRIuUOFF_T,
			    i_stream_get_name(&zstream->istream.istream), error,
			    i_stream_get_absolute_offset(&zstream->istream.istream));
	if (zstream->log_errors)
		i_error("%s", zstream->istream.iostream.error);
}
static void lz4_read_error(struct lz4_istream *zstream, const char *error)
{
	io_stream_set_error(&zstream->istream.iostream,
			    "lz4.read(%s): %s at %"PRIuUOFF_T,
			    i_stream_get_name(&zstream->istream.istream), error,
			    zstream->istream.abs_start_offset +
			    zstream->istream.istream.v_offset);
	if (zstream->log_errors)
		i_error("%s", zstream->istream.iostream.error);
}
Example #12
0
static ssize_t
o_stream_failure_at_sendv(struct ostream_private *stream,
			  const struct const_iovec *iov, unsigned int iov_count)
{
	struct failure_at_ostream *fstream =
		(struct failure_at_ostream *)stream;
	unsigned int i;
	struct const_iovec *iov_dup;
	unsigned int iov_dup_count;
	uoff_t bytes_until_failure, blocking_bytes_count = 0;
	ssize_t ret;

	if (stream->ostream.blocking) {
		/* blocking ostream must return either a full success or a
		   failure. if the current write would go past failure_offset,
		   return a failure now before writing anything. */
		for (i = 0; i < iov_count; i++)
			blocking_bytes_count += iov[i].iov_len;
		if (blocking_bytes_count > 0) {
			/* if we're exactly at the failure offset after this
			   write, fail it only on the next write. */
			blocking_bytes_count--;
		}
	}

	if (fstream->failure_offset <= stream->ostream.offset + blocking_bytes_count) {
		io_stream_set_error(&stream->iostream, "%s",
				    fstream->error_string);
		stream->ostream.stream_errno = errno = EIO;
		fstream->failed = TRUE;
		return -1;
	}
	bytes_until_failure = fstream->failure_offset - stream->ostream.offset;

	iov_dup = i_new(struct const_iovec, iov_count);
	iov_dup_count = iov_count;
	for (i = 0; i < iov_count; i++) {
		iov_dup[i] = iov[i];
		if (iov_dup[i].iov_len >= bytes_until_failure) {
			iov_dup[i].iov_len = bytes_until_failure;
			iov_dup_count = i+1;
			break;
		}
	}
	ret = o_stream_sendv(stream->parent, iov_dup, iov_dup_count);
	i_free(iov_dup);

	if (ret < 0) {
		o_stream_copy_error_from_parent(stream);
		return -1;
	}
	stream->ostream.offset += ret;
	return ret;
}
static ssize_t
i_stream_failure_at_read(struct istream_private *stream)
{
	struct failure_at_istream *fstream = (struct failure_at_istream *)stream;
	uoff_t new_offset;
	ssize_t ret;

	i_stream_seek(stream->parent, stream->parent_start_offset +
		      stream->istream.v_offset);

	ret = i_stream_read_copy_from_parent(&stream->istream);
	new_offset = stream->istream.v_offset + (stream->pos - stream->skip);
	if (ret >= 0 && new_offset >= fstream->failure_offset) {
		if (stream->istream.v_offset >= fstream->failure_offset) {
			/* we already passed the wanted failure offset,
			   return error immediately. */
			stream->pos = stream->skip;
			stream->istream.stream_errno = errno = EIO;
			io_stream_set_error(&stream->iostream, "%s",
					    fstream->error_string);
			ret = -1;
		} else {
			/* return data up to the wanted failure offset and
			   on the next read() call return failure */
			size_t new_pos = fstream->failure_offset -
				stream->istream.v_offset + stream->skip;
			i_assert(new_pos >= stream->skip &&
				 stream->pos >= new_pos);
			ret -= stream->pos - new_pos;
			stream->pos = new_pos;
		}
	} else if (ret < 0 && stream->istream.stream_errno == 0 &&
		   fstream->failure_offset == (uoff_t)-1) {
		/* failure at EOF */
		stream->istream.stream_errno = errno = EIO;
		io_stream_set_error(&stream->iostream, "%s",
				    fstream->error_string);
	}
	return ret;
}
Example #14
0
static int i_stream_file_open(struct istream_private *stream)
{
	const char *path = i_stream_get_name(&stream->istream);

	stream->fd = open(path, O_RDONLY);
	if (stream->fd == -1) {
		io_stream_set_error(&stream->iostream,
				    "open(%s) failed: %m", path);
		stream->istream.stream_errno = errno;
		return -1;
	}
	return 0;
}
Example #15
0
static void
i_stream_hash_seek(struct istream_private *stream,
		   uoff_t v_offset, bool mark ATTR_UNUSED)
{
	struct hash_istream *hstream = (struct hash_istream *)stream;

	if (hstream->hash_context != NULL) {
		io_stream_set_error(&stream->iostream,
			"Seeking not supported before hashing is finished");
		stream->istream.stream_errno = ESPIPE;
	}
	stream->istream.v_offset = v_offset;
	stream->skip = stream->pos = 0;
}
Example #16
0
static int
o_stream_failure_at_flush(struct ostream_private *stream)
{
	struct failure_at_ostream *fstream =
		(struct failure_at_ostream *)stream;

	if (fstream->failed) {
		io_stream_set_error(&stream->iostream, "%s",
				    fstream->error_string);
		stream->ostream.stream_errno = errno = EIO;
		return -1;
	}
	return o_stream_flush(stream->parent);
}
Example #17
0
static struct istream *
i_stream_create_file_common(int fd, const char *path,
			    size_t max_buffer_size, bool autoclose_fd)
{
	struct file_istream *fstream;
	struct istream *input;
	struct stat st;
	bool is_file;

	fstream = i_new(struct file_istream, 1);
	fstream->autoclose_fd = autoclose_fd;

	fstream->istream.iostream.close = i_stream_file_close;
	fstream->istream.max_buffer_size = max_buffer_size;
	fstream->istream.read = i_stream_file_read;
	fstream->istream.seek = i_stream_file_seek;
	fstream->istream.sync = i_stream_file_sync;
	fstream->istream.stat = i_stream_file_stat;

	/* if it's a file, set the flags properly */
	if (fd == -1)
		is_file = TRUE;
	else if (fstat(fd, &st) < 0)
		is_file = FALSE;
	else if (S_ISREG(st.st_mode))
		is_file = TRUE;
	else if (!S_ISDIR(st.st_mode))
		is_file = FALSE;
	else {
		/* we're trying to open a directory.
		   we're not designed for it. */
		io_stream_set_error(&fstream->istream.iostream,
			"%s is a directory, can't read it as file",
			path != NULL ? path : t_strdup_printf("<fd %d>", fd));
		fstream->istream.istream.stream_errno = EISDIR;
		is_file = FALSE;
	}
	if (is_file) {
		fstream->file = TRUE;
		fstream->istream.istream.blocking = TRUE;
		fstream->istream.istream.seekable = TRUE;
	}
	fstream->istream.istream.readable_fd = TRUE;

	input = i_stream_create(&fstream->istream, NULL, fd);
	i_stream_set_name(input, is_file ? "(file)" : "(fd)");
	return input;
}
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;
}
Example #19
0
static
int o_stream_encrypt_send(struct encrypt_ostream *stream,
			  const unsigned char *data, size_t size)
{
	ssize_t ec;

	ec = o_stream_send(stream->ostream.parent, data, size);
	if (ec == (ssize_t)size)
		return 0;
	else if (ec < 0) {
		o_stream_copy_error_from_parent(&stream->ostream);
		return -1;
	} else {
		io_stream_set_error(&stream->ostream.iostream,
			"ostream-encrypt: Unexpectedly short write to parent stream");
		stream->ostream.ostream.stream_errno = EINVAL;
		return -1;
	}
}
static ssize_t i_stream_qp_decoder_read(struct istream_private *stream)
{
    struct qp_decoder_istream *bstream =
        (struct qp_decoder_istream *)stream;
    const unsigned char *data;
    size_t pre_count, post_count, size;
    int ret;
    size_t prev_size = 0;

    do {
        ret = i_stream_read_parent(stream, &prev_size);
        if (ret <= 0) {
            if (ret != -1 || stream->istream.stream_errno != 0)
                return 0;

            ret = i_stream_qp_try_decode_input(bstream, TRUE);
            if (ret == 0) {
                /* ended with =[whitespace] but without LF */
                stream->istream.eof = TRUE;
                return -1;
            }
            /* partial qp input */
            i_assert(ret < 0);
            data = i_stream_get_data(stream->parent, &size);
            io_stream_set_error(&stream->iostream,
                                "quoted-printable input ends with a partial block: 0x%s",
                                binary_to_hex(data, size));
            stream->istream.stream_errno = EINVAL;
            return -1;
        }

        /* encode as much data as fits into destination buffer */
        pre_count = stream->pos - stream->skip;
        while ((ret = i_stream_qp_try_decode_input(bstream, FALSE)) > 0) ;
        post_count = stream->pos - stream->skip;
    } while (ret == 0 && pre_count == post_count);

    if (ret < 0)
        return ret;

    i_assert(post_count > pre_count);
    return post_count - pre_count;
}
Example #21
0
static ssize_t
o_stream_failure_at_sendv(struct ostream_private *stream,
			  const struct const_iovec *iov, unsigned int iov_count)
{
	struct failure_at_ostream *fstream =
		(struct failure_at_ostream *)stream;
	unsigned int i;
	struct const_iovec *iov_dup;
	unsigned int iov_dup_count;
	uoff_t bytes_until_failure;
	ssize_t ret;

	if (fstream->failure_offset <= stream->ostream.offset) {
		io_stream_set_error(&stream->iostream, "%s",
				    fstream->error_string);
		stream->ostream.stream_errno = errno = EIO;
		fstream->failed = TRUE;
		return -1;
	}
	bytes_until_failure = fstream->failure_offset - stream->ostream.offset;

	iov_dup = i_new(struct const_iovec, iov_count);
	iov_dup_count = iov_count;
	for (i = 0; i < iov_count; i++) {
		iov_dup[i] = iov[i];
		if (iov_dup[i].iov_len >= bytes_until_failure) {
			iov_dup[i].iov_len = bytes_until_failure;
			iov_dup_count = i+1;
			break;
		}
	}
	ret = o_stream_sendv(stream->parent, iov_dup, iov_dup_count);
	i_free(iov_dup);

	if (ret < 0) {
		o_stream_copy_error_from_parent(stream);
		return -1;
	}
	stream->ostream.offset += ret;
	return ret;
}
Example #22
0
static ssize_t read_more(struct seekable_istream *sstream)
{
	size_t size;
	ssize_t ret;

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

	while ((ret = i_stream_read_memarea(sstream->cur_input)) == -1) {
		if (sstream->cur_input->stream_errno != 0) {
			io_stream_set_error(&sstream->istream.iostream,
				"read(%s) failed: %s",
				i_stream_get_name(sstream->cur_input),
				i_stream_get_error(sstream->cur_input));
			sstream->istream.istream.eof = TRUE;
			sstream->istream.istream.stream_errno =
				sstream->cur_input->stream_errno;
			return -1;
		}

		/* go to next stream */
		sstream->cur_input = sstream->input[sstream->cur_idx++];
		if (sstream->cur_input == NULL) {
			/* last one, EOF */
			sstream->size = sstream->istream.istream.v_offset;
			sstream->istream.istream.eof = TRUE;
			unref_streams(sstream);
			return -1;
		}

		/* see if stream has pending data */
		size = i_stream_get_data_size(sstream->cur_input);
		if (size != 0)
			return size;
	}
	return ret;
}
Example #23
0
struct istream *
i_stream_create_sym_decrypt(struct istream *input, struct dcrypt_context_symmetric *ctx)
{
	const char *error;
	int ec;
	struct decrypt_istream *dstream;
	dstream = i_stream_create_decrypt_common(input);
	dstream->use_mac = FALSE;
	dstream->initialized = TRUE;

	if (!dcrypt_ctx_sym_init(ctx, &error)) ec = -1;
	else ec = 0;

	dstream->ctx_sym = ctx;

	if (ec != 0) {
		io_stream_set_error(&dstream->istream.iostream, "Cannot initialize decryption: %s", error);
		dstream->istream.istream.stream_errno = EIO;
	};

	return &dstream->istream.istream;
}
Example #24
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;
}
Example #25
0
static bool o_stream_temp_dup_cancel(struct temp_ostream *tstream,
				     enum ostream_send_istream_result *res_r)
{
	struct istream *input;
	uoff_t size = tstream->dupstream_offset -
		tstream->dupstream_start_offset;
	bool ret = TRUE; /* use res_r to return error */

	i_stream_seek(tstream->dupstream, tstream->dupstream_start_offset);
	tstream->ostream.ostream.offset = 0;

	input = i_stream_create_limit(tstream->dupstream, size);
	i_stream_unref(&tstream->dupstream);

	*res_r = io_stream_copy(&tstream->ostream.ostream, input);
	switch (*res_r) {
	case OSTREAM_SEND_ISTREAM_RESULT_FINISHED:
		/* everything copied */
		ret = FALSE;
		break;
	case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT:
	case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT:
		i_unreached();
	case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT:
		tstream->ostream.ostream.stream_errno = input->stream_errno;
		io_stream_set_error(&tstream->ostream.iostream,
			"iostream-temp: read(%s) failed: %s",
			i_stream_get_name(input),
			i_stream_get_error(input));
		break;
	case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT:
		break;
	}
	i_stream_destroy(&input);
	return ret;
}
static int astream_read_next(struct attachment_istream *astream, bool *retry_r)
{
	struct istream_private *stream = &astream->istream;
	struct message_block block;
	size_t old_size, new_size;
	const char *error;
	int ret;

	*retry_r = FALSE;

	if (stream->pos - stream->skip >= i_stream_get_max_buffer_size(&stream->istream))
		return -2;

	if (astream->failed) {
		stream->istream.stream_errno = EINVAL;
		return -1;
	}

	old_size = stream->pos - stream->skip;
	switch (message_parser_parse_next_block(astream->parser, &block)) {
	case -1:
		/* done / error */
		ret = astream_end_of_part(astream, &error);
		if (ret > 0) {
			/* final data */
			new_size = stream->pos - stream->skip;
			return new_size - old_size;
		}
		stream->istream.eof = TRUE;
		stream->istream.stream_errno = stream->parent->stream_errno;

		if (ret < 0) {
			io_stream_set_error(&stream->iostream, "%s", error);
			stream->istream.stream_errno = EINVAL;
			astream->failed = TRUE;
		}
		astream->cur_part = NULL;
		return -1;
	case 0:
		/* need more data */
		return 0;
	default:
		break;
	}

	if (block.part != astream->cur_part && astream->cur_part != NULL) {
		/* end of a MIME part */
		if (astream_end_of_part(astream, &error) < 0) {
			io_stream_set_error(&stream->iostream, "%s", error);
			stream->istream.stream_errno = EINVAL;
			astream->failed = TRUE;
			return -1;
		}
	}
	astream->cur_part = block.part;

	if (block.hdr != NULL) {
		/* parsing a header */
		astream_parse_header(astream, block.hdr);
	} else if (block.size == 0) {
		/* end of headers */
		if (astream_want_attachment(astream, block.part)) {
			astream->part.state = MAIL_ATTACHMENT_STATE_MAYBE;
			astream->part.start_offset = stream->parent->v_offset;
		}
	} else {
		astream_add_body(astream, &block);
	}
	new_size = stream->pos - stream->skip;
	*retry_r = new_size == old_size;
	return new_size - old_size;
}
Example #27
0
void i_stream_callback_set_error(struct istream *input, int stream_errno,
				 const char *error)
{
	input->stream_errno = stream_errno;
	io_stream_set_error(&input->real_stream->iostream, "%s", error);
}
Example #28
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;
}
Example #29
0
static ssize_t
i_stream_decrypt_read(struct istream_private *stream)
{
	struct decrypt_istream *dstream =
		(struct decrypt_istream *)stream;
	const unsigned char *data;
	size_t size, decrypt_size;
	const char *error = NULL;
	int ret;
	bool check_mac = FALSE;

	/* not if it's broken */
	if (stream->istream.stream_errno != 0)
		return -1;

	for (;;) {
		/* remove skipped data from buffer */
		if (stream->skip > 0) {
			i_assert(stream->skip <= dstream->buf->used);
			buffer_delete(dstream->buf, 0, stream->skip);
			stream->pos -= stream->skip;
			stream->skip = 0;
		}

		stream->buffer = dstream->buf->data;

		i_assert(stream->pos <= dstream->buf->used);
		if (stream->pos >= dstream->istream.max_buffer_size) {
			/* stream buffer still at maximum */
			return -2;
		}

		/* if something is already decrypted, return as much of it as
		   we can */
		if (dstream->initialized && dstream->buf->used > 0) {
			size_t new_pos, bytes;

			/* only return up to max_buffer_size bytes, even when buffer
			   actually has more, as not to confuse the caller */
			if (dstream->buf->used <= dstream->istream.max_buffer_size) {
				new_pos = dstream->buf->used;
				if (dstream->finalized)
					stream->istream.eof = TRUE;
			} else {
				new_pos = dstream->istream.max_buffer_size;
			}

			bytes = new_pos - stream->pos;
			stream->pos = new_pos;
			return (ssize_t)bytes;
		}
		if (dstream->finalized) {
			/* all data decrypted */
			stream->istream.eof = TRUE;
			return -1;
		}
		/* need to read more input */
		ret = i_stream_read(stream->parent);
		if (ret == 0 || ret == -2)
			return ret;
		data = i_stream_get_data(stream->parent, &size);

		if (ret == -1 && (size == 0 || stream->parent->stream_errno != 0)) {
			stream->istream.stream_errno = stream->parent->stream_errno;

			/* file was empty */
			if (!dstream->initialized && size == 0 && stream->parent->eof) {
				stream->istream.eof = TRUE;
				return -1;
			}

			if (stream->istream.stream_errno != 0)
				return -1;

			if (!dstream->initialized) {
				io_stream_set_error(&stream->iostream,
					"Decryption error: %s",
					"Input truncated in decryption header");
				stream->istream.stream_errno = EINVAL;
				return -1;
			}

			/* final block */
			if (dcrypt_ctx_sym_final(dstream->ctx_sym,
				dstream->buf, &error)) {
				dstream->finalized = TRUE;
				continue;
			}
			io_stream_set_error(&stream->iostream,
				"MAC error: %s", error);
			stream->istream.stream_errno = EINVAL;
			return -1;
		}

		if (!dstream->initialized) {
			ssize_t hret;

			if ((hret=i_stream_decrypt_read_header(dstream, data, size)) <= 0) {
				if (hret < 0) {
					if (stream->istream.stream_errno == 0)
						/* assume temporary failure */
						stream->istream.stream_errno = EIO;
					return -1;
				}

				if (hret == 0 && stream->parent->eof) {
					/* not encrypted by us */
					stream->istream.stream_errno = EINVAL;
					io_stream_set_error(&stream->iostream,
						"Truncated header");
					return -1;
				}
			}

			if (hret == 0) {
				/* see if we can get more data */
				continue;
			} else {
				/* clean up buffer */
				safe_memset(buffer_get_modifiable_data(dstream->buf, 0), 0, dstream->buf->used);
				buffer_set_used_size(dstream->buf, 0);
				i_stream_skip(stream->parent, hret);
			}

			data = i_stream_get_data(stream->parent, &size);
		}
		decrypt_size = size;

		if (dstream->use_mac) {
			if (stream->parent->eof) {
				if (decrypt_size < dstream->ftr) {
					io_stream_set_error(&stream->iostream,
						"Decryption error: footer is longer than data");
					stream->istream.stream_errno = EINVAL;
					return -1;
				}
				check_mac = TRUE;
			} else {
				/* ignore footer's length of data until we
				   reach EOF */
				size -= dstream->ftr;
			}
			decrypt_size -= dstream->ftr;
			if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) {
				if (!dcrypt_ctx_hmac_update(dstream->ctx_mac,
				    data, decrypt_size, &error)) {
					io_stream_set_error(&stream->iostream,
						"MAC error: %s", error);
					stream->istream.stream_errno = EINVAL;
					return -1;
				}
			}
		}

		if (check_mac) {
			if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == IO_STREAM_ENC_INTEGRITY_HMAC) {
				unsigned char dgst[dcrypt_ctx_hmac_get_digest_length(dstream->ctx_mac)];
				buffer_t db;
				buffer_create_from_data(&db, dgst, sizeof(dgst));
				if (!dcrypt_ctx_hmac_final(dstream->ctx_mac, &db, &error)) {
					io_stream_set_error(&stream->iostream,
						"Cannot verify MAC: %s", error);
					stream->istream.stream_errno = EINVAL;
					return -1;
				}
				if (memcmp(dgst, data + decrypt_size, dcrypt_ctx_hmac_get_digest_length(dstream->ctx_mac)) != 0) {
					io_stream_set_error(&stream->iostream,
						"Cannot verify MAC: mismatch");
					stream->istream.stream_errno = EINVAL;
					return -1;
				}
			} else if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == IO_STREAM_ENC_INTEGRITY_AEAD) {
				dcrypt_ctx_sym_set_tag(dstream->ctx_sym, data + decrypt_size, dstream->ftr);
			}
		}

		if (!dcrypt_ctx_sym_update(dstream->ctx_sym,
		    data, decrypt_size, dstream->buf, &error)) {
			io_stream_set_error(&stream->iostream,
				"Decryption error: %s", error);
			stream->istream.stream_errno = EINVAL;
			return -1;
		}
		i_stream_skip(stream->parent, size);
	}
}
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;
}