int maildir_save_continue(struct mail_save_context *_ctx) { struct maildir_save_context *ctx = (struct maildir_save_context *)_ctx; struct mail_storage *storage = &ctx->mbox->storage->storage; if (ctx->failed) return -1; do { if (o_stream_send_istream(_ctx->data.output, ctx->input) < 0) { if (!mail_storage_set_error_from_errno(storage)) { mail_storage_set_critical(storage, "o_stream_send_istream(%s/%s) " "failed: %m", ctx->tmpdir, ctx->file_last->tmp_name); } ctx->failed = TRUE; return -1; } if (ctx->cur_dest_mail != NULL) index_mail_cache_parse_continue(ctx->cur_dest_mail); /* both tee input readers may consume data from our primary input stream. we'll have to make sure we don't return with one of the streams still having data in them. */ } while (i_stream_read(ctx->input) > 0); return 0; }
int sieve_file_storage_save_continue (struct sieve_storage_save_context *sctx) { struct sieve_file_save_context *fsctx = (struct sieve_file_save_context *)sctx; switch (o_stream_send_istream(fsctx->output, sctx->input)) { case OSTREAM_SEND_ISTREAM_RESULT_FINISHED: case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT: return 0; case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT: i_unreached(); case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT: sieve_storage_set_critical(sctx->storage, "save: read(%s) failed: %s", i_stream_get_name(sctx->input), i_stream_get_error(sctx->input)); return -1; case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT: sieve_storage_set_critical(sctx->storage, "save: write(%s) failed: %s", fsctx->tmp_path, o_stream_get_error(fsctx->output)); return -1; } return 0; }
int mail_send_forward(struct mail_deliver_context *ctx, const char *forwardto) { static const char *hide_headers[] = { "Return-Path" }; struct istream *input; struct ostream *output; struct smtp_client *smtp_client; const char *return_path; if (mail_get_stream(ctx->src_mail, NULL, NULL, &input) < 0) return -1; return_path = mail_deliver_get_return_address(ctx); if (mailbox_get_settings(ctx->src_mail->box)->mail_debug) { i_debug("Sending a forward to <%s> with return path <%s>", forwardto, return_path); } smtp_client = smtp_client_open(ctx->set, forwardto, return_path, &output); input = i_stream_create_header_filter(input, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR, hide_headers, N_ELEMENTS(hide_headers), *null_header_filter_callback, (void *)NULL); (void)o_stream_send_istream(output, input); i_stream_unref(&input); return smtp_client_close(smtp_client); }
static int server_connection_send_cmd_input_more(struct server_connection *conn) { off_t ret; /* ostream-dot writes only up to max buffer size, so keep it non-zero */ o_stream_set_max_buffer_size(conn->cmd_output, IO_BLOCK_SIZE); ret = o_stream_send_istream(conn->cmd_output, conn->cmd_input); o_stream_set_max_buffer_size(conn->cmd_output, (size_t)-1); if (ret >= 0 && i_stream_have_bytes_left(conn->cmd_input)) { o_stream_set_flush_pending(conn->cmd_output, TRUE); return 0; } if (conn->cmd_input->stream_errno != 0) { i_error("read(%s) failed: %s", i_stream_get_name(conn->cmd_input), i_stream_get_error(conn->cmd_input)); } else if (conn->cmd_output->stream_errno != 0 || o_stream_flush(conn->cmd_output) < 0) { i_error("write(%s) failed: %s", o_stream_get_name(conn->cmd_output), o_stream_get_error(conn->cmd_output)); } i_stream_destroy(&conn->cmd_input); o_stream_destroy(&conn->cmd_output); return ret < 0 ? -1 : 1; }
static ssize_t i_stream_mail_filter_read_once(struct mail_filter_istream *mstream) { struct istream_private *stream = &mstream->istream; ssize_t ret; if (mstream->ext_out != NULL) { /* we haven't sent everything yet */ (void)o_stream_send_istream(mstream->ext_out, stream->parent); if (mstream->ext_out->stream_errno != 0) { stream->istream.stream_errno = mstream->ext_out->stream_errno; return -1; } if (i_stream_is_eof(stream->parent)) { o_stream_destroy(&mstream->ext_out); /* if we wanted to be a blocking stream, from now on the rest of the reads are */ if (stream->istream.blocking) net_set_nonblock(mstream->fd, FALSE); if (shutdown(mstream->fd, SHUT_WR) < 0) i_error("ext-filter: shutdown() failed: %m"); } } i_stream_skip(mstream->ext_in, mstream->prev_ret); ret = i_stream_read_copy_from(&stream->istream, mstream->ext_in); mstream->prev_ret = ret < 0 ? 0 : ret; return ret; }
int dbox_save_continue(struct mail_save_context *_ctx) { struct dbox_save_context *ctx = (struct dbox_save_context *)_ctx; struct mail_storage *storage = _ctx->transaction->box->storage; if (ctx->failed) return -1; if (_ctx->data.attach != NULL) return index_attachment_save_continue(_ctx); do { if (o_stream_send_istream(_ctx->data.output, ctx->input) < 0) { if (!mail_storage_set_error_from_errno(storage)) { mail_storage_set_critical(storage, "write(%s) failed: %m", o_stream_get_name(_ctx->data.output)); } ctx->failed = TRUE; return -1; } index_mail_cache_parse_continue(_ctx->dest_mail); /* both tee input readers may consume data from our primary input stream. we'll have to make sure we don't return with one of the streams still having data in them. */ } while (i_stream_read(ctx->input) > 0); return 0; }
static int stream_copy(struct dbox_file *file, struct ostream *output, const char *path, uoff_t count) { struct istream *input; off_t bytes; input = i_stream_create_limit(file->input, count); bytes = o_stream_send_istream(output, input); i_stream_unref(&input); if (bytes < 0) { mail_storage_set_critical(&file->storage->storage, "o_stream_send_istream(%s, %s) failed: %m", file->cur_path, path); return -1; } if ((uoff_t)bytes != count) { mail_storage_set_critical(&file->storage->storage, "o_stream_send_istream(%s) copied only %" PRIuUOFF_T" of %"PRIuUOFF_T" bytes", path, bytes, count); return -1; } return 0; }
static void iostream_pump_copy(struct iostream_pump *pump) { enum ostream_send_istream_result res; size_t old_size; o_stream_cork(pump->output); old_size = o_stream_get_max_buffer_size(pump->output); o_stream_set_max_buffer_size(pump->output, I_MIN(IO_BLOCK_SIZE, o_stream_get_max_buffer_size(pump->output))); res = o_stream_send_istream(pump->output, pump->input); o_stream_set_max_buffer_size(pump->output, old_size); o_stream_uncork(pump->output); switch(res) { case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT: io_remove(&pump->io); pump->callback(IOSTREAM_PUMP_STATUS_INPUT_ERROR, pump->context); return; case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT: io_remove(&pump->io); pump->callback(IOSTREAM_PUMP_STATUS_OUTPUT_ERROR, pump->context); return; case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT: i_assert(!pump->output->blocking); pump->waiting_output = TRUE; io_remove(&pump->io); return; case OSTREAM_SEND_ISTREAM_RESULT_FINISHED: pump->waiting_output = FALSE; io_remove(&pump->io); /* flush it */ switch (o_stream_flush(pump->output)) { case -1: pump->callback(IOSTREAM_PUMP_STATUS_OUTPUT_ERROR, pump->context); break; case 0: pump->waiting_output = TRUE; pump->completed = TRUE; break; default: pump->callback(IOSTREAM_PUMP_STATUS_INPUT_EOF, pump->context); break; } return; case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT: i_assert(!pump->input->blocking); pump->waiting_output = FALSE; return; } i_unreached(); }
static int fetch_stream_continue(struct imap_fetch_context *ctx) { struct imap_fetch_state *state = &ctx->state; const char *disconnect_reason; off_t ret; o_stream_set_max_buffer_size(ctx->client->output, 0); ret = o_stream_send_istream(ctx->client->output, state->cur_input); o_stream_set_max_buffer_size(ctx->client->output, (size_t)-1); if (ret > 0) { state->cur_offset += ret; if (ctx->state.cur_stats_sizep != NULL) *ctx->state.cur_stats_sizep += ret; } if (state->cur_offset != state->cur_size) { /* unfinished */ if (state->cur_input->stream_errno != 0) { fetch_read_error(ctx, &disconnect_reason); client_disconnect(ctx->client, disconnect_reason); return -1; } if (!i_stream_have_bytes_left(state->cur_input)) { /* Input stream gave less data than expected */ i_error("read(%s): FETCH %s for mailbox %s UID %u " "got too little data: " "%"PRIuUOFF_T" vs %"PRIuUOFF_T, i_stream_get_name(state->cur_input), state->cur_human_name, mailbox_get_vname(state->cur_mail->box), state->cur_mail->uid, state->cur_offset, state->cur_size); mail_set_cache_corrupted(state->cur_mail, state->cur_size_field); client_disconnect(ctx->client, "FETCH failed"); return -1; } if (ret < 0) { /* client probably disconnected */ return -1; } o_stream_set_flush_pending(ctx->client->output, TRUE); return 0; } return 1; }
int mbox_move(struct mbox_sync_context *sync_ctx, uoff_t dest, uoff_t source, uoff_t size) { struct istream *input; struct ostream *output; off_t ret; i_assert(size < OFF_T_MAX); if (size == 0 || source == dest) return 0; i_stream_sync(sync_ctx->input); output = o_stream_create_fd_file(sync_ctx->write_fd, (uoff_t)-1, FALSE); i_stream_seek(sync_ctx->file_input, source); if (o_stream_seek(output, dest) < 0) { mbox_set_syscall_error(sync_ctx->mbox, "o_stream_seek()"); o_stream_unref(&output); return -1; } input = i_stream_create_limit(sync_ctx->file_input, size); ret = o_stream_send_istream(output, input); i_stream_unref(&input); if (ret == (off_t)size) ret = 0; else if (ret >= 0) { mbox_sync_set_critical(sync_ctx, "mbox_move(%"PRIuUOFF_T", %"PRIuUOFF_T", %"PRIuUOFF_T ") moved only %"PRIuUOFF_T" bytes", dest, source, size, (uoff_t)ret); ret = -1; } else if (ret < 0) { errno = output->stream_errno; mbox_set_syscall_error(sync_ctx->mbox, "o_stream_send_istream()"); } mbox_sync_file_updated(sync_ctx, FALSE); o_stream_destroy(&output); return (int)ret; }
static int fetch_stream_continue(struct imap_fetch_context *ctx) { struct imap_fetch_state *state = &ctx->state; const char *disconnect_reason; uoff_t orig_input_offset = state->cur_input->v_offset; enum ostream_send_istream_result res; o_stream_set_max_buffer_size(ctx->client->output, 0); res = o_stream_send_istream(ctx->client->output, state->cur_input); o_stream_set_max_buffer_size(ctx->client->output, (size_t)-1); if (ctx->state.cur_stats_sizep != NULL) { *ctx->state.cur_stats_sizep += state->cur_input->v_offset - orig_input_offset; } switch (res) { case OSTREAM_SEND_ISTREAM_RESULT_FINISHED: if (state->cur_input->v_offset != state->cur_size) { /* Input stream gave less data than expected */ mail_set_cache_corrupted(state->cur_mail, state->cur_size_field, t_strdup_printf( "read(%s): FETCH %s got too little data: " "%"PRIuUOFF_T" vs %"PRIuUOFF_T, i_stream_get_name(state->cur_input), state->cur_human_name, state->cur_input->v_offset, state->cur_size)); client_disconnect(ctx->client, "FETCH failed"); return -1; } return 1; case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT: i_unreached(); case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT: return 0; case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT: fetch_read_error(ctx, &disconnect_reason); client_disconnect(ctx->client, disconnect_reason); return -1; case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT: /* client disconnected */ return -1; } i_unreached(); }
static int server_connection_send_cmd_input_more(struct server_connection *conn) { enum ostream_send_istream_result res; int ret = -1; /* ostream-dot writes only up to max buffer size, so keep it non-zero */ o_stream_set_max_buffer_size(conn->cmd_output, IO_BLOCK_SIZE); res = o_stream_send_istream(conn->cmd_output, conn->cmd_input); o_stream_set_max_buffer_size(conn->cmd_output, (size_t)-1); switch (res) { case OSTREAM_SEND_ISTREAM_RESULT_FINISHED: break; case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT: return 1; case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT: return 0; case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT: i_error("read(%s) failed: %s", i_stream_get_name(conn->cmd_input), i_stream_get_error(conn->cmd_input)); break; case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT: i_error("write(%s) failed: %s", o_stream_get_name(conn->cmd_output), o_stream_get_error(conn->cmd_output)); break; } if (res == OSTREAM_SEND_ISTREAM_RESULT_FINISHED) { if ((ret = o_stream_flush(conn->cmd_output)) == 0) return 0; else if (ret < 0) { i_error("write(%s) failed: %s", o_stream_get_name(conn->cmd_output), o_stream_get_error(conn->cmd_output)); } } i_stream_destroy(&conn->cmd_input); o_stream_destroy(&conn->cmd_output); return ret; }
static int fetch_stream_send_direct(struct imap_fetch_context *ctx) { off_t ret; o_stream_set_max_buffer_size(ctx->client->output, 0); ret = o_stream_send_istream(ctx->client->output, ctx->cur_input); o_stream_set_max_buffer_size(ctx->client->output, (size_t)-1); if (ret < 0) return -1; ctx->cur_offset += ret; if (ctx->cur_append_eoh && ctx->cur_offset + 2 == ctx->cur_size) { /* Netscape missing EOH workaround. */ if (o_stream_send(ctx->client->output, "\r\n", 2) < 0) return -1; ctx->cur_offset += 2; ctx->cur_append_eoh = FALSE; } if (ctx->cur_offset != ctx->cur_size) { /* unfinished */ if (!i_stream_have_bytes_left(ctx->cur_input)) { /* Input stream gave less data than expected */ i_error("FETCH %s for mailbox %s UID %u " "got too little data (copying): " "%"PRIuUOFF_T" vs %"PRIuUOFF_T, ctx->cur_name, mailbox_get_vname(ctx->mail->box), ctx->mail->uid, ctx->cur_offset, ctx->cur_size); mail_set_cache_corrupted(ctx->mail, ctx->cur_size_field); client_disconnect(ctx->client, "FETCH failed"); return -1; } o_stream_set_flush_pending(ctx->client->output, TRUE); return 0; } return 1; }
static bool cmd_getscript_continue(struct client_command_context *cmd) { struct client *client = cmd->client; struct cmd_getscript_context *ctx = cmd->context; switch (o_stream_send_istream(client->output, ctx->script_stream)) { case OSTREAM_SEND_ISTREAM_RESULT_FINISHED: if ( ctx->script_stream->v_offset != ctx->script_size && !ctx->failed ) { /* Input stream gave less data than expected */ sieve_storage_set_critical(ctx->storage, "GETSCRIPT for script `%s' from %s got too little data: " "%"PRIuUOFF_T" vs %"PRIuUOFF_T, sieve_script_name(ctx->script), sieve_script_location(ctx->script), ctx->script_stream->v_offset, ctx->script_size); client_disconnect(ctx->client, "GETSCRIPT failed"); ctx->failed = TRUE; } break; case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT: i_unreached(); case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT: return FALSE; case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT: sieve_storage_set_critical(ctx->storage, "o_stream_send_istream() failed for script `%s' from %s: %s", sieve_script_name(ctx->script), sieve_script_location(ctx->script), i_stream_get_error(ctx->script_stream)); ctx->failed = TRUE; break; case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT: client_disconnect(ctx->client, io_stream_get_disconnect_reason(client->input, client->output)); ctx->failed = TRUE; break; } return cmd_getscript_finish(ctx); }
static bool cmd_getscript_continue(struct client_command_context *cmd) { struct client *client = cmd->client; struct cmd_getscript_context *ctx = cmd->context; off_t ret; ret = o_stream_send_istream(client->output, ctx->script_stream); if ( ret < 0 ) { sieve_storage_set_critical(ctx->storage, "o_stream_send_istream(%s) failed: %m", sieve_script_filename(ctx->script)); ctx->failed = TRUE; return cmd_getscript_finish(ctx); } ctx->script_offset += ret; if ( ctx->script_offset != ctx->script_size && !ctx->failed ) { /* unfinished */ if ( !i_stream_have_bytes_left(ctx->script_stream) ) { /* Input stream gave less data than expected */ sieve_storage_set_critical(ctx->storage, "GETSCRIPT for SCRIPT %s got too little data: " "%"PRIuUOFF_T" vs %"PRIuUOFF_T, sieve_script_name(ctx->script), ctx->script_offset, ctx->script_size); client_disconnect(ctx->client, "GETSCRIPT failed"); ctx->failed = TRUE; return cmd_getscript_finish(ctx); } return FALSE; } return cmd_getscript_finish(ctx); }
static void test_ostream_file_send_istream_sendfile(void) { struct istream *input, *input2; struct ostream *output; char buf[10]; int fd, sock_fd[2]; test_begin("ostream file send istream sendfile()"); /* temp file istream */ fd = open(".temp.istream", O_RDWR | O_CREAT | O_TRUNC, 0600); if (fd == -1) i_fatal("creat(.temp.istream) failed: %m"); test_assert(write(fd, "abcdefghij", 10) == 10); test_assert(lseek(fd, 0, SEEK_SET) == 0); input = i_stream_create_fd_autoclose(&fd, 1024); /* temp socket ostream */ i_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fd) == 0); output = o_stream_create_fd_autoclose(sock_fd, 0); /* test that sendfile() works */ i_stream_seek(input, 3); input2 = i_stream_create_limit(input, 4); test_assert(o_stream_send_istream(output, input2) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 4); test_assert(read(sock_fd[1], buf, sizeof(buf)) == 4 && memcmp(buf, "defg", 4) == 0); i_stream_unref(&input2); i_stream_unref(&input); o_stream_destroy(&output); i_close_fd(&sock_fd[1]); i_unlink(".temp.istream"); test_end(); }
int http_client_request_send_more(struct http_client_request *req, bool pipelined, const char **error_r) { struct http_client_connection *conn = req->conn; struct ostream *output = req->payload_output; enum ostream_send_istream_result res; i_assert(req->payload_input != NULL); i_assert(req->payload_output != NULL); if (conn->io_req_payload != NULL) io_remove(&conn->io_req_payload); /* chunked ostream needs to write to the parent stream's buffer */ o_stream_set_max_buffer_size(output, IO_BLOCK_SIZE); res = o_stream_send_istream(output, req->payload_input); o_stream_set_max_buffer_size(output, (size_t)-1); switch (res) { case OSTREAM_SEND_ISTREAM_RESULT_FINISHED: /* finished sending */ if (!req->payload_chunked && req->payload_input->v_offset - req->payload_offset != req->payload_size) { *error_r = t_strdup_printf("BUG: stream '%s' input size changed: " "%"PRIuUOFF_T"-%"PRIuUOFF_T" != %"PRIuUOFF_T, i_stream_get_name(req->payload_input), req->payload_input->v_offset, req->payload_offset, req->payload_size); i_error("%s", *error_r); //FIXME: remove? return -1; } if (req->payload_wait) { /* this chunk of input is finished (client needs to act; disable timeout) */ i_assert(!pipelined); conn->output_locked = TRUE; http_client_connection_stop_request_timeout(conn); if (req->client->ioloop != NULL) io_loop_stop(req->client->ioloop); } else { /* finished sending payload */ http_client_request_finish_payload_out(req); } return 0; case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT: /* input is blocking (client needs to act; disable timeout) */ conn->output_locked = TRUE; if (!pipelined) http_client_connection_stop_request_timeout(conn); conn->io_req_payload = io_add_istream(req->payload_input, http_client_request_payload_input, req); return 0; case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT: /* output is blocking (server needs to act; enable timeout) */ conn->output_locked = TRUE; if (!pipelined) http_client_connection_start_request_timeout(conn); http_client_request_debug(req, "Partially sent payload"); return 0; case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT: /* we're in the middle of sending a request, so the connection will also have to be aborted */ *error_r = t_strdup_printf("read(%s) failed: %s", i_stream_get_name(req->payload_input), i_stream_get_error(req->payload_input)); /* the payload stream assigned to this request is broken, fail this the request immediately */ http_client_request_error(&req, HTTP_CLIENT_REQUEST_ERROR_BROKEN_PAYLOAD, "Broken payload stream"); return -1; case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT: /* failed to send request */ *error_r = t_strdup_printf("write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); return -1; } i_unreached(); }
static void test_ostream_file_send_istream_file(void) { struct istream *input, *input2; struct ostream *output; char buf[10]; int fd; test_begin("ostream file send istream file"); /* temp file istream */ fd = open(".temp.istream", O_RDWR | O_CREAT | O_TRUNC, 0600); if (fd == -1) i_fatal("creat(.temp.istream) failed: %m"); test_assert(write(fd, "1234567890", 10) == 10); test_assert(lseek(fd, 0, SEEK_SET) == 0); input = i_stream_create_fd_autoclose(&fd, 1024); /* temp file ostream */ fd = open(".temp.ostream", O_RDWR | O_CREAT | O_TRUNC, 0600); if (fd == -1) i_fatal("creat(.temp.ostream) failed: %m"); output = o_stream_create_fd(fd, 0); /* test that writing works between two files */ i_stream_seek(input, 3); input2 = i_stream_create_limit(input, 4); test_assert(o_stream_send_istream(output, input2) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 4); test_assert(pread(fd, buf, sizeof(buf), 0) == 4 && memcmp(buf, "4567", 4) == 0); i_stream_unref(&input2); /* test that writing works within the same file */ i_stream_destroy(&input); input = i_stream_create_fd(fd, 1024); /* forwards: 4567 -> 4677 */ o_stream_seek(output, 1); i_stream_seek(input, 2); input2 = i_stream_create_limit(input, 2); test_assert(o_stream_send_istream(output, input2) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 3); test_assert(pread(fd, buf, sizeof(buf), 0) == 4 && memcmp(buf, "4677", 4) == 0); i_stream_destroy(&input2); i_stream_destroy(&input); /* backwards: 1234 -> 11234 */ memcpy(buf, "1234", 4); test_assert(pwrite(fd, buf, 4, 0) == 4); input = i_stream_create_fd(fd, 1024); o_stream_seek(output, 1); test_assert(o_stream_send_istream(output, input) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 5); test_assert(pread(fd, buf, sizeof(buf), 0) == 5 && memcmp(buf, "11234", 5) == 0); i_stream_destroy(&input); o_stream_destroy(&output); i_close_fd(&fd); i_unlink(".temp.istream"); i_unlink(".temp.ostream"); test_end(); }
static void test_iostream_temp_istream(void) { struct istream *input, *input2, *temp_input; struct ostream *output; int fd; test_begin("iostream_temp istream"); fd = open(".temp.istream", O_RDWR | O_CREAT | O_TRUNC, 0600); if (fd == -1) i_fatal("create(.temp.istream) failed: %m"); test_assert(write(fd, "foobar", 6) == 6); test_assert(lseek(fd, 0, SEEK_SET) == 0); input = i_stream_create_fd_autoclose(&fd, 1024); /* a working fd-dup */ output = iostream_temp_create_sized(".nonexistent/", IOSTREAM_TEMP_FLAG_TRY_FD_DUP, "test", 1); test_assert(o_stream_send_istream(output, input) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 6); temp_input = iostream_temp_finish(&output, 128); test_assert(i_stream_read(temp_input) == 6); i_stream_destroy(&temp_input); /* non-working fd-dup: write data before sending istream */ i_stream_seek(input, 0); output = iostream_temp_create_sized(".intentional-nonexistent-error/", IOSTREAM_TEMP_FLAG_TRY_FD_DUP, "test", 4); test_assert(o_stream_send(output, "1234", 4) == 4); test_assert(output->offset == 4); test_expect_error_string("safe_mkstemp"); test_assert(o_stream_send_istream(output, input) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 10); test_expect_no_more_errors(); o_stream_destroy(&output); /* non-working fd-dup: write data after sending istream */ i_stream_seek(input, 0); output = iostream_temp_create_sized(".intentional-nonexistent-error/", IOSTREAM_TEMP_FLAG_TRY_FD_DUP, "test", 4); test_assert(o_stream_send_istream(output, input) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 6); test_expect_error_string("safe_mkstemp"); test_assert(o_stream_send(output, "1", 1) == 1); test_assert(output->offset == 7); test_expect_no_more_errors(); o_stream_destroy(&output); /* non-working fd-dup: send two istreams */ i_stream_seek(input, 0); input2 = i_stream_create_limit(input, (uoff_t)-1); output = iostream_temp_create_sized(".intentional-nonexistent-error/", IOSTREAM_TEMP_FLAG_TRY_FD_DUP, "test", 4); test_assert(o_stream_send_istream(output, input) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 6); test_expect_error_string("safe_mkstemp"); test_assert(o_stream_send_istream(output, input2) == OSTREAM_SEND_ISTREAM_RESULT_FINISHED); test_assert(output->offset == 12); test_expect_no_more_errors(); o_stream_destroy(&output); i_stream_unref(&input2); i_stream_destroy(&input); i_unlink(".temp.istream"); test_end(); }
int http_client_request_send_more(struct http_client_request *req, const char **error_r) { struct http_client_connection *conn = req->conn; struct ostream *output = req->payload_output; off_t ret; int fd; i_assert(req->payload_input != NULL); i_assert(req->payload_output != NULL); if (conn->io_req_payload != NULL) io_remove(&conn->io_req_payload); /* chunked ostream needs to write to the parent stream's buffer */ o_stream_set_max_buffer_size(output, IO_BLOCK_SIZE); ret = o_stream_send_istream(output, req->payload_input); o_stream_set_max_buffer_size(output, (size_t)-1); if (req->payload_input->stream_errno != 0) { /* the payload stream assigned to this request is broken, fail this the request immediately */ http_client_request_send_error(req, HTTP_CLIENT_REQUEST_ERROR_BROKEN_PAYLOAD, "Broken payload stream"); /* we're in the middle of sending a request, so the connection will also have to be aborted */ errno = req->payload_input->stream_errno; *error_r = t_strdup_printf("read(%s) failed: %s", i_stream_get_name(req->payload_input), i_stream_get_error(req->payload_input)); ret = -1; } else if (output->stream_errno != 0) { /* failed to send request */ errno = output->stream_errno; *error_r = t_strdup_printf("write(%s) failed: %s", o_stream_get_name(output), o_stream_get_error(output)); ret = -1; } else { i_assert(ret >= 0); } if (ret < 0 || i_stream_is_eof(req->payload_input)) { if (!req->payload_chunked && req->payload_input->v_offset - req->payload_offset != req->payload_size) { *error_r = "stream input size changed [BUG]"; i_error("stream input size changed"); //FIXME return -1; } if (req->payload_wait) { conn->output_locked = TRUE; if (req->client->ioloop != NULL) io_loop_stop(req->client->ioloop); } else { http_client_request_finish_payload_out(req); } } else if (i_stream_get_data_size(req->payload_input) > 0) { /* output is blocking */ conn->output_locked = TRUE; o_stream_set_flush_pending(output, TRUE); http_client_request_debug(req, "Partially sent payload"); } else { /* input is blocking */ fd = i_stream_get_fd(req->payload_input); conn->output_locked = TRUE; i_assert(fd >= 0); conn->io_req_payload = io_add (fd, IO_READ, http_client_request_payload_input, req); } return ret < 0 ? -1 : 0; }
int mail_send_rejection(struct mail_deliver_context *ctx, const char *recipient, const char *reason) { struct mail *mail = ctx->src_mail; struct istream *input; struct smtp_client *smtp_client; struct ostream *output; const char *return_addr, *hdr; const char *value, *msgid, *orig_msgid, *boundary; string_t *str; int ret; if (mail_get_first_header(mail, "Message-ID", &orig_msgid) < 0) orig_msgid = NULL; if (mail_get_first_header(mail, "Auto-Submitted", &value) > 0 && strcasecmp(value, "no") != 0) { i_info("msgid=%s: Auto-submitted message discarded: %s", orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80), str_sanitize(reason, 512)); return 0; } return_addr = mail_deliver_get_return_address(ctx); if (return_addr == NULL) { i_info("msgid=%s: Return-Path missing, rejection reason: %s", orig_msgid == NULL ? "" : str_sanitize(orig_msgid, 80), str_sanitize(reason, 512)); return 0; } if (mailbox_get_settings(mail->box)->mail_debug) { i_debug("Sending a rejection to %s: %s", recipient, str_sanitize(reason, 512)); } smtp_client = smtp_client_open(ctx->set, return_addr, NULL, &output); msgid = mail_deliver_get_new_message_id(ctx); boundary = t_strdup_printf("%s/%s", my_pid, ctx->set->hostname); str = t_str_new(512); str_printfa(str, "Message-ID: %s\r\n", msgid); str_printfa(str, "Date: %s\r\n", message_date_create(ioloop_time)); str_printfa(str, "From: Mail Delivery Subsystem <%s>\r\n", ctx->set->postmaster_address); str_printfa(str, "To: <%s>\r\n", return_addr); str_append(str, "MIME-Version: 1.0\r\n"); str_printfa(str, "Content-Type: " "multipart/report; report-type=%s;\r\n" "\tboundary=\"%s\"\r\n", ctx->dsn ? "delivery-status" : "disposition-notification", boundary); str_append(str, "Subject: "); var_expand(str, ctx->set->rejection_subject, get_var_expand_table(mail, reason, recipient)); str_append(str, "\r\n"); str_append(str, "Auto-Submitted: auto-replied (rejected)\r\n"); str_append(str, "Precedence: bulk\r\n"); str_append(str, "\r\nThis is a MIME-encapsulated message\r\n\r\n"); /* human readable status report */ str_printfa(str, "--%s\r\n", boundary); str_append(str, "Content-Type: text/plain; charset=utf-8\r\n"); str_append(str, "Content-Disposition: inline\r\n"); str_append(str, "Content-Transfer-Encoding: 8bit\r\n\r\n"); var_expand(str, ctx->set->rejection_reason, get_var_expand_table(mail, reason, recipient)); str_append(str, "\r\n"); if (ctx->dsn) { /* DSN status report: For LDA rejects. currently only used when user is out of quota */ str_printfa(str, "--%s\r\n" "Content-Type: message/delivery-status\r\n\r\n", boundary); str_printfa(str, "Reporting-MTA: dns; %s\r\n", ctx->set->hostname); if (mail_get_first_header(mail, "Original-Recipient", &hdr) > 0) str_printfa(str, "Original-Recipient: rfc822; %s\r\n", hdr); str_printfa(str, "Final-Recipient: rfc822; %s\r\n", recipient); str_append(str, "Action: failed\r\n"); str_printfa(str, "Status: %s\r\n", ctx->mailbox_full ? "5.2.2" : "5.2.0"); } else { /* MDN status report: For Sieve "reject" */ str_printfa(str, "--%s\r\n" "Content-Type: message/disposition-notification\r\n\r\n", boundary); str_printfa(str, "Reporting-UA: %s; Dovecot Mail Delivery Agent\r\n", ctx->set->hostname); if (mail_get_first_header(mail, "Original-Recipient", &hdr) > 0) str_printfa(str, "Original-Recipient: rfc822; %s\r\n", hdr); str_printfa(str, "Final-Recipient: rfc822; %s\r\n", recipient); if (orig_msgid != NULL) str_printfa(str, "Original-Message-ID: %s\r\n", orig_msgid); str_append(str, "Disposition: " "automatic-action/MDN-sent-automatically; deleted\r\n"); } str_append(str, "\r\n"); /* original message's headers */ str_printfa(str, "--%s\r\nContent-Type: message/rfc822\r\n\r\n", boundary); o_stream_nsend(output, str_data(str), str_len(str)); if (mail_get_hdr_stream(mail, NULL, &input) == 0) { /* Note: If you add more headers, they need to be sorted. We'll drop Content-Type because we're not including the message body, and having a multipart Content-Type may confuse some MIME parsers when they don't see the message boundaries. */ static const char *const exclude_headers[] = { "Content-Type" }; input = i_stream_create_header_filter(input, HEADER_FILTER_EXCLUDE | HEADER_FILTER_NO_CR | HEADER_FILTER_HIDE_BODY, exclude_headers, N_ELEMENTS(exclude_headers), *null_header_filter_callback, (void *)NULL); ret = o_stream_send_istream(output, input); i_stream_unref(&input); i_assert(ret != 0); } str_truncate(str, 0); str_printfa(str, "\r\n\r\n--%s--\r\n", boundary); o_stream_nsend(output, str_data(str), str_len(str)); return smtp_client_close(smtp_client); }
static int sieve_file_storage_save_to(struct sieve_file_storage *fstorage, string_t *temp_path, struct istream *input, const char *target) { struct sieve_storage *storage = &fstorage->storage; struct ostream *output; int fd; // FIXME: move this to base class // FIXME: use io_stream_temp fd = safe_mkstemp_hostpid (temp_path, fstorage->file_create_mode, (uid_t)-1, (gid_t)-1); if ( fd < 0 ) { if ( errno == EACCES ) { sieve_storage_set_critical(storage, "Failed to create temporary file: %s", eacces_error_get_creating("open", str_c(temp_path))); } else { sieve_storage_set_critical(storage, "Failed to create temporary file: open(%s) failed: %m", str_c(temp_path)); } return -1; } output = o_stream_create_fd(fd, 0); switch ( o_stream_send_istream(output, input) ) { case OSTREAM_SEND_ISTREAM_RESULT_FINISHED: break; case OSTREAM_SEND_ISTREAM_RESULT_WAIT_INPUT: case OSTREAM_SEND_ISTREAM_RESULT_WAIT_OUTPUT: i_unreached(); case OSTREAM_SEND_ISTREAM_RESULT_ERROR_INPUT: sieve_storage_set_critical(storage, "read(%s) failed: %s", i_stream_get_name(input), i_stream_get_error(input)); o_stream_destroy(&output); i_unlink(str_c(temp_path)); return -1; case OSTREAM_SEND_ISTREAM_RESULT_ERROR_OUTPUT: sieve_storage_set_critical(storage, "write(%s) failed: %s", str_c(temp_path), o_stream_get_error(output)); o_stream_destroy(&output); i_unlink(str_c(temp_path)); return -1; } o_stream_destroy(&output); if ( rename(str_c(temp_path), target) < 0 ) { if ( ENOQUOTA(errno) ) { sieve_storage_set_error(storage, SIEVE_ERROR_NO_QUOTA, "Not enough disk quota"); } else if ( errno == EACCES ) { sieve_storage_set_critical(storage, "%s", eacces_error_get("rename", target)); } else { sieve_storage_set_critical(storage, "rename(%s, %s) failed: %m", str_c(temp_path), target); } i_unlink(str_c(temp_path)); } return 0; }