istream-chain.c revision 7be8ba0c0462887826e5ee6da6a27964d30383b5
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/* Copyright (c) 2003-2016 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "lib.h"
16f816d3f3c32ae3351834253f52ddd0212bcbf3Timo Sirainen#include "llist.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "istream-private.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "istream-chain.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct chain_istream;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainenstruct istream_chain_link {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct istream_chain_link *prev, *next;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen struct istream *stream;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen bool eof;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen};
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainenstruct istream_chain {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen struct istream_chain_link *head, *tail;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen struct chain_istream *stream;
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen};
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainenstruct chain_istream {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen struct istream_private istream;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen size_t prev_stream_left, prev_skip;
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen bool have_explicit_max_buffer_size;
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen struct istream_chain chain;
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen};
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainenstatic void ATTR_NULL(2)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Siraineni_stream_chain_append_internal(struct istream_chain *chain,
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen struct istream *stream)
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct istream_chain_link *link;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (stream == NULL && chain->tail != NULL && chain->tail->stream == NULL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen link = i_new(struct istream_chain_link, 1);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen link->stream = stream;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen link->eof = stream == NULL;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (stream != NULL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_ref(stream);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen if (chain->head == NULL && stream != NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct chain_istream *cstream = (struct chain_istream *)chain->stream;
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen if (cstream->have_explicit_max_buffer_size) {
ea546eaab672d441e180b7619d4750be813c08d8Timo Sirainen i_stream_set_max_buffer_size(stream,
ea546eaab672d441e180b7619d4750be813c08d8Timo Sirainen chain->stream->istream.max_buffer_size);
ea546eaab672d441e180b7619d4750be813c08d8Timo Sirainen } else {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen size_t max_size = i_stream_get_max_buffer_size(stream);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (cstream->istream.max_buffer_size < max_size)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen cstream->istream.max_buffer_size = max_size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen DLLIST2_APPEND(&chain->head, &chain->tail, link);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* if io_add_istream() has been added to this chain stream, notify
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen the callback that we have more data available. */
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen if (stream != NULL)
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen i_stream_set_input_pending(stream, TRUE);
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen}
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainenvoid i_stream_chain_append(struct istream_chain *chain, struct istream *stream)
31ddc75584c5cde53d2e78a737587f2e7fdcb0d2Timo Sirainen{
5c1a8aee989af87bddefd71e2aa83aa2bd695155Timo Sirainen i_stream_chain_append_internal(chain, stream);
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen}
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainenvoid i_stream_chain_append_eof(struct istream_chain *chain)
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen{
0add8c99ca65e56dbf613595fc37c41aafff3f7fTimo Sirainen i_stream_chain_append_internal(chain, NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
ec1e30ecc38f0deddaf655413cf02d5972ddbc70Timo Sirainenstatic void
ec1e30ecc38f0deddaf655413cf02d5972ddbc70Timo Siraineni_stream_chain_set_max_buffer_size(struct iostream_private *stream,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen size_t max_size)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct chain_istream *cstream = (struct chain_istream *)stream;
f99575e1d6cd251bd7b6d0654bd75b475e6a894cTimo Sirainen struct istream_chain_link *link = cstream->chain.head;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen cstream->have_explicit_max_buffer_size = TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen cstream->istream.max_buffer_size = max_size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen while (link != NULL) {
f99575e1d6cd251bd7b6d0654bd75b475e6a894cTimo Sirainen if (link->stream != NULL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_set_max_buffer_size(link->stream, max_size);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen link = link->next;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void i_stream_chain_destroy(struct iostream_private *stream)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct chain_istream *cstream = (struct chain_istream *)stream;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct istream_chain_link *link = cstream->chain.head;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen while (link != NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct istream_chain_link *next = link->next;
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen
93b29720c5141f787bd1861796867e4595c9d084Timo Sirainen if (link->stream != NULL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_unref(&link->stream);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_free(link);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen link = next;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_free(cstream->istream.w_buffer);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void i_stream_chain_read_next(struct chain_istream *cstream)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct istream_chain_link *link = cstream->chain.head;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct istream *prev_input;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const unsigned char *data;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen size_t data_size, cur_data_pos;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(link != NULL && link->stream != NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(link->stream->eof);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen prev_input = link->stream;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen data = i_stream_get_data(prev_input, &data_size);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen DLLIST2_REMOVE(&cstream->chain.head, &cstream->chain.tail, link);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen i_free(link);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen /* a) we have more streams, b) we have EOF, c) we need to wait
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen for more streams */
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen link = cstream->chain.head;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen if (link != NULL && link->stream != NULL)
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen i_stream_seek(link->stream, 0);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen if (cstream->prev_stream_left > 0) {
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen /* we've already buffered some of the prev_input. continue
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen appending the rest to it. */
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen cur_data_pos = cstream->istream.pos -
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen (cstream->istream.skip + cstream->prev_stream_left);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen i_assert(cur_data_pos <= data_size);
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen data += cur_data_pos;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen data_size -= cur_data_pos;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen } else {
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen cstream->istream.pos = 0;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen cstream->istream.skip = 0;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen cstream->prev_stream_left = 0;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen }
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen if (data_size > 0) {
bef8712387812fc5d9496b9958935c6d0c418777Timo Sirainen memcpy(i_stream_alloc(&cstream->istream, data_size),
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen data, data_size);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen cstream->istream.pos += data_size;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen cstream->prev_stream_left += data_size;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen }
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
bef8712387812fc5d9496b9958935c6d0c418777Timo Sirainen i_stream_skip(prev_input, i_stream_get_data_size(prev_input));
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen i_stream_unref(&prev_input);
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen}
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainenstatic bool i_stream_chain_skip(struct chain_istream *cstream)
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen{
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen struct istream_private *stream = &cstream->istream;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen struct istream_chain_link *link = cstream->chain.head;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen size_t bytes_skipped;
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen i_assert(stream->skip >= cstream->prev_skip);
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen bytes_skipped = stream->skip - cstream->prev_skip;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen if (cstream->prev_stream_left == 0) {
b20fb5b1df9d604a7541f5118fc5b4b466d211efTimo Sirainen /* no need to worry about buffers, skip everything */
b20fb5b1df9d604a7541f5118fc5b4b466d211efTimo Sirainen } else if (bytes_skipped < cstream->prev_stream_left) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we're still skipping inside buffer */
b20fb5b1df9d604a7541f5118fc5b4b466d211efTimo Sirainen cstream->prev_stream_left -= bytes_skipped;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen bytes_skipped = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen } else {
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen /* done with the buffer */
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen bytes_skipped -= cstream->prev_stream_left;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen cstream->prev_stream_left = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen stream->pos -= bytes_skipped;
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen stream->skip -= bytes_skipped;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen stream->buffer += bytes_skipped;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen cstream->prev_skip = stream->skip;
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen if (link == NULL || link->eof) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(bytes_skipped == 0);
f99575e1d6cd251bd7b6d0654bd75b475e6a894cTimo Sirainen return FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_skip(link->stream, bytes_skipped);
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen return TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic ssize_t i_stream_chain_read(struct istream_private *stream)
b5ea11802f2bafbec06282a7b3b6704dc5fae584Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct chain_istream *cstream = (struct chain_istream *)stream;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct istream_chain_link *link = cstream->chain.head;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const unsigned char *data;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen size_t data_size, cur_data_pos, new_pos;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen size_t new_bytes_count;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ssize_t ret;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen if (link != NULL && link->eof) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen stream->istream.eof = TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen if (!i_stream_chain_skip(cstream))
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen return 0;
5529671faac3c5672a948be93091056736c7afffTimo Sirainen i_assert(link != NULL);
5529671faac3c5672a948be93091056736c7afffTimo Sirainen
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen i_assert(stream->pos >= stream->skip + cstream->prev_stream_left);
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen cur_data_pos = stream->pos - (stream->skip + cstream->prev_stream_left);
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen data = i_stream_get_data(link->stream, &data_size);
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen if (data_size > cur_data_pos)
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen ret = 0;
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen else {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* need to read more */
024815ea2ffdda9ea79919f18e865663977f73eaTimo Sirainen i_assert(cur_data_pos == data_size);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = i_stream_read(link->stream);
b5ea11802f2bafbec06282a7b3b6704dc5fae584Timo Sirainen if (ret == -2 || ret == 0)
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen return ret;
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen
b5ea11802f2bafbec06282a7b3b6704dc5fae584Timo Sirainen if (ret == -1) {
b5ea11802f2bafbec06282a7b3b6704dc5fae584Timo Sirainen if (link->stream->stream_errno != 0) {
b5ea11802f2bafbec06282a7b3b6704dc5fae584Timo Sirainen io_stream_set_error(&stream->iostream,
7c5b51bdf43a98e12c654ad437e0b258c5fffbc1Timo Sirainen "read(%s) failed: %s",
b5ea11802f2bafbec06282a7b3b6704dc5fae584Timo Sirainen i_stream_get_name(link->stream),
b5ea11802f2bafbec06282a7b3b6704dc5fae584Timo Sirainen i_stream_get_error(link->stream));
b5ea11802f2bafbec06282a7b3b6704dc5fae584Timo Sirainen stream->istream.stream_errno =
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen link->stream->stream_errno;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen }
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen /* EOF of this stream, go to next stream */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_chain_read_next(cstream);
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen cstream->prev_skip = stream->skip;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return i_stream_chain_read(stream);
f99575e1d6cd251bd7b6d0654bd75b475e6a894cTimo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we read something */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen data = i_stream_get_data(link->stream, &data_size);
7e94cf9d70ce9fdeccb7a85ff400b899e6386f36Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (cstream->prev_stream_left == 0) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* we can point directly to the current stream's buffers */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen stream->buffer = data;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen stream->pos -= stream->skip;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen stream->skip = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen new_pos = data_size;
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen } else if (data_size == cur_data_pos) {
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen /* nothing new read */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(ret == 0 || ret == -1);
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen stream->buffer = stream->w_buffer;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen new_pos = stream->pos;
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen } else {
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen /* we still have some of the previous stream left. merge the
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen new data with it. */
62950eeff28f00989a17b20eeade3af7e200c6bcTimo Sirainen i_assert(data_size > cur_data_pos);
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen new_bytes_count = data_size - cur_data_pos;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen memcpy(i_stream_alloc(stream, new_bytes_count),
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen data + cur_data_pos, new_bytes_count);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen stream->buffer = stream->w_buffer;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen new_pos = stream->pos + new_bytes_count;
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen }
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ret = new_pos > stream->pos ? (ssize_t)(new_pos - stream->pos) :
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen (ret == 0 ? 0 : -1);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen stream->pos = new_pos;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen cstream->prev_skip = stream->skip;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainenstatic void i_stream_chain_close(struct iostream_private *stream,
62950eeff28f00989a17b20eeade3af7e200c6bcTimo Sirainen bool close_parent)
62950eeff28f00989a17b20eeade3af7e200c6bcTimo Sirainen{
62950eeff28f00989a17b20eeade3af7e200c6bcTimo Sirainen struct chain_istream *cstream = (struct chain_istream *)stream;
62950eeff28f00989a17b20eeade3af7e200c6bcTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* seek to the correct position in parent stream in case it didn't
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen end with EOF */
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen (void)i_stream_chain_skip(cstream);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (close_parent) {
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen struct istream_chain_link *link = cstream->chain.head;
df4018ae2f0a95be602f724ca70df7e0e3bd6a7dTimo Sirainen while (link != NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_close(link->stream);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen link = link->next;
8e7da21696c9f8a6d5e601243fb6172ec85d47b2Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstruct istream *i_stream_create_chain(struct istream_chain **chain_r)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct chain_istream *cstream;
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen cstream = i_new(struct chain_istream, 1);
24fc71a693331ffe77e2b6d81c70aca6fa055e47Timo Sirainen cstream->chain.stream = cstream;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen cstream->istream.iostream.close = i_stream_chain_close;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen cstream->istream.iostream.destroy = i_stream_chain_destroy;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen cstream->istream.iostream.set_max_buffer_size =
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_stream_chain_set_max_buffer_size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
f99575e1d6cd251bd7b6d0654bd75b475e6a894cTimo Sirainen cstream->istream.read = i_stream_chain_read;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen cstream->istream.istream.readable_fd = FALSE;
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen cstream->istream.istream.blocking = FALSE;
1b3bb8d39686ed24730cbc31cc9a33dc62c8c6c3Timo Sirainen cstream->istream.istream.seekable = FALSE;
b0a446671b8f09a1d2ed1d8c86a47298309e989dTimo Sirainen
b0a446671b8f09a1d2ed1d8c86a47298309e989dTimo Sirainen *chain_r = &cstream->chain;
b0a446671b8f09a1d2ed1d8c86a47298309e989dTimo Sirainen return i_stream_create(&cstream->istream, NULL, -1);
b0a446671b8f09a1d2ed1d8c86a47298309e989dTimo Sirainen}
b0a446671b8f09a1d2ed1d8c86a47298309e989dTimo Sirainen