static int i_stream_base64_try_encode_line(struct base64_encoder_istream *bstream) { struct istream_private *stream = &bstream->istream; const unsigned char *data; size_t size, avail, buffer_avail; buffer_t buf; data = i_stream_get_data(stream->parent, &size); if (size == 0 || (size < 3 && !stream->parent->eof)) return 0; if (bstream->cur_line_len == bstream->chars_per_line) { /* @UNSAFE: end of line, add newline */ if (!i_stream_try_alloc(stream, bstream->crlf ? 2 : 1, &avail)) return -2; if (bstream->crlf) stream->w_buffer[stream->pos++] = '\r'; stream->w_buffer[stream->pos++] = '\n'; bstream->cur_line_len = 0; } i_stream_try_alloc(stream, (size+2)/3*4, &avail); buffer_avail = stream->buffer_size - stream->pos; if ((size + 2) / 3 * 4 > buffer_avail) { /* can't fit everything to destination buffer. write as much as we can. */ size = (buffer_avail / 4) * 3; if (size == 0) return -2; } else if (!stream->parent->eof && size % 3 != 0) { /* encode 3 chars at a time, so base64_encode() doesn't add '=' characters in the middle of the stream */ size -= (size % 3); } i_assert(size != 0); if (bstream->cur_line_len + (size+2)/3*4 > bstream->chars_per_line) { size = (bstream->chars_per_line - bstream->cur_line_len)/4 * 3; i_assert(size != 0); } buffer_create_from_data(&buf, stream->w_buffer + stream->pos, buffer_avail); base64_encode(data, size, &buf); i_assert(buf.used > 0); bstream->cur_line_len += buf.used; i_assert(bstream->cur_line_len <= bstream->chars_per_line); stream->pos += buf.used; i_stream_skip(stream->parent, size); return 1; }
void *i_stream_alloc(struct istream_private *stream, size_t size) { size_t old_size, avail_size; i_stream_try_alloc(stream, size, &avail_size); if (avail_size < size) { old_size = stream->buffer_size; stream->buffer_size = nearest_power(stream->pos + size); stream->w_buffer = i_realloc(stream->w_buffer, old_size, stream->buffer_size); stream->buffer = stream->w_buffer; i_stream_try_alloc(stream, size, &avail_size); i_assert(avail_size >= size); } return stream->w_buffer + stream->pos; }
static int i_stream_dot_read_some(struct dot_istream *dstream) { struct istream_private *stream = &dstream->istream; size_t size, avail; ssize_t ret; size = i_stream_get_data_size(stream->parent); if (size == 0) { ret = i_stream_read(stream->parent); if (ret <= 0 && (ret != -2 || stream->skip == 0)) { if (stream->parent->stream_errno != 0) { stream->istream.stream_errno = stream->parent->stream_errno; } else if (ret < 0 && stream->parent->eof) { /* we didn't see "." line */ io_stream_set_error(&stream->iostream, "dot-input stream ends without '.' line"); stream->istream.stream_errno = EPIPE; } return ret; } size = i_stream_get_data_size(stream->parent); i_assert(size != 0); } if (!i_stream_try_alloc(stream, size, &avail)) return -2; return 1; }
static ssize_t i_stream_nonuls_read(struct istream_private *stream) { struct nonuls_istream *nstream = (struct nonuls_istream *)stream; const unsigned char *data, *p; size_t i, size, avail_size; int ret; if ((ret = i_stream_read_parent(stream)) <= 0) return ret; data = i_stream_get_data(stream->parent, &size); if (!i_stream_try_alloc(stream, size, &avail_size)) return -2; if (size > avail_size) size = avail_size; i_assert(size > 0); p = memchr(data, '\0', size); if (p == NULL) { /* no NULs in this block */ memcpy(stream->w_buffer+stream->pos, data, size); } else { i = p-data; memcpy(stream->w_buffer+stream->pos, data, i); for (; i < size; i++) { stream->w_buffer[stream->pos+i] = data[i] == '\0' ? nstream->replace_chr : data[i]; } } stream->pos += size; i_stream_skip(stream->parent, size); return size; }
static bool read_from_buffer(struct seekable_istream *sstream, ssize_t *ret_r) { struct istream_private *stream = &sstream->istream; const unsigned char *data; size_t size, avail_size; if (stream->pos < sstream->buffer_peak) { /* This could be the first read() or we could have already seeked backwards. */ i_assert(stream->pos == 0 && stream->skip == 0); stream->skip = stream->istream.v_offset; stream->pos = sstream->buffer_peak; size = stream->pos - stream->skip; } else { /* need to read more */ i_assert(stream->pos == sstream->buffer_peak); size = sstream->cur_input == NULL ? 0 : i_stream_get_data_size(sstream->cur_input); if (size == 0) { /* read more to buffer */ *ret_r = read_more(sstream); if (*ret_r == 0 || *ret_r == -1) return TRUE; } /* we should have more now. */ data = i_stream_get_data(sstream->cur_input, &size); i_assert(size > 0); /* change skip to 0 temporarily so i_stream_try_alloc() won't try to compress the buffer. */ size_t old_skip = stream->skip; stream->skip = 0; bool have_space = i_stream_try_alloc(stream, size, &avail_size); stream->skip = old_skip; if (!have_space) return FALSE; if (size > avail_size) size = avail_size; memcpy(stream->w_buffer + stream->pos, data, size); stream->pos += size; sstream->buffer_peak += size; i_stream_skip(sstream->cur_input, size); } *ret_r = size; i_assert(*ret_r > 0); return TRUE; }
bool i_stream_add_data(struct istream *_stream, const unsigned char *data, size_t size) { struct istream_private *stream = _stream->real_stream; size_t size2; i_stream_try_alloc(stream, size, &size2); if (size > size2) return FALSE; memcpy(stream->w_buffer + stream->pos, data, size); stream->pos += size; return TRUE; }
static int i_stream_qp_try_decode_input(struct qp_decoder_istream *bstream, bool eof) { struct istream_private *stream = &bstream->istream; const unsigned char *data; size_t size, avail, buffer_avail, pos; buffer_t buf; int ret; data = i_stream_get_data(stream->parent, &size); if (size == 0) return 0; /* normally the decoded quoted-printable content can't be larger than the encoded content, but because we always use CRLFs, it may use twice as much space by only converting LFs to CRLFs. */ i_stream_try_alloc(stream, size, &avail); buffer_avail = stream->buffer_size - stream->pos; if (size > buffer_avail/2) { /* can't fit everything to destination buffer. write as much as we can. */ size = buffer_avail/2; if (size == 0) return -2; } buffer_create_from_data(&buf, stream->w_buffer + stream->pos, buffer_avail); ret = !eof ? quoted_printable_decode(data, size, &pos, &buf) : quoted_printable_decode_final(data, size, &pos, &buf); if (ret < 0) { io_stream_set_error(&stream->iostream, "Invalid quoted-printable data: 0x%s", binary_to_hex(data+pos, I_MAX(size-pos, 8))); stream->istream.stream_errno = EINVAL; return -1; } stream->pos += buf.used; i_stream_skip(stream->parent, pos); return pos > 0 ? 1 : 0; }
static int i_stream_qp_try_decode_input(struct qp_decoder_istream *bstream, bool eof) { struct istream_private *stream = &bstream->istream; const unsigned char *data; size_t size, avail, buffer_avail, pos; buffer_t buf; int ret; data = i_stream_get_data(stream->parent, &size); if (size == 0) return 0; /* the decoded quoted-printable content can never be larger than the encoded content. at worst they are equal. */ i_stream_try_alloc(stream, size, &avail); buffer_avail = stream->buffer_size - stream->pos; if (size > buffer_avail) { /* can't fit everything to destination buffer. write as much as we can. */ size = buffer_avail; if (size == 0) return -2; } buffer_create_from_data(&buf, stream->w_buffer + stream->pos, buffer_avail); ret = !eof ? quoted_printable_decode(data, size, &pos, &buf) : quoted_printable_decode_final(data, size, &pos, &buf); if (ret < 0) { stream->istream.stream_errno = EINVAL; return -1; } stream->pos += buf.used; i_stream_skip(stream->parent, pos); return pos > 0 ? 1 : 0; }
static int i_stream_base64_try_decode_block(struct base64_decoder_istream *bstream) { struct istream_private *stream = &bstream->istream; const unsigned char *data; size_t size, avail, buffer_avail, pos; buffer_t buf; data = i_stream_get_data(stream->parent, &size); if (size == 0) return 0; i_stream_try_alloc(stream, (size+3)/4*3, &avail); buffer_avail = stream->buffer_size - stream->pos; if ((size + 3) / 4 * 3 > buffer_avail) { /* can't fit everything to destination buffer. write as much as we can. */ size = (buffer_avail / 3) * 4; if (size == 0) return -2; } buffer_create_from_data(&buf, stream->w_buffer + stream->pos, buffer_avail); if (base64_decode(data, size, &pos, &buf) < 0) { io_stream_set_error(&stream->iostream, "Invalid base64 data: 0x%s", binary_to_hex(data+pos, I_MIN(size-pos, 8))); stream->istream.stream_errno = EINVAL; return -1; } stream->pos += buf.used; i_stream_skip(stream->parent, pos); return pos > 0 ? 1 : 0; }
static ssize_t quoted_string_istream_read(struct istream_private *stream) { struct quoted_string_istream *qsstream = (struct quoted_string_istream *)stream; const unsigned char *data; size_t i, dest, size, avail; ssize_t ret = 0; bool slash; if ( qsstream->finished ) { stream->istream.eof = TRUE; return -1; } /* Read from parent */ data = i_stream_get_data(stream->parent, &size); if (size == 0) { ret = i_stream_read(stream->parent); if (ret <= 0 && (ret != -2 || stream->skip == 0)) { if ( stream->parent->eof && stream->parent->stream_errno == 0 ) { io_stream_set_error(&stream->iostream, "Quoted string ends without closing quotes"); stream->istream.stream_errno = EINVAL; return -1; } stream->istream.stream_errno = stream->parent->stream_errno; stream->istream.eof = stream->parent->eof; return ret; } data = i_stream_get_data(stream->parent, &size); i_assert(size != 0); } /* Allocate buffer space */ if (!i_stream_try_alloc(stream, size, &avail)) return -2; /* Parse quoted string content */ dest = stream->pos; slash = qsstream->pending_slash; ret = 0; for (i = 0; i < size && dest < stream->buffer_size; i++) { if ( data[i] == '"' ) { if ( !slash ) { qsstream->finished = TRUE; i++; break; } slash = FALSE; } else if ( data[i] == '\\' ) { if ( !slash ) { slash = TRUE; continue; } slash = FALSE; } else if ( slash ) { if ( !IS_QUOTED_SPECIAL(data[i]) ) { io_stream_set_error(&stream->iostream, "Escaped quoted-string character is not a QUOTED-SPECIAL"); stream->istream.stream_errno = EINVAL; ret = -1; break; } slash = FALSE; } if ( (data[i] & 0x80) == 0 && ( data[i] == '\r' || data[i] == '\n' ) ) { io_stream_set_error(&stream->iostream, "Quoted string contains an invalid character"); stream->istream.stream_errno = EINVAL; ret = -1; break; } stream->w_buffer[dest++] = data[i]; } i_stream_skip(stream->parent, i); qsstream->pending_slash = slash; if ( ret < 0 ) { stream->pos = dest; return ret; } ret = dest - stream->pos; if (ret == 0) { if ( qsstream->finished ) { stream->istream.eof = TRUE; return -1; } i_assert(qsstream->pending_slash && size == 1); return quoted_string_istream_read(stream); } i_assert(ret > 0); stream->pos = dest; return ret; }
static ssize_t i_stream_file_read(struct istream_private *stream) { struct file_istream *fstream = (struct file_istream *) stream; uoff_t offset; size_t size; ssize_t ret; if (!i_stream_try_alloc(stream, 1, &size)) return -2; if (stream->fd == -1) { if (i_stream_file_open(stream) < 0) return -1; } offset = stream->istream.v_offset + (stream->pos - stream->skip); do { if (fstream->file) { ret = pread(stream->fd, stream->w_buffer + stream->pos, size, offset); } else if (fstream->seen_eof) { /* don't try to read() again. EOF from keyboard (^D) requires this to work right. */ ret = 0; } else { ret = read(stream->fd, stream->w_buffer + stream->pos, size); } } while (unlikely(ret < 0 && errno == EINTR && stream->istream.blocking)); if (ret == 0) { /* EOF */ stream->istream.eof = TRUE; fstream->seen_eof = TRUE; return -1; } if (unlikely(ret < 0)) { if (errno == EINTR || errno == EAGAIN) { i_assert(!stream->istream.blocking); ret = 0; } else { i_assert(errno != 0); /* if we get EBADF for a valid fd, it means something's really wrong and we'd better just crash. */ i_assert(errno != EBADF); if (fstream->file) { io_stream_set_error(&stream->iostream, "pread(size=%"PRIuSIZE_T " offset=%"PRIuUOFF_T") failed: %m", size, offset); } else { io_stream_set_error(&stream->iostream, "read(size=%"PRIuSIZE_T") failed: %m", size); } stream->istream.stream_errno = errno; return -1; } } if (ret > 0 && fstream->skip_left > 0) { i_assert(!fstream->file); i_assert(stream->skip == stream->pos); if (fstream->skip_left >= (size_t)ret) { fstream->skip_left -= ret; ret = 0; } else { ret -= fstream->skip_left; stream->pos += fstream->skip_left; stream->skip += fstream->skip_left; fstream->skip_left = 0; } } stream->pos += ret; i_assert(ret != 0 || !fstream->file); i_assert(ret != -1); return ret; }
static ssize_t i_stream_file_read(struct istream_private *stream) { struct file_istream *fstream = (struct file_istream *) stream; size_t size; ssize_t ret; if (!i_stream_try_alloc(stream, 1, &size)) return -2; if (stream->fd == -1) { if (i_stream_file_open(stream) < 0) return -1; } do { if (fstream->file) { ret = pread(stream->fd, stream->w_buffer + stream->pos, size, stream->istream.v_offset + (stream->pos - stream->skip)); } else if (fstream->seen_eof) { /* don't try to read() again. EOF from keyboard (^D) requires this to work right. */ ret = 0; } else { ret = read(stream->fd, stream->w_buffer + stream->pos, size); } } while (unlikely(ret < 0 && errno == EINTR && stream->istream.blocking)); if (ret == 0) { /* EOF */ stream->istream.eof = TRUE; fstream->seen_eof = TRUE; return -1; } if (unlikely(ret < 0)) { if (errno == EINTR || errno == EAGAIN) { i_assert(!stream->istream.blocking); ret = 0; } else { i_assert(errno != 0); stream->istream.stream_errno = errno; return -1; } } if (ret > 0 && fstream->skip_left > 0) { i_assert(!fstream->file); i_assert(stream->skip == stream->pos); if (fstream->skip_left >= (size_t)ret) { fstream->skip_left -= ret; ret = 0; } else { ret -= fstream->skip_left; stream->pos += fstream->skip_left; stream->skip += fstream->skip_left; fstream->skip_left = 0; } } stream->pos += ret; i_assert(ret != 0 || !fstream->file); i_assert(ret != -1); return ret; }
static ssize_t http_transfer_chunked_istream_read_data( struct http_transfer_chunked_istream *tcstream) { struct istream_private *stream = &tcstream->istream; const unsigned char *data; size_t size, avail; ssize_t ret = 0; if (tcstream->chunk_pos >= tcstream->chunk_size) { tcstream->state = HTTP_CHUNKED_PARSE_STATE_DATA_READY; return 0; } // FIXME: is this even necessary? i_stream_seek(stream->parent, tcstream->chunk_v_offset + tcstream->chunk_pos); /* read from parent if necessary */ data = i_stream_get_data(stream->parent, &size); if (size == 0) { ret = i_stream_read(stream->parent); if (ret <= 0 && (ret != -2 || stream->skip == 0)) { if ( stream->parent->eof && stream->parent->stream_errno == 0 ) { /* unexpected EOF */ tcstream->error = "Unexpected end of payload"; stream->istream.stream_errno = EIO; } else { /* parent stream error */ tcstream->error = "Stream error"; stream->istream.stream_errno = stream->parent->stream_errno; } return ret; } data = i_stream_get_data(stream->parent, &size); i_assert(size != 0); } size = size > (tcstream->chunk_size - tcstream->chunk_pos) ? (tcstream->chunk_size - tcstream->chunk_pos) : size; /* Allocate buffer space */ if (!i_stream_try_alloc(stream, size, &avail)) return -2; /* Copy payload */ size = size > avail ? avail : size; memcpy(&stream->w_buffer[stream->pos], data, size); i_stream_skip(stream->parent, size); tcstream->chunk_pos += size; if (tcstream->chunk_pos >= tcstream->chunk_size) tcstream->state = HTTP_CHUNKED_PARSE_STATE_DATA_READY; if ( ret < 0 ) { stream->pos = stream->pos+size; return ret; } ret = size; stream->pos = stream->pos+size; return ret; }
static ssize_t i_stream_ssl_read_real(struct istream_private *stream) { struct ssl_istream *sstream = (struct ssl_istream *)stream; struct ssl_iostream *ssl_io = sstream->ssl_io; unsigned char buffer[IO_BLOCK_SIZE]; size_t max_buffer_size = i_stream_get_max_buffer_size(&stream->istream); size_t orig_max_buffer_size = stream->max_buffer_size; size_t size; ssize_t ret, total_ret; if (sstream->seen_eof) { stream->istream.eof = TRUE; return -1; } if (stream->pos >= max_buffer_size) { i_stream_compress(stream); if (stream->pos >= max_buffer_size) return -2; } ret = openssl_iostream_more(ssl_io); if (ret <= 0) { if (ret < 0) { /* handshake failed */ i_assert(errno != 0); io_stream_set_error(&stream->iostream, "%s", ssl_io->last_error); stream->istream.stream_errno = errno; } return ret; } if (!i_stream_try_alloc(stream, 1, &size)) i_unreached(); if (stream->pos + size > max_buffer_size) { i_assert(max_buffer_size > stream->pos); size = max_buffer_size - stream->pos; } while ((ret = SSL_read(ssl_io->ssl, stream->w_buffer + stream->pos, size)) <= 0) { /* failed to read anything */ ret = openssl_iostream_handle_error(ssl_io, ret, "SSL_read"); if (ret <= 0) { if (ret == 0) return 0; if (ssl_io->last_error != NULL) { io_stream_set_error(&stream->iostream, "%s", ssl_io->last_error); } if (errno != EPIPE) stream->istream.stream_errno = errno; stream->istream.eof = TRUE; sstream->seen_eof = TRUE; return -1; } /* we did some BIO I/O, try reading again */ } stream->pos += ret; total_ret = ret; /* now make sure that we read everything already buffered in OpenSSL into the stream (without reading anything more). this makes I/O loop behave similary for ssl-istream as file-istream. */ sstream->ssl_io->input_handler = FALSE; stream->max_buffer_size = (size_t)-1; while ((ret = SSL_read(ssl_io->ssl, buffer, sizeof(buffer))) > 0) { memcpy(i_stream_alloc(stream, ret), buffer, ret); stream->pos += ret; total_ret += ret; } stream->max_buffer_size = orig_max_buffer_size; return total_ret; }
static ssize_t i_stream_dot_read(struct istream_private *stream) { /* @UNSAFE */ struct dot_istream *dstream = (struct dot_istream *)stream; const unsigned char *data; size_t i, dest, size, avail; ssize_t ret, ret1; if (dstream->pending[0] != '\0') { if (!i_stream_try_alloc(stream, 1, &avail)) return -2; dest = stream->pos; (void)flush_pending(dstream, &dest); } else { dest = stream->pos; } if (dstream->dot_eof) { stream->istream.eof = TRUE; return i_stream_dot_return(stream, dest, -1); } /* we have to update stream->pos before reading more data */ ret1 = i_stream_dot_return(stream, dest, 0); if ((ret = i_stream_dot_read_some(dstream)) <= 0) { if (ret1 != 0) return ret1; dest = stream->pos; if (ret == -1 && dstream->state != 0) (void)flush_dot_state(dstream, &dest); return i_stream_dot_return(stream, dest, ret); } dest = stream->pos; data = i_stream_get_data(stream->parent, &size); for (i = 0; i < size && dest < stream->buffer_size; i++) { switch (dstream->state) { case 0: break; case 1: /* CR seen */ if (data[i] == '\n') dstream->state++; else { if (!flush_dot_state(dstream, &dest)) goto end; } break; case 2: /* [CR]LF seen */ if (data[i] == '.') dstream->state++; else { if (!flush_dot_state(dstream, &dest)) goto end; } break; case 3: /* [CR]LF. seen */ if (data[i] == '\r') dstream->state++; else if (data[i] == '\n') { /* EOF */ i_stream_dot_eof(dstream, &dest); i++; goto end; } else { /* drop the initial dot */ if (!flush_dot_state(dstream, &dest)) goto end; } break; case 4: /* [CR]LF.CR seen */ if (data[i] == '\n') { /* EOF */ i_stream_dot_eof(dstream, &dest); i++; goto end; } else { /* drop the initial dot */ if (!flush_dot_state(dstream, &dest)) goto end; } } if (dstream->state == 0) { if (data[i] == '\r') { dstream->state = 1; dstream->state_no_cr = FALSE; } else if (data[i] == '\n') { dstream->state = 2; dstream->state_no_cr = TRUE; } else { stream->w_buffer[dest++] = data[i]; } } } end: i_stream_skip(stream->parent, i); ret = i_stream_dot_return(stream, dest, 0) + ret1; if (ret == 0) return i_stream_dot_read(stream); i_assert(ret > 0); return ret; }
static ssize_t i_stream_zlib_read(struct istream_private *stream) { struct zlib_istream *zstream = (struct zlib_istream *)stream; const unsigned char *data; uoff_t high_offset; size_t size, out_size; int ret; high_offset = stream->istream.v_offset + (stream->pos - stream->skip); if (zstream->eof_offset == high_offset) { i_assert(zstream->high_pos == 0 || zstream->high_pos == stream->pos); if (!zstream->trailer_read) { do { ret = i_stream_zlib_read_trailer(zstream); } while (ret == 0 && stream->istream.blocking); if (ret <= 0) return ret; } if (!zstream->gz || i_stream_read_eof(stream->parent)) { stream->istream.eof = TRUE; return -1; } zstream->starting_concated_output = TRUE; } if (zstream->starting_concated_output) { /* make sure there actually is something in parent stream. we don't want to reset the stream unless we actually see some concated output. */ ret = i_stream_read_more(stream->parent, &data, &size); if (ret <= 0) { if (ret == 0) return 0; if (stream->parent->stream_errno != 0) { stream->istream.stream_errno = stream->parent->stream_errno; } stream->istream.eof = TRUE; return -1; } /* gzip file with concatenated content */ zstream->eof_offset = (uoff_t)-1; zstream->stream_size = (uoff_t)-1; zstream->header_read = FALSE; zstream->trailer_read = FALSE; zstream->crc32 = 0; zstream->starting_concated_output = FALSE; (void)inflateEnd(&zstream->zs); i_stream_zlib_init(zstream); } if (!zstream->header_read) { do { ret = i_stream_zlib_read_header(stream); } while (ret == 0 && stream->istream.blocking); if (ret <= 0) return ret; zstream->header_read = TRUE; } if (stream->pos < zstream->high_pos) { /* we're here because we seeked back within the read buffer. */ ret = zstream->high_pos - stream->pos; stream->pos = zstream->high_pos; zstream->high_pos = 0; if (zstream->trailer_read) { high_offset = stream->istream.v_offset + (stream->pos - stream->skip); i_assert(zstream->eof_offset == high_offset); stream->istream.eof = TRUE; } return ret; } zstream->high_pos = 0; if (!zstream->marked) { if (!i_stream_try_alloc(stream, CHUNK_SIZE, &out_size)) return -2; /* buffer full */ } else { /* try to avoid compressing, so we can quickly seek backwards */ if (!i_stream_try_alloc_avoid_compress(stream, CHUNK_SIZE, &out_size)) return -2; /* buffer full */ } if (i_stream_read_more(stream->parent, &data, &size) < 0) { if (stream->parent->stream_errno != 0) { stream->istream.stream_errno = stream->parent->stream_errno; } else { i_assert(stream->parent->eof); zlib_read_error(zstream, "unexpected EOF"); stream->istream.stream_errno = EPIPE; } return -1; } if (size == 0) { /* no more input */ i_assert(!stream->istream.blocking); return 0; } zstream->zs.next_in = (void *)data; zstream->zs.avail_in = size; zstream->zs.next_out = stream->w_buffer + stream->pos; zstream->zs.avail_out = out_size; ret = inflate(&zstream->zs, Z_SYNC_FLUSH); out_size -= zstream->zs.avail_out; zstream->crc32 = crc32_data_more(zstream->crc32, stream->w_buffer + stream->pos, out_size); stream->pos += out_size; i_stream_skip(stream->parent, size - zstream->zs.avail_in); switch (ret) { case Z_OK: break; case Z_NEED_DICT: zlib_read_error(zstream, "can't read file without dict"); stream->istream.stream_errno = EIO; return -1; case Z_DATA_ERROR: zlib_read_error(zstream, "corrupted data"); stream->istream.stream_errno = EINVAL; return -1; case Z_MEM_ERROR: i_fatal_status(FATAL_OUTOFMEM, "zlib.read(%s): Out of memory", i_stream_get_name(&stream->istream)); case Z_STREAM_END: zstream->eof_offset = stream->istream.v_offset + (stream->pos - stream->skip); zstream->stream_size = zstream->eof_offset; zstream->zs.avail_in = 0; if (!zstream->trailer_read) { /* try to read and verify the trailer, we might not be called again. */ if (i_stream_zlib_read_trailer(zstream) < 0) return -1; } break; default: i_fatal("inflate() failed with %d", ret); } if (out_size == 0) { /* read more input */ return i_stream_zlib_read(stream); } return out_size; }