bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen#include "lib.h"
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen#include "istream-private.h"
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen#include "istream-try.h"
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainenstruct try_istream {
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen struct istream_private istream;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen unsigned int try_input_count;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen struct istream **try_input;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen unsigned int try_idx;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen struct istream *final_input;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen};
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainenstatic void i_stream_unref_try_inputs(struct try_istream *tstream)
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen{
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen for (unsigned int i = 0; i < tstream->try_input_count; i++) {
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen if (tstream->try_input[i] != NULL)
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen i_stream_unref(&tstream->try_input[i]);
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen }
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen tstream->try_input_count = 0;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen i_free(tstream->try_input);
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen}
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainenstatic void i_stream_try_close(struct iostream_private *stream,
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen bool close_parent)
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen{
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen struct try_istream *tstream = (struct try_istream *)stream;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen if (close_parent) {
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen if (tstream->istream.parent != NULL)
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen i_stream_close(tstream->istream.parent);
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen for (unsigned int i = 0; i < tstream->try_input_count; i++) {
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen if (tstream->try_input[i] != NULL)
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen i_stream_close(tstream->try_input[i]);
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen }
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen }
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen i_stream_unref_try_inputs(tstream);
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen}
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainenstatic bool i_stream_try_is_buffer_full(struct istream *try_input)
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen{
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen /* See if one of the parent istreams have their buffer full.
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen This is mainly intended to check with istream-tee whether its
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen parent is full. That means that the try_input has already seen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen a full buffer of input, but it hasn't decided to return anything
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen yet. But it also hasn't failed, so we'll assume that the input is
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen correct for it and it simply needs a lot more input before it can
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen return anything (e.g. istream-bzlib). */
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen while (try_input->real_stream->parent != NULL) {
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen try_input = try_input->real_stream->parent;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen if (try_input->real_stream->pos == try_input->real_stream->buffer_size &&
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen try_input->real_stream->buffer_size > 0)
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen return TRUE;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen }
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen return FALSE;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen}
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainenstatic int i_stream_try_detect(struct try_istream *tstream)
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen{
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen int ret;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen for (; tstream->try_idx < tstream->try_input_count; tstream->try_idx++) {
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen struct istream *try_input =
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen tstream->try_input[tstream->try_idx];
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen ret = i_stream_read(try_input);
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen if (ret == 0 && i_stream_try_is_buffer_full(try_input))
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen ret = 1;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen if (ret > 0) {
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen i_stream_init_parent(&tstream->istream, try_input);
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen i_stream_unref_try_inputs(tstream);
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen return 1;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen }
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen if (ret == 0)
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen return 0;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen if (try_input->stream_errno != EINVAL) {
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen tstream->istream.istream.stream_errno =
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen try_input->stream_errno;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen io_stream_set_error(&tstream->istream.iostream,
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen "Unexpected error while detecting stream format: %s",
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen i_stream_get_error(try_input));
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen return -1;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen }
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen }
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen /* All streams failed with EINVAL. */
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen io_stream_set_error(&tstream->istream.iostream,
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen "Failed to detect stream format");
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen tstream->istream.istream.stream_errno = EINVAL;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen return -1;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen}
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainenstatic ssize_t
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Siraineni_stream_try_read(struct istream_private *stream)
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen{
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen struct try_istream *tstream = (struct try_istream *)stream;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen int ret;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen if (stream->parent == NULL) {
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen if ((ret = i_stream_try_detect(tstream)) <= 0)
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen return ret;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen }
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen i_stream_seek(stream->parent, stream->parent_start_offset +
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen stream->istream.v_offset);
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen return i_stream_read_copy_from_parent(&stream->istream);
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen}
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainenstruct istream *istream_try_create(struct istream *const input[])
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen{
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen struct try_istream *tstream;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen unsigned int count;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen size_t max_buffer_size = I_STREAM_MIN_SIZE;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen bool blocking = TRUE, seekable = TRUE;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen for (count = 0; input[count] != NULL; count++) {
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen max_buffer_size = I_MAX(max_buffer_size,
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen i_stream_get_max_buffer_size(input[count]));
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen if (!input[count]->blocking)
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen blocking = FALSE;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen if (!input[count]->seekable)
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen seekable = FALSE;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen i_stream_ref(input[count]);
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen }
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen i_assert(count != 0);
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen tstream = i_new(struct try_istream, 1);
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen tstream->try_input_count = count;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen tstream->try_input = p_memdup(default_pool, input,
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen sizeof(*input) * count);
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen tstream->istream.iostream.close = i_stream_try_close;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen tstream->istream.max_buffer_size = max_buffer_size;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen tstream->istream.read = i_stream_try_read;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen tstream->istream.istream.readable_fd = FALSE;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen tstream->istream.istream.blocking = blocking;
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen tstream->istream.istream.seekable = seekable;
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen return i_stream_create(&tstream->istream, NULL, -1, 0);
e9fe799e27b5a3b67c256766d4bafd388e560684Timo Sirainen}