bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch#include "lib.h"
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch#include "llist.h"
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch#include "istream-private.h"
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch#include "istream-chain.h"
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschstruct chain_istream;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschstruct istream_chain_link {
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct istream_chain_link *prev, *next;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct istream *stream;
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen bool eof;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch};
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschstruct istream_chain {
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct istream_chain_link *head, *tail;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct chain_istream *stream;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch};
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschstruct chain_istream {
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct istream_private istream;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen /* how much of the previous link's stream still exists at the
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen beginning of our buffer. skipping through this should point to
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen the beginning of the current link's stream. */
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen size_t prev_stream_left;
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen size_t prev_skip;
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen bool have_explicit_max_buffer_size;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct istream_chain chain;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch};
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainenstatic void ATTR_NULL(2)
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Siraineni_stream_chain_append_internal(struct istream_chain *chain,
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen struct istream *stream)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch{
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct istream_chain_link *link;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch if (stream == NULL && chain->tail != NULL && chain->tail->stream == NULL)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch return;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch link = i_new(struct istream_chain_link, 1);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch link->stream = stream;
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen link->eof = stream == NULL;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen if (stream != NULL)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_stream_ref(stream);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch if (chain->head == NULL && stream != NULL) {
597db47e06df5ac9dbf80c336383b921fec9d373Timo Sirainen struct chain_istream *cstream = (struct chain_istream *)chain->stream;
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen if (cstream->have_explicit_max_buffer_size) {
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_stream_set_max_buffer_size(stream,
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch chain->stream->istream.max_buffer_size);
7be8ba0c0462887826e5ee6da6a27964d30383b5Timo Sirainen } else {
7be8ba0c0462887826e5ee6da6a27964d30383b5Timo Sirainen size_t max_size = i_stream_get_max_buffer_size(stream);
7be8ba0c0462887826e5ee6da6a27964d30383b5Timo Sirainen
7be8ba0c0462887826e5ee6da6a27964d30383b5Timo Sirainen if (cstream->istream.max_buffer_size < max_size)
7be8ba0c0462887826e5ee6da6a27964d30383b5Timo Sirainen cstream->istream.max_buffer_size = max_size;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch }
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch }
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch DLLIST2_APPEND(&chain->head, &chain->tail, link);
d295cff1b640240cd198b9c8e963c9116ab95510Timo Sirainen /* if io_add_istream() has been added to this chain stream, notify
d295cff1b640240cd198b9c8e963c9116ab95510Timo Sirainen the callback that we have more data available. */
935a2434cd674e9cd17eabb296f84d1d891a23aeTimo Sirainen if (stream != NULL)
935a2434cd674e9cd17eabb296f84d1d891a23aeTimo Sirainen i_stream_set_input_pending(stream, TRUE);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch}
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainenvoid i_stream_chain_append(struct istream_chain *chain, struct istream *stream)
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen{
9a5980c7bb836f69a63082f4699c30596ea4ee74Timo Sirainen i_stream_chain_append_internal(chain, stream);
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen}
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainenvoid i_stream_chain_append_eof(struct istream_chain *chain)
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen{
9a5980c7bb836f69a63082f4699c30596ea4ee74Timo Sirainen i_stream_chain_append_internal(chain, NULL);
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen}
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschstatic void
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschi_stream_chain_set_max_buffer_size(struct iostream_private *stream,
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch size_t max_size)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch{
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct chain_istream *cstream = (struct chain_istream *)stream;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct istream_chain_link *link = cstream->chain.head;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen cstream->have_explicit_max_buffer_size = TRUE;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch cstream->istream.max_buffer_size = max_size;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch while (link != NULL) {
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch if (link->stream != NULL)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_stream_set_max_buffer_size(link->stream, max_size);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch link = link->next;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch }
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch}
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschstatic void i_stream_chain_destroy(struct iostream_private *stream)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch{
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct chain_istream *cstream = (struct chain_istream *)stream;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct istream_chain_link *link = cstream->chain.head;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch while (link != NULL) {
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct istream_chain_link *next = link->next;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
204ee6ed414f5e4eeb6f6c10763b55daf56f11acJosef 'Jeff' Sipek i_stream_unref(&link->stream);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_free(link);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch link = next;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch }
54bd0fec0be357266e299466a582f3c9269884e9Timo Sirainen i_stream_free_buffer(&cstream->istream);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch}
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschstatic void i_stream_chain_read_next(struct chain_istream *cstream)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch{
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct istream_chain_link *link = cstream->chain.head;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct istream *prev_input;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch const unsigned char *data;
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen size_t data_size, cur_data_pos;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_assert(link != NULL && link->stream != NULL);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_assert(link->stream->eof);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch prev_input = link->stream;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch data = i_stream_get_data(prev_input, &data_size);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch DLLIST2_REMOVE(&cstream->chain.head, &cstream->chain.tail, link);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_free(link);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen /* a) we have more streams, b) we have EOF, c) we need to wait
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen for more streams */
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch link = cstream->chain.head;
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen if (link != NULL && link->stream != NULL)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_stream_seek(link->stream, 0);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
1a3254b83ba00315cfc47d3c6e99e837914594cfTimo Sirainen if (cstream->prev_stream_left > 0) {
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen /* we've already buffered some of the prev_input. continue
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen appending the rest to it. if it's already at EOF, there's
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen nothing more to append. */
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen cur_data_pos = cstream->istream.pos -
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen (cstream->istream.skip + cstream->prev_stream_left);
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen i_assert(cur_data_pos <= data_size);
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen data += cur_data_pos;
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen data_size -= cur_data_pos;
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen /* the stream has now become "previous", so its contents in
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen buffer are now part of prev_stream_left. */
bac6427712674cd4b6146ef51f90c14eb9603db6Timo Sirainen cstream->prev_stream_left += cur_data_pos;
d730192e34fbedbc590a5abc7351e5af5e120c5fTimo Sirainen } else {
d730192e34fbedbc590a5abc7351e5af5e120c5fTimo Sirainen cstream->istream.pos = 0;
d730192e34fbedbc590a5abc7351e5af5e120c5fTimo Sirainen cstream->istream.skip = 0;
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen cstream->prev_stream_left = 0;
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen }
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen if (data_size > 0) {
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen memcpy(i_stream_alloc(&cstream->istream, data_size),
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen data, data_size);
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen cstream->istream.pos += data_size;
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen cstream->prev_stream_left += data_size;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch }
76f0cc074ea79151e968078906224d8b6a5806fdTimo Sirainen
1a3254b83ba00315cfc47d3c6e99e837914594cfTimo Sirainen i_stream_skip(prev_input, i_stream_get_data_size(prev_input));
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_stream_unref(&prev_input);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch}
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
3ab672903a7ed98263b89180261079870c964831Timo Sirainenstatic bool i_stream_chain_skip(struct chain_istream *cstream)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch{
3ab672903a7ed98263b89180261079870c964831Timo Sirainen struct istream_private *stream = &cstream->istream;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct istream_chain_link *link = cstream->chain.head;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen size_t bytes_skipped;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_assert(stream->skip >= cstream->prev_skip);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch bytes_skipped = stream->skip - cstream->prev_skip;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch if (cstream->prev_stream_left == 0) {
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch /* no need to worry about buffers, skip everything */
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch } else if (bytes_skipped < cstream->prev_stream_left) {
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch /* we're still skipping inside buffer */
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch cstream->prev_stream_left -= bytes_skipped;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch bytes_skipped = 0;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch } else {
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch /* done with the buffer */
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch bytes_skipped -= cstream->prev_stream_left;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch cstream->prev_stream_left = 0;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch }
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch stream->pos -= bytes_skipped;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch stream->skip -= bytes_skipped;
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen stream->buffer += bytes_skipped;
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen cstream->prev_skip = stream->skip;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen if (link == NULL || link->eof) {
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_assert(bytes_skipped == 0);
3ab672903a7ed98263b89180261079870c964831Timo Sirainen return FALSE;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch }
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_stream_skip(link->stream, bytes_skipped);
3ab672903a7ed98263b89180261079870c964831Timo Sirainen return TRUE;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen}
3ab672903a7ed98263b89180261079870c964831Timo Sirainen
3ab672903a7ed98263b89180261079870c964831Timo Sirainenstatic ssize_t i_stream_chain_read(struct istream_private *stream)
3ab672903a7ed98263b89180261079870c964831Timo Sirainen{
3ab672903a7ed98263b89180261079870c964831Timo Sirainen struct chain_istream *cstream = (struct chain_istream *)stream;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen struct istream_chain_link *link = cstream->chain.head;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen const unsigned char *data;
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen size_t data_size, cur_data_pos, new_pos;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen size_t new_bytes_count;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen ssize_t ret;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen
3ab672903a7ed98263b89180261079870c964831Timo Sirainen if (link != NULL && link->eof) {
3ab672903a7ed98263b89180261079870c964831Timo Sirainen stream->istream.eof = TRUE;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen return -1;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen }
3ab672903a7ed98263b89180261079870c964831Timo Sirainen
3ab672903a7ed98263b89180261079870c964831Timo Sirainen if (!i_stream_chain_skip(cstream))
3ab672903a7ed98263b89180261079870c964831Timo Sirainen return 0;
c67f6d09c1aa9d8bfdce4167da8ac009fd2917ceTimo Sirainen i_assert(link != NULL);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen i_assert(stream->pos >= stream->skip + cstream->prev_stream_left);
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen cur_data_pos = stream->pos - (stream->skip + cstream->prev_stream_left);
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen data = i_stream_get_data(link->stream, &data_size);
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen if (data_size > cur_data_pos)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch ret = 0;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch else {
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen /* need to read more - NOTE: Can't use i_stream_read_memarea()
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen here, because our stream->buffer may point to the parent
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen istream. This could be avoided if we implemented
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen snapshotting ourself. */
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen i_assert(cur_data_pos == data_size);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch ret = i_stream_read(link->stream);
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen if (ret == -2 || ret == 0)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch return ret;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen if (ret == -1) {
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen if (link->stream->stream_errno != 0) {
2c42748505ef4aed83ff59b34e50ed5606900c86Timo Sirainen io_stream_set_error(&stream->iostream,
2c42748505ef4aed83ff59b34e50ed5606900c86Timo Sirainen "read(%s) failed: %s",
2c42748505ef4aed83ff59b34e50ed5606900c86Timo Sirainen i_stream_get_name(link->stream),
2c42748505ef4aed83ff59b34e50ed5606900c86Timo Sirainen i_stream_get_error(link->stream));
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen stream->istream.stream_errno =
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen link->stream->stream_errno;
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen return -1;
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen }
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen /* EOF of this stream, go to next stream */
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_stream_chain_read_next(cstream);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch cstream->prev_skip = stream->skip;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch return i_stream_chain_read(stream);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch }
5afe997e79978b7e989aa3b0bfdf4a813ecdc6f6Timo Sirainen /* we read something */
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen data = i_stream_get_data(link->stream, &data_size);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch }
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
59a63791d4ec70a134cb0dcbad1255d952075efeTimo Sirainen if (data_size == cur_data_pos) {
59a63791d4ec70a134cb0dcbad1255d952075efeTimo Sirainen /* nothing new read - preserve the buffer as it was */
59a63791d4ec70a134cb0dcbad1255d952075efeTimo Sirainen i_assert(ret == 0 || ret == -1);
59a63791d4ec70a134cb0dcbad1255d952075efeTimo Sirainen return ret;
59a63791d4ec70a134cb0dcbad1255d952075efeTimo Sirainen }
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch if (cstream->prev_stream_left == 0) {
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen /* we can point directly to the current stream's buffers */
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch stream->buffer = data;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch stream->pos -= stream->skip;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch stream->skip = 0;
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen new_pos = data_size;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch } else {
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen /* we still have some of the previous stream left. merge the
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen new data with it. */
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen i_assert(data_size > cur_data_pos);
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen new_bytes_count = data_size - cur_data_pos;
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen memcpy(i_stream_alloc(stream, new_bytes_count),
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen data + cur_data_pos, new_bytes_count);
04f9886078d53b136f747484b3ad9e1f7fad5994Timo Sirainen stream->buffer = stream->w_buffer;
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen new_pos = stream->pos + new_bytes_count;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch }
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
59a63791d4ec70a134cb0dcbad1255d952075efeTimo Sirainen i_assert(new_pos > stream->pos);
59a63791d4ec70a134cb0dcbad1255d952075efeTimo Sirainen ret = (ssize_t)(new_pos - stream->pos);
4afd5082f38342fd688acb5796912329f57dd02cTimo Sirainen stream->pos = new_pos;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch cstream->prev_skip = stream->skip;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch return ret;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch}
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
3ab672903a7ed98263b89180261079870c964831Timo Sirainenstatic void i_stream_chain_close(struct iostream_private *stream,
3ab672903a7ed98263b89180261079870c964831Timo Sirainen bool close_parent)
3ab672903a7ed98263b89180261079870c964831Timo Sirainen{
3ab672903a7ed98263b89180261079870c964831Timo Sirainen struct chain_istream *cstream = (struct chain_istream *)stream;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen
3ab672903a7ed98263b89180261079870c964831Timo Sirainen /* seek to the correct position in parent stream in case it didn't
3ab672903a7ed98263b89180261079870c964831Timo Sirainen end with EOF */
3ab672903a7ed98263b89180261079870c964831Timo Sirainen (void)i_stream_chain_skip(cstream);
3ab672903a7ed98263b89180261079870c964831Timo Sirainen
3ab672903a7ed98263b89180261079870c964831Timo Sirainen if (close_parent) {
3ab672903a7ed98263b89180261079870c964831Timo Sirainen struct istream_chain_link *link = cstream->chain.head;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen while (link != NULL) {
3ab672903a7ed98263b89180261079870c964831Timo Sirainen i_stream_close(link->stream);
3ab672903a7ed98263b89180261079870c964831Timo Sirainen link = link->next;
3ab672903a7ed98263b89180261079870c964831Timo Sirainen }
3ab672903a7ed98263b89180261079870c964831Timo Sirainen }
3ab672903a7ed98263b89180261079870c964831Timo Sirainen}
3ab672903a7ed98263b89180261079870c964831Timo Sirainen
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Boschstruct istream *i_stream_create_chain(struct istream_chain **chain_r)
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch{
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch struct chain_istream *cstream;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch cstream = i_new(struct chain_istream, 1);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch cstream->chain.stream = cstream;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
3ab672903a7ed98263b89180261079870c964831Timo Sirainen cstream->istream.iostream.close = i_stream_chain_close;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch cstream->istream.iostream.destroy = i_stream_chain_destroy;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch cstream->istream.iostream.set_max_buffer_size =
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch i_stream_chain_set_max_buffer_size;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch cstream->istream.read = i_stream_chain_read;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch cstream->istream.istream.readable_fd = FALSE;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch cstream->istream.istream.blocking = FALSE;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch cstream->istream.istream.seekable = FALSE;
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch *chain_r = &cstream->chain;
25c833d3cff7047f5c383892c379f59d116ff218Timo Sirainen return i_stream_create(&cstream->istream, NULL, -1,
25c833d3cff7047f5c383892c379f59d116ff218Timo Sirainen ISTREAM_CREATE_FLAG_NOOP_SNAPSHOT);
0fe2992e4d09f3ae4cceea88c9871c832d67b461Stephan Bosch}