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