static void cmd_dump_imapzlib(int argc ATTR_UNUSED, char *argv[]) { struct istream *input, *input2; const unsigned char *data; size_t size; const char *line; int fd; fd = open(argv[1], O_RDONLY); if (fd < 0) i_fatal("open(%s) failed: %m", argv[1]); input = i_stream_create_fd_autoclose(&fd, 1024*32); while ((line = i_stream_read_next_line(input)) != NULL) { /* skip tag */ printf("%s\r\n", line); while (*line != ' ' && *line != '\0') line++; if (*line == '\0') continue; line++; if (strcmp(line, "OK Begin compression.") == 0 || strcasecmp(line, "COMPRESS DEFLATE") == 0) break; } input2 = i_stream_create_deflate(input, TRUE); i_stream_unref(&input); while (i_stream_read_more(input2, &data, &size) != -1) { fwrite(data, 1, size, stdout); i_stream_skip(input2, size); } i_stream_unref(&input2); fflush(stdout); }
static int client_run_url(struct client *client) { const unsigned char *data; size_t size; ssize_t ret = 0; while (i_stream_read_more(client->msg_part_input, &data, &size) > 0) { if ((ret = o_stream_send(client->output, data, size)) < 0) break; i_stream_skip(client->msg_part_input, ret); if (o_stream_get_buffer_used_size(client->output) >= 4096) { if ((ret = o_stream_flush(client->output)) < 0) break; if (ret == 0) return 0; } } if (client->output->closed || ret < 0) { imap_msgpart_url_free(&client->url); return -1; } if (client->msg_part_input->eof) { o_stream_send(client->output, "\n", 1); imap_msgpart_url_free(&client->url); return 1; } return 0; }
int client_auth_read_line(struct client *client) { const unsigned char *data; size_t i, size; unsigned int len; if (i_stream_read_more(client->input, &data, &size) == -1) { client_destroy(client, "Disconnected"); return -1; } /* see if we have a full line */ for (i = 0; i < size; i++) { if (data[i] == '\n') break; } if (client->auth_response == NULL) client->auth_response = str_new(default_pool, I_MAX(i+1, 256)); if (str_len(client->auth_response) + i > LOGIN_MAX_AUTH_BUF_SIZE) { client_destroy(client, "Authentication response too large"); return -1; } str_append_n(client->auth_response, data, i); i_stream_skip(client->input, i == size ? size : i+1); /* drop trailing \r */ len = str_len(client->auth_response); if (len > 0 && str_c(client->auth_response)[len-1] == '\r') str_truncate(client->auth_response, len-1); return i < size; }
static void test_ostream_multiplex_stream_read(struct istream *is) { uint8_t cid; const unsigned char *data; size_t siz,dlen=0,pos=0; if (i_stream_read_more(is, &data, &siz)>0) { /* parse stream */ for(;pos<siz;) { if (dlen > 0) { if (dlen < N_ELEMENTS(msgs)) { test_assert_idx(memcmp(&data[pos], msgs[dlen], dlen)==0, channel_counter[data[0] % 2]); } channel_counter[data[0] % 2]++; pos += dlen; dlen = 0; } else if (dlen == 0) { cid = data[pos] % 2; test_assert_idx(data[pos] < 2, channel_counter[cid]); pos++; dlen = be32_to_cpu_unaligned(&data[pos]); pos += 4; test_assert(dlen > 0 && dlen < N_ELEMENTS(msgs)); } } i_stream_skip(is, siz); } if (channel_counter[0] > 100 && channel_counter[1] > 100) io_loop_stop(current_ioloop); }
static void proxy_client_input(struct login_proxy *proxy) { const unsigned char *data; size_t size; ssize_t ret; proxy->last_io = ioloop_time; if (o_stream_get_buffer_used_size(proxy->server_output) > OUTBUF_THRESHOLD) { /* proxy's output buffer is already quite full. don't send more until we're below threshold. */ io_remove(&proxy->client_io); return; } if (i_stream_read_more(proxy->client_input, &data, &size) < 0) { const char *errstr = i_stream_get_error(proxy->client_input); login_proxy_free_errstr(&proxy, errstr, FALSE); return; } o_stream_cork(proxy->server_output); ret = o_stream_send(proxy->server_output, data, size); o_stream_uncork(proxy->server_output); if (ret != (ssize_t)size) login_proxy_free_ostream(&proxy, proxy->server_output, TRUE); else i_stream_skip(proxy->client_input, ret); }
static void decode_test(const char *qp_input, const char *output, bool broken_input, unsigned int buffer_size) { unsigned int qp_input_len = strlen(qp_input); struct istream *input_data, *input; const unsigned char *data; size_t i, size; string_t *str = t_str_new(32); int ret = 0; input_data = test_istream_create_data(qp_input, qp_input_len); test_istream_set_max_buffer_size(input_data, buffer_size); test_istream_set_allow_eof(input_data, FALSE); input = i_stream_create_qp_decoder(input_data); for (i = 1; i <= qp_input_len; i++) { test_istream_set_size(input_data, i); while ((ret = i_stream_read_more(input, &data, &size)) > 0) { str_append_n(str, data, size); i_stream_skip(input, size); } if (ret == -1 && broken_input) break; test_assert(ret == 0); } if (ret == 0) { test_istream_set_allow_eof(input_data, TRUE); while ((ret = i_stream_read_more(input, &data, &size)) > 0) { str_append_n(str, data, size); i_stream_skip(input, size); } } test_assert(ret == -1); test_assert((input->stream_errno == 0 && !broken_input) || (input->stream_errno == EINVAL && broken_input)); test_assert(strcmp(str_c(str), output) == 0); i_stream_unref(&input); i_stream_unref(&input_data); }
int message_get_body_size(struct istream *input, struct message_size *body, bool *has_nuls_r) { const unsigned char *msg; size_t i, size, missing_cr_count; int ret; memset(body, 0, sizeof(struct message_size)); *has_nuls_r = FALSE; missing_cr_count = 0; if ((ret = i_stream_read_more(input, &msg, &size)) <= 0) { i_assert(ret == -1); return ret < 0 && input->stream_errno != 0 ? -1 : 0; } if (msg[0] == '\n') missing_cr_count++; do { for (i = 1; i < size; i++) { if (msg[i] > '\n') continue; if (msg[i] == '\n') { if (msg[i-1] != '\r') { /* missing CR */ missing_cr_count++; } /* increase after making sure we didn't break at virtual \r */ body->lines++; } else if (msg[i] == '\0') { *has_nuls_r = TRUE; } } /* leave the last character, it may be \r */ i_stream_skip(input, i - 1); body->physical_size += i - 1; } while ((ret = i_stream_read_bytes(input, &msg, &size, 2)) > 0); i_assert(ret == -1); ret = input->stream_errno != 0 ? -1 : 0; i_stream_skip(input, 1); body->physical_size++; body->virtual_size = body->physical_size + missing_cr_count; i_assert(body->virtual_size >= body->physical_size); return ret; }
int message_skip_virtual(struct istream *input, uoff_t virtual_skip, bool *last_cr_r) { const unsigned char *msg; size_t i, size; bool cr_skipped = FALSE; int ret; *last_cr_r = FALSE; if (virtual_skip == 0) return 0; while ((ret = i_stream_read_more(input, &msg, &size)) > 0) { for (i = 0; i < size && virtual_skip > 0; i++) { virtual_skip--; if (msg[i] == '\r') { /* CR */ if (virtual_skip == 0) *last_cr_r = TRUE; } else if (msg[i] == '\n') { /* LF */ if ((i == 0 && !cr_skipped) || (i > 0 && msg[i-1] != '\r')) { if (virtual_skip == 0) { /* CR/LF boundary */ *last_cr_r = TRUE; break; } virtual_skip--; } } } i_stream_skip(input, i); if (i < size) return 0; cr_skipped = msg[i-1] == '\r'; } i_assert(ret == -1); return input->stream_errno == 0 ? 0 : -1; }
static ssize_t openssl_iostream_read_more(struct ssl_iostream *ssl_io, const unsigned char **data_r, size_t *size_r) { *data_r = i_stream_get_data(ssl_io->plain_input, size_r); if (*size_r > 0) return 0; if (!ssl_io->input_handler) { /* read plain_input only when we came here from input handler. this makes sure that we don't get stuck with some input unexpectedly buffered. */ return 0; } if (i_stream_read_more(ssl_io->plain_input, data_r, size_r) < 0) return -1; return 0; }
int http_header_parse_next_field(struct http_header_parser *parser, const char **name_r, const unsigned char **data_r, size_t *size_r, const char **error_r) { const uoff_t max_size = parser->limits.max_size; const uoff_t max_field_size = parser->limits.max_field_size; const unsigned char *data; size_t size; int ret; *error_r = NULL; while ((ret=i_stream_read_more(parser->input, &parser->begin, &size)) > 0) { /* check header size limits */ if (parser->size >= max_size) { *error_r = "Excessive header size"; return -1; } if (parser->field_size > max_field_size) { *error_r = "Excessive header field size"; return -1; } /* don't parse beyond header size limits */ if (size > (max_size - parser->size)) size = max_size - parser->size; if (size > (max_field_size - parser->field_size)) { size = max_field_size - parser->field_size; size = (size == 0 ? 1 : size); /* need to parse one more byte */ } parser->cur = parser->begin; parser->end = parser->cur + size; if ((ret=http_header_parse(parser)) < 0) { *error_r = parser->error; return -1; } i_stream_skip(parser->input, parser->cur - parser->begin); parser->size += parser->cur - parser->begin; parser->field_size += parser->cur - parser->begin; if (ret == 1) { parser->field_size = 0; if (parser->state != HTTP_HEADER_PARSE_STATE_EOH) { data = buffer_get_data(parser->value_buf, &size); /* trim trailing OWS */ while (size > 0 && (data[size-1] == ' ' || data[size-1] == '\t')) size--; *name_r = str_c(parser->name); *data_r = data; *size_r = size; parser->state = HTTP_HEADER_PARSE_STATE_INIT; } else { *name_r = NULL; *data_r = NULL; } return 1; } } i_assert(ret != -2); if (ret < 0) { if (i_stream_is_eof(parser->input)) *error_r = "Premature end of input"; else *error_r = "Stream error"; } return ret; }
static ssize_t i_stream_lzma_read(struct istream_private *stream) { struct lzma_istream *zstream = (struct lzma_istream *)stream; const unsigned char *data; uoff_t high_offset; size_t size, out_size; lzma_ret 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); stream->istream.eof = TRUE; return -1; } 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->eof_offset != (uoff_t)-1) { 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 (stream->pos + CHUNK_SIZE > stream->buffer_size) { /* try to keep at least CHUNK_SIZE available */ if (!zstream->marked && stream->skip > 0) { /* don't try to keep anything cached if we don't have a seek mark. */ i_stream_compress(stream); } if (stream->buffer_size < i_stream_get_max_buffer_size(&stream->istream)) i_stream_grow_buffer(stream, CHUNK_SIZE); if (stream->pos == stream->buffer_size) { if (stream->skip > 0) { /* lose our buffer cache */ i_stream_compress(stream); } if (stream->pos == stream->buffer_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); lzma_stream_end(zstream); stream->istream.eof = TRUE; } return -1; } if (size == 0) { /* no more input */ i_assert(!stream->istream.blocking); return 0; } zstream->strm.next_in = data; zstream->strm.avail_in = size; out_size = stream->buffer_size - stream->pos; zstream->strm.next_out = stream->w_buffer + stream->pos; zstream->strm.avail_out = out_size; ret = lzma_code(&zstream->strm, LZMA_RUN); out_size -= zstream->strm.avail_out; stream->pos += out_size; i_stream_skip(stream->parent, size - zstream->strm.avail_in); switch (ret) { case LZMA_OK: break; case LZMA_DATA_ERROR: case LZMA_BUF_ERROR: lzma_read_error(zstream, "corrupted data"); stream->istream.stream_errno = EINVAL; return -1; case LZMA_FORMAT_ERROR: lzma_read_error(zstream, "wrong magic in header (not xz file?)"); stream->istream.stream_errno = EINVAL; return -1; case LZMA_OPTIONS_ERROR: lzma_read_error(zstream, "Unsupported xz options"); stream->istream.stream_errno = EINVAL; return -1; case LZMA_MEM_ERROR: i_fatal_status(FATAL_OUTOFMEM, "lzma.read(%s): Out of memory", i_stream_get_name(&stream->istream)); case LZMA_STREAM_END: lzma_stream_end(zstream); if (out_size == 0) { stream->istream.eof = TRUE; return -1; } break; default: lzma_read_error(zstream, t_strdup_printf( "lzma_code() failed with %d", ret)); stream->istream.stream_errno = EINVAL; return -1; } if (out_size == 0) { /* read more input */ return i_stream_lzma_read(stream); } return out_size; }
static int astream_part_finish(struct attachment_istream *astream, const char **error_r) { struct attachment_istream_part *part = &astream->part; struct istream_attachment_info info; struct istream *input; struct ostream *output; string_t *digest_str; const unsigned char *data; size_t size; int ret = 0; if (o_stream_nfinish(part->temp_output) < 0) { *error_r = t_strdup_printf("write(%s) failed: %s", o_stream_get_name(part->temp_output), o_stream_get_error(part->temp_output)); return -1; } memset(&info, 0, sizeof(info)); info.start_offset = astream->part.start_offset; /* base64_bytes contains how many valid base64 bytes there are so far. if the base64 ends properly, it'll specify how much of the MIME part is saved as an attachment. the rest of the data (typically linefeeds) is added back to main stream */ info.encoded_size = part->base64_bytes; /* get the hash before base64-decoder resets it */ digest_str = t_str_new(128); hash_format_write(astream->set.hash_format, digest_str); info.hash = str_c(digest_str); /* if it looks like we can decode base64 without any data loss, do it and write the decoded data to another temp file. */ if (!part->base64_failed) { if (part->base64_state == BASE64_STATE_0 && part->base64_bytes > 0) { /* there is no trailing LF or '=' characters, but it's not completely empty */ part->base64_state = BASE64_STATE_EOM; } if (part->base64_state == BASE64_STATE_EOM) { /* base64 data looks ok. */ if (astream_decode_base64(astream) < 0) part->base64_failed = TRUE; } else { part->base64_failed = TRUE; } } /* open attachment output file */ info.part = astream->cur_part; if (!part->base64_failed) { info.base64_blocks_per_line = part->base64_line_blocks; info.base64_have_crlf = part->base64_have_crlf; /* base64-decoder updated the hash, use it */ str_truncate(digest_str, 0); hash_format_write(astream->set.hash_format, digest_str); info.hash = str_c(digest_str); } else { /* couldn't decode base64, so write the entire MIME part as attachment */ info.encoded_size = part->temp_output->offset; } if (astream->set.open_attachment_ostream(&info, &output, error_r, astream->context) < 0) return -1; /* copy data to attachment from temp file */ input = i_stream_create_fd(part->temp_fd, IO_BLOCK_SIZE); while (i_stream_read_more(input, &data, &size) > 0) { o_stream_nsend(output, data, size); i_stream_skip(input, size); } if (input->stream_errno != 0) { *error_r = t_strdup_printf("read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); ret = -1; } i_stream_destroy(&input); if (astream->set.close_attachment_ostream(output, ret == 0, error_r, astream->context) < 0) ret = -1; return ret; }
static int astream_decode_base64(struct attachment_istream *astream) { struct attachment_istream_part *part = &astream->part; buffer_t *extra_buf = NULL; struct istream *input, *base64_input; struct ostream *output; const unsigned char *data; size_t size; ssize_t ret; buffer_t *buf; int outfd; bool failed = FALSE; if (part->base64_bytes < astream->set.min_size || part->temp_output->offset > part->base64_bytes + BASE64_ATTACHMENT_MAX_EXTRA_BYTES) { /* only a small part of the MIME part is base64-encoded. */ return -1; } if (part->base64_line_blocks == 0) { /* only one line of base64 */ part->base64_line_blocks = part->cur_base64_blocks; i_assert(part->base64_line_blocks > 0); } /* decode base64 data and write it to another temp file */ outfd = astream->set.open_temp_fd(astream->context); if (outfd == -1) return -1; buf = buffer_create_dynamic(default_pool, 1024); input = i_stream_create_fd(part->temp_fd, IO_BLOCK_SIZE); base64_input = i_stream_create_limit(input, part->base64_bytes); output = o_stream_create_fd_file(outfd, 0, FALSE); o_stream_cork(output); hash_format_reset(astream->set.hash_format); size_t bytes_needed = 1; while ((ret = i_stream_read_bytes(base64_input, &data, &size, bytes_needed)) > 0) { buffer_set_used_size(buf, 0); if (base64_decode(data, size, &size, buf) < 0) { i_error("istream-attachment: BUG: " "Attachment base64 data unexpectedly broke"); failed = TRUE; break; } i_stream_skip(base64_input, size); o_stream_nsend(output, buf->data, buf->used); hash_format_loop(astream->set.hash_format, buf->data, buf->used); bytes_needed = i_stream_get_data_size(base64_input) + 1; } if (ret != -1) { i_assert(failed); } else if (base64_input->stream_errno != 0) { i_error("istream-attachment: read(%s) failed: %s", i_stream_get_name(base64_input), i_stream_get_error(base64_input)); failed = TRUE; } if (o_stream_nfinish(output) < 0) { i_error("istream-attachment: write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); failed = TRUE; } buffer_free(&buf); i_stream_unref(&base64_input); o_stream_unref(&output); if (input->v_offset != part->temp_output->offset && !failed) { /* write the rest of the data to the message stream */ extra_buf = buffer_create_dynamic(default_pool, 1024); while ((ret = i_stream_read_more(input, &data, &size)) > 0) { buffer_append(extra_buf, data, size); i_stream_skip(input, size); } i_assert(ret == -1); if (input->stream_errno != 0) { i_error("istream-attachment: read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); failed = TRUE; } } i_stream_unref(&input); if (failed) { i_close_fd(&outfd); return -1; } /* successfully wrote it. switch to using it. */ o_stream_destroy(&part->temp_output); i_close_fd(&part->temp_fd); part->temp_fd = outfd; if (extra_buf != NULL) { stream_add_data(astream, extra_buf->data, extra_buf->used); buffer_free(&extra_buf); } return 0; }
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_is_eof(stream->parent)) { 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; (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 (stream->pos + CHUNK_SIZE > stream->buffer_size) { /* try to keep at least CHUNK_SIZE available */ if (!zstream->marked && stream->skip > 0) { /* don't try to keep anything cached if we don't have a seek mark. */ i_stream_compress(stream); } if (stream->buffer_size < i_stream_get_max_buffer_size(&stream->istream)) i_stream_grow_buffer(stream, CHUNK_SIZE); if (stream->pos == stream->buffer_size) { if (stream->skip > 0) { /* lose our buffer cache */ i_stream_compress(stream); } if (stream->pos == stream->buffer_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; out_size = stream->buffer_size - stream->pos; 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 = EINVAL; 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; }
static int lmtp_client_send_data(struct lmtp_client *client) { const unsigned char *data; unsigned char add; size_t i, size; bool sent_bytes = FALSE; int ret; if (client->output_finished) return 0; while ((ret = i_stream_read_more(client->data_input, &data, &size)) > 0) { add = '\0'; for (i = 0; i < size; i++) { if (data[i] == '\n') { if ((i == 0 && client->output_last != '\r') || (i > 0 && data[i-1] != '\r')) { /* missing CR */ add = '\r'; break; } } else if (data[i] == '.' && ((i == 0 && client->output_last == '\n') || (i > 0 && data[i-1] == '\n'))) { /* escape the dot */ add = '.'; break; } } if (i > 0) { if (o_stream_send(client->output, data, i) < 0) break; client->output_last = data[i-1]; i_stream_skip(client->data_input, i); sent_bytes = TRUE; } if (o_stream_get_buffer_used_size(client->output) >= 4096) { if ((ret = o_stream_flush(client->output)) < 0) break; if (ret == 0) { /* continue later */ o_stream_set_flush_pending(client->output, TRUE); return 0; } } if (add != '\0') { if (o_stream_send(client->output, &add, 1) < 0) break; client->output_last = add; } } if (client->data_input->stream_errno != 0) { i_error("lmtp client: read(%s) failed: %s", i_stream_get_name(client->data_input), i_stream_get_error(client->data_input)); lmtp_client_fail(client, "451 4.3.0 Internal failure while reading DATA input"); return -1; } if (sent_bytes && client->data_output_callback != NULL) client->data_output_callback(client->data_output_context); if (ret == 0 || ret == -2) { /* -2 can happen with tee istreams */ return 0; } if (client->output_last != '\n') { /* didn't end with CRLF */ o_stream_nsend(client->output, "\r\n", 2); } o_stream_nsend(client->output, ".\r\n", 3); client->output_finished = TRUE; io_loop_time_refresh(); client->times.data_sent = ioloop_timeval; return 0; }
static ssize_t i_stream_bzlib_read(struct istream_private *stream) { struct bzlib_istream *zstream = (struct bzlib_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); stream->istream.eof = TRUE; return -1; } 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->eof_offset != (uoff_t)-1) { 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 (stream->pos + CHUNK_SIZE > stream->buffer_size) { /* try to keep at least CHUNK_SIZE available */ if (!zstream->marked && stream->skip > 0) { /* don't try to keep anything cached if we don't have a seek mark. */ i_stream_compress(stream); } if (stream->buffer_size < i_stream_get_max_buffer_size(&stream->istream)) i_stream_grow_buffer(stream, CHUNK_SIZE); if (stream->pos == stream->buffer_size) { if (stream->skip > 0) { /* lose our buffer cache */ i_stream_compress(stream); } if (stream->pos == stream->buffer_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); bzlib_read_error(zstream, "unexpected EOF"); stream->istream.stream_errno = EINVAL; } return -1; } if (size == 0) { /* no more input */ i_assert(!stream->istream.blocking); return 0; } zstream->zs.next_in = (char *)data; zstream->zs.avail_in = size; out_size = stream->buffer_size - stream->pos; zstream->zs.next_out = (char *)stream->w_buffer + stream->pos; zstream->zs.avail_out = out_size; ret = BZ2_bzDecompress(&zstream->zs); out_size -= zstream->zs.avail_out; stream->pos += out_size; i_stream_skip(stream->parent, size - zstream->zs.avail_in); switch (ret) { case BZ_OK: break; case BZ_PARAM_ERROR: i_unreached(); case BZ_DATA_ERROR: bzlib_read_error(zstream, "corrupted data"); stream->istream.stream_errno = EINVAL; return -1; case BZ_DATA_ERROR_MAGIC: bzlib_read_error(zstream, "wrong magic in header (not bz2 file?)"); stream->istream.stream_errno = EINVAL; return -1; case BZ_MEM_ERROR: i_fatal_status(FATAL_OUTOFMEM, "bzlib.read(%s): Out of memory", i_stream_get_name(&stream->istream)); case BZ_STREAM_END: zstream->eof_offset = stream->istream.v_offset + (stream->pos - stream->skip); zstream->stream_size = zstream->eof_offset; if (out_size == 0) { stream->istream.eof = TRUE; return -1; } break; default: i_fatal("BZ2_bzDecompress() failed with %d", ret); } if (out_size == 0) { /* read more input */ return i_stream_bzlib_read(stream); } return out_size; }