istream.c revision 7a77b948806106b46a33f3e6a3869657f49877fd
2454dfa32c93c20a8522c6ed42fe057baaac9f9aStephan Bosch/* Copyright (c) 2002-2014 Dovecot authors, see the included COPYING file */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenvoid i_stream_set_name(struct istream *stream, const char *name)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen stream->real_stream->iostream.name = i_strdup(name);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenconst char *i_stream_get_name(struct istream *stream)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen while (stream->real_stream->iostream.name == NULL) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic void i_stream_close_full(struct istream *stream, bool close_parents)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen io_stream_close(&stream->real_stream->iostream, close_parents);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen io_stream_ref(&stream->real_stream->iostream);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen struct istream_private *_stream = (*stream)->real_stream;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen io_stream_unref(&(*stream)->real_stream->iostream);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenvoid i_stream_add_destroy_callback(struct istream *stream,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct iostream_private *iostream = &stream->real_stream->iostream;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (!array_is_created(&iostream->destroy_callbacks))
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen i_array_init(&iostream->destroy_callbacks, 2);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen dc = array_append_space(&iostream->destroy_callbacks);
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainenvoid i_stream_remove_destroy_callback(struct istream *stream,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct iostream_private *iostream = &stream->real_stream->iostream;
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen unsigned int i, count;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen dcs = array_get(&iostream->destroy_callbacks, &count);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen for (i = 0; i < count; i++) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (dcs[i].callback == (istream_callback_t *)callback) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen array_delete(&iostream->destroy_callbacks, i, 1);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen struct istream_private *_stream = stream->real_stream;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenconst char *i_stream_get_error(struct istream *stream)
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* we'll only return errors for streams that have stream_errno set.
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen we might be returning unintended error otherwise. */
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen return "<no error>";
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen for (s = stream; s != NULL; s = s->real_stream->parent) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenvoid i_stream_set_init_buffer_size(struct istream *stream, size_t size)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenvoid i_stream_set_max_buffer_size(struct istream *stream, size_t max_size)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen io_stream_set_max_buffer_size(&stream->real_stream->iostream, max_size);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainensize_t i_stream_get_max_buffer_size(struct istream *stream)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenvoid i_stream_set_return_partial_line(struct istream *stream, bool set)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic void i_stream_update(struct istream_private *stream)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen stream->parent_expected_offset = stream->parent->v_offset;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct istream_private *_stream = stream->real_stream;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (unlikely(stream->closed || stream->stream_errno != 0)) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen i_stream_seek(_stream->parent, _stream->parent_expected_offset);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen i_assert(old_size <= _stream->pos - _stream->skip);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* error handling should be easier if we now just
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen assume the stream is now at EOF */
686ad6d723004b807fd558f3ef9d1f88afa7e127Timo Sirainen i_assert(old_size == _stream->pos - _stream->skip);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen i_assert((size_t)ret+old_size == _stream->pos - _stream->skip);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenssize_t i_stream_read_copy_from_parent(struct istream *istream)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct istream_private *stream = istream->real_stream;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen stream->buffer = i_stream_get_data(stream->parent, &pos);
7bd72e4deca3cbf757dd1ea298486d9f3bc24226Timo Sirainen if ((ret = i_stream_read(stream->parent)) == -2) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen stream->istream.stream_errno = stream->parent->stream_errno;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen stream->buffer = i_stream_get_data(stream->parent, &pos);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* check again, in case the parent stream had been seeked
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen backwards and the previous read() didn't get us far
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen ret = pos > stream->pos ? (ssize_t)(pos - stream->pos) :
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainenvoid i_stream_skip(struct istream *stream, uoff_t count)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct istream_private *_stream = stream->real_stream;
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainen /* within buffer */
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainen /* have to seek forward */
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainen _stream->seek(_stream, stream->v_offset + count, FALSE);
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainenstatic bool i_stream_can_optimize_seek(struct istream_private *stream)
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainen /* use the fast route only if the parent stream hasn't been changed */
7da99e97d68f854b8726755d36dfb24b6cf08701Timo Sirainen return i_stream_can_optimize_seek(stream->parent->real_stream);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenvoid i_stream_seek(struct istream *stream, uoff_t v_offset)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct istream_private *_stream = stream->real_stream;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen i_stream_skip(stream, v_offset - stream->v_offset);
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainenvoid i_stream_seek_mark(struct istream *stream, uoff_t v_offset)
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen struct istream_private *_stream = stream->real_stream;
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen struct istream_private *_stream = stream->real_stream;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenint i_stream_stat(struct istream *stream, bool exact, const struct stat **st_r)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct istream_private *_stream = stream->real_stream;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenint i_stream_get_size(struct istream *stream, bool exact, uoff_t *size_r)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct istream_private *_stream = stream->real_stream;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen return _stream->get_size(_stream, exact, size_r);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenbool i_stream_have_bytes_left(struct istream *stream)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen return i_stream_get_data_size(stream) > 0 || !stream->eof;
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainenuoff_t i_stream_get_absolute_offset(struct istream *stream)
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen return stream->real_stream->abs_start_offset + stream->v_offset;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic char *i_stream_next_line_finish(struct istream_private *stream, size_t i)
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen /* modify the buffer directly */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen ret = (char *)stream->w_buffer + stream->skip;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* use a temporary string to return it */
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen stream->line_str = str_new(default_pool, 256);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen str_append_n(stream->line_str, stream->buffer + stream->skip,
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainenstatic char *i_stream_last_line(struct istream_private *_stream)
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen if (_stream->istream.eof && _stream->skip != _stream->pos &&
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen /* the last line is missing LF and we want to return it. */
da2f9bc5d477bdfee1773d7dcbcdd5a293c7d48cTimo Sirainen return i_stream_next_line_finish(_stream, _stream->pos);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenchar *i_stream_next_line(struct istream *stream)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct istream_private *_stream = stream->real_stream;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen const unsigned char *pos;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen pos = memchr(_stream->buffer + _stream->skip, '\n',
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenchar *i_stream_read_next_line(struct istream *stream)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen io_stream_set_error(&stream->real_stream->iostream,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen i_stream_get_data_size(stream), stream->v_offset);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen return i_stream_last_line(stream->real_stream);
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainenbool i_stream_last_line_crlf(struct istream *stream)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenstatic bool i_stream_is_buffer_invalid(const struct istream_private *stream)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* the buffer can't point to parent, because it doesn't exist */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* we can pretty safely assume that the stream is using its
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen own private buffer, so it can never become invalid. */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen stream->parent->real_stream->access_counter) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* parent has been modified behind this stream, we can't trust
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen that our buffer is valid */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen return i_stream_is_buffer_invalid(stream->parent->real_stream);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenconst unsigned char *
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Siraineni_stream_get_data(struct istream *stream, size_t *size_r)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct istream_private *_stream = stream->real_stream;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* This stream may be using parent's buffer directly as
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen _stream->buffer, but the parent stream has already been
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen modified indirectly. This means that the buffer might no
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen longer point to where we assume it points to. So we'll
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen just return the stream as empty until it's read again.
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen It's a bit ugly to suddenly drop data from the stream that
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen was already read, but since this happens only with shared
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen parent istreams the caller is hopefully aware enough that
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen something like this might happen. The other solutions would
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen be to a) try to automatically read the data back (but we
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen can't handle errors..) or b) always copy data to stream's
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen own buffer instead of pointing to parent's buffer (but this
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen causes data copying that is nearly always unnecessary). */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* if we had already read until EOF, mark the stream again as
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen not being at the end of file. */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainensize_t i_stream_get_data_size(struct istream *stream)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenunsigned char *i_stream_get_modifiable_data(struct istream *stream,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct istream_private *_stream = stream->real_stream;
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (_stream->skip >= _stream->pos || _stream->w_buffer == NULL) {
459f60325f94f486ef057241b42d8a9e9c376fb4Timo Sirainenint i_stream_read_data(struct istream *stream, const unsigned char **data_r,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* we need more data */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen } while (ret > 0);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen /* need to read more */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* we read at least some new data */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenvoid i_stream_compress(struct istream_private *stream)
c2fbbf7515aa419dc8b2d62a3c2bb0471d51a391Timo Sirainen memmove(stream->w_buffer, stream->w_buffer + stream->skip,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenvoid i_stream_grow_buffer(struct istream_private *stream, size_t bytes)
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (stream->buffer_size <= stream->init_buffer_size)
3a508ab3b10ff08889f3046a6bbf8553b55e3d44Timo Sirainen stream->buffer_size = stream->init_buffer_size;
cddfd1355db6b60c71d7ee3c0b4f23b3efcc9ad1Timo Sirainen stream->buffer_size = nearest_power(stream->buffer_size);
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen if (stream->buffer_size > stream->max_buffer_size)
eef4ba0cc3e78f8c26804c1c9251a76580a41f0cTimo Sirainen stream->buffer_size = stream->max_buffer_size;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen stream->w_buffer = i_realloc(stream->w_buffer, old_size,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenbool i_stream_try_alloc(struct istream_private *stream,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen if (wanted_size > stream->buffer_size - stream->pos) {
5caf685b62a30b1f935b80a3f9f1bdcefb63a38cTimo Sirainen /* remove the unused bytes from beginning of buffer */
5caf685b62a30b1f935b80a3f9f1bdcefb63a38cTimo Sirainen stream->buffer_size < stream->max_buffer_size) {
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen /* buffer is full - grow it */
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen i_stream_grow_buffer(stream, I_STREAM_MIN_SIZE);
be6bde0e1f40db97871b657603188fc9041f1d04Timo Sirainenvoid *i_stream_alloc(struct istream_private *stream, size_t size)
be6bde0e1f40db97871b657603188fc9041f1d04Timo Sirainen i_stream_try_alloc(stream, size, &avail_size);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen stream->buffer_size = nearest_power(stream->pos + size);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen stream->w_buffer = i_realloc(stream->w_buffer, old_size,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen i_stream_try_alloc(stream, size, &avail_size);
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainenbool i_stream_add_data(struct istream *_stream, const unsigned char *data,
d43bed2d458520fd01c28229ce2b178a4593a4a7Timo Sirainen struct istream_private *stream = _stream->real_stream;
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainen memcpy(stream->w_buffer + stream->pos, data, size);
51cbc45fc1ac5dde29bc2adbb175945df1b4f7d4Timo Sirainenvoid i_stream_set_input_pending(struct istream *stream, bool pending)
bool close_parent)
if (available == 0) {
struct istream *
struct istream *
return input;