bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen#include "lib.h"
0536ccb51d41e3078c3a9fa33e509fb4b2420f95Timo Sirainen#include "istream-private.h"
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen#include "istream-dot.h"
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainenstruct dot_istream {
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen struct istream_private istream;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen char pending[3]; /* max. \r\n */
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen /* how far in string "\r\n.\r" are we */
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen unsigned int state;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen /* state didn't actually start with \r */
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool state_no_cr:1;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen /* state didn't contain \n either (only at the beginnign of stream) */
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool state_no_lf:1;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen /* we've seen the "." line, keep returning EOF */
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool dot_eof:1;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen bool send_last_lf:1;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen};
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainenstatic int i_stream_dot_read_some(struct dot_istream *dstream)
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen{
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen struct istream_private *stream = &dstream->istream;
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen size_t size, avail;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen ssize_t ret;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen size = i_stream_get_data_size(stream->parent);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (size == 0) {
bcd286622779a93f809b11993db0550f8c7cc9b5Timo Sirainen ret = i_stream_read_memarea(stream->parent);
22ee6e1e6193299034ab99f77a650290de2fd6caTimo Sirainen if (ret <= 0) {
22ee6e1e6193299034ab99f77a650290de2fd6caTimo Sirainen i_assert(ret != -2); /* 0 sized buffer can't be full */
21bf1ba026461d15b5ecc2c2b6272206ce84fa80Timo Sirainen if (stream->parent->stream_errno != 0) {
21bf1ba026461d15b5ecc2c2b6272206ce84fa80Timo Sirainen stream->istream.stream_errno =
21bf1ba026461d15b5ecc2c2b6272206ce84fa80Timo Sirainen stream->parent->stream_errno;
21bf1ba026461d15b5ecc2c2b6272206ce84fa80Timo Sirainen } else if (ret < 0 && stream->parent->eof) {
21bf1ba026461d15b5ecc2c2b6272206ce84fa80Timo Sirainen /* we didn't see "." line */
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen io_stream_set_error(&stream->iostream,
1c6f6f5bef70f16546b3bc8f4cd5f93f373e82a2Timo Sirainen "dot-input stream ends without '.' line");
21bf1ba026461d15b5ecc2c2b6272206ce84fa80Timo Sirainen stream->istream.stream_errno = EPIPE;
21bf1ba026461d15b5ecc2c2b6272206ce84fa80Timo Sirainen }
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen return ret;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen }
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen size = i_stream_get_data_size(stream->parent);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen i_assert(size != 0);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen }
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
dd2df6a67f10792ce31a3666197c0b6885893a3aTimo Sirainen if (!i_stream_try_alloc(stream, size, &avail))
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen return -2;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen return 1;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen}
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainenstatic bool flush_pending(struct dot_istream *dstream, size_t *destp)
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen{
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen struct istream_private *stream = &dstream->istream;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen size_t dest = *destp;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen unsigned int i = 0;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen for (; dstream->pending[i] != '\0' && dest < stream->buffer_size; i++)
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen stream->w_buffer[dest++] = dstream->pending[i];
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen memmove(dstream->pending, dstream->pending + i,
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen sizeof(dstream->pending) - i);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen *destp = dest;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen return dest < stream->buffer_size;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen}
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainenstatic bool flush_dot_state(struct dot_istream *dstream, size_t *destp)
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen{
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen unsigned int i = 0;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (!dstream->state_no_cr)
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->pending[i++] = '\r';
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (dstream->state_no_lf)
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->state_no_lf = FALSE;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen else if (dstream->state > 1)
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->pending[i++] = '\n';
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->pending[i] = '\0';
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (dstream->state != 4)
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->state = 0;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen else {
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen /* \r\n.\r seen, go back to \r state */
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->state = 1;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen }
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen return flush_pending(dstream, destp);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen}
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainenstatic void i_stream_dot_eof(struct dot_istream *dstream, size_t *destp)
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen{
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (dstream->send_last_lf) {
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->state = 2;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen (void)flush_dot_state(dstream, destp);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen }
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->dot_eof = TRUE;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen}
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainenstatic ssize_t
eca36422686d8672ff73b84067a16bdb01fe6746Timo Siraineni_stream_dot_return(struct istream_private *stream, size_t dest, ssize_t ret)
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen{
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (dest != stream->pos) {
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen i_assert(dest > stream->pos);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen ret = dest - stream->pos;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen stream->pos = dest;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen }
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen return ret;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen}
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainenstatic ssize_t i_stream_dot_read(struct istream_private *stream)
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen{
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen /* @UNSAFE */
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen struct dot_istream *dstream = (struct dot_istream *)stream;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen const unsigned char *data;
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen size_t i, dest, size, avail;
21bf1ba026461d15b5ecc2c2b6272206ce84fa80Timo Sirainen ssize_t ret, ret1;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (dstream->pending[0] != '\0') {
dd2df6a67f10792ce31a3666197c0b6885893a3aTimo Sirainen if (!i_stream_try_alloc(stream, 1, &avail))
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen return -2;
21bf1ba026461d15b5ecc2c2b6272206ce84fa80Timo Sirainen dest = stream->pos;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen (void)flush_pending(dstream, &dest);
21bf1ba026461d15b5ecc2c2b6272206ce84fa80Timo Sirainen } else {
21bf1ba026461d15b5ecc2c2b6272206ce84fa80Timo Sirainen dest = stream->pos;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen }
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (dstream->dot_eof) {
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen stream->istream.eof = TRUE;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen return i_stream_dot_return(stream, dest, -1);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen }
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
21bf1ba026461d15b5ecc2c2b6272206ce84fa80Timo Sirainen /* we have to update stream->pos before reading more data */
21bf1ba026461d15b5ecc2c2b6272206ce84fa80Timo Sirainen ret1 = i_stream_dot_return(stream, dest, 0);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if ((ret = i_stream_dot_read_some(dstream)) <= 0) {
f7355a57790f41d5c0d5228552b19b25b2169d7cTimo Sirainen if (stream->istream.stream_errno != 0)
f7355a57790f41d5c0d5228552b19b25b2169d7cTimo Sirainen return -1;
21bf1ba026461d15b5ecc2c2b6272206ce84fa80Timo Sirainen if (ret1 != 0)
21bf1ba026461d15b5ecc2c2b6272206ce84fa80Timo Sirainen return ret1;
21bf1ba026461d15b5ecc2c2b6272206ce84fa80Timo Sirainen dest = stream->pos;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (ret == -1 && dstream->state != 0)
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen (void)flush_dot_state(dstream, &dest);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen return i_stream_dot_return(stream, dest, ret);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen }
21bf1ba026461d15b5ecc2c2b6272206ce84fa80Timo Sirainen dest = stream->pos;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen data = i_stream_get_data(stream->parent, &size);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen for (i = 0; i < size && dest < stream->buffer_size; i++) {
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen switch (dstream->state) {
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen case 0:
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen break;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen case 1:
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen /* CR seen */
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (data[i] == '\n')
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->state++;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen else {
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (!flush_dot_state(dstream, &dest))
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen goto end;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen }
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen break;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen case 2:
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen /* [CR]LF seen */
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (data[i] == '.')
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->state++;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen else {
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (!flush_dot_state(dstream, &dest))
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen goto end;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen }
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen break;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen case 3:
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen /* [CR]LF. seen */
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (data[i] == '\r')
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->state++;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen else if (data[i] == '\n') {
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen /* EOF */
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen i_stream_dot_eof(dstream, &dest);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen i++;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen goto end;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen } else {
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen /* drop the initial dot */
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (!flush_dot_state(dstream, &dest))
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen goto end;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen }
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen break;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen case 4:
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen /* [CR]LF.CR seen */
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (data[i] == '\n') {
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen /* EOF */
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen i_stream_dot_eof(dstream, &dest);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen i++;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen goto end;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen } else {
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen /* drop the initial dot */
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (!flush_dot_state(dstream, &dest))
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen goto end;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen }
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen }
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (dstream->state == 0) {
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (data[i] == '\r') {
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->state = 1;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->state_no_cr = FALSE;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen } else if (data[i] == '\n') {
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->state = 2;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->state_no_cr = TRUE;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen } else {
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen stream->w_buffer[dest++] = data[i];
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen }
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen }
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen }
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainenend:
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen i_stream_skip(stream->parent, i);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
21bf1ba026461d15b5ecc2c2b6272206ce84fa80Timo Sirainen ret = i_stream_dot_return(stream, dest, 0) + ret1;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen if (ret == 0)
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen return i_stream_dot_read(stream);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen i_assert(ret > 0);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen return ret;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen}
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainenstruct istream *i_stream_create_dot(struct istream *input, bool send_last_lf)
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen{
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen struct dot_istream *dstream;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream = i_new(struct dot_istream, 1);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->istream.max_buffer_size = input->real_stream->max_buffer_size;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->istream.read = i_stream_dot_read;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen
da2aa032ccfa8e7e4a4380ef738014549f4d2c2dTimo Sirainen dstream->istream.istream.readable_fd = FALSE;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->istream.istream.blocking = input->blocking;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->istream.istream.seekable = FALSE;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->send_last_lf = send_last_lf;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->state = 2;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->state_no_cr = TRUE;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen dstream->state_no_lf = TRUE;
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen return i_stream_create(&dstream->istream, input,
2974dca6be5120e49279f06c8aa952e5fac56048Timo Sirainen i_stream_get_fd(input), 0);
eca36422686d8672ff73b84067a16bdb01fe6746Timo Sirainen}