static void lmtp_client_input(struct lmtp_client *client) { const char *line; int ret; lmtp_client_ref(client); o_stream_cork(client->output); while ((line = i_stream_read_next_line(client->input)) != NULL) { T_BEGIN { ret = lmtp_client_input_line(client, line); } T_END; if (ret < 0) { o_stream_uncork(client->output); lmtp_client_unref(&client); return; } if (ret > 0) str_truncate(client->input_multiline, 0); } if (client->input->stream_errno == ENOBUFS) { lmtp_client_fail(client, "501 5.5.4 Command reply line too long"); } else if (client->input->stream_errno != 0) { errno = client->input->stream_errno; i_error("lmtp client: read() failed: %m"); lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (read failure)"); } else if (client->input->eof) { lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (disconnected in input)"); } o_stream_uncork(client->output); lmtp_client_unref(&client); }
static void idle_finish(struct cmd_idle_context *ctx, bool done_ok, bool free_cmd) { struct client *client = ctx->client; if (ctx->keepalive_to != NULL) timeout_remove(&ctx->keepalive_to); if (ctx->sync_ctx != NULL) { /* we're here only in connection failure cases */ (void)imap_sync_deinit(ctx->sync_ctx, ctx->cmd); } o_stream_cork(client->output); if (client->io != NULL) io_remove(&client->io); if (client->mailbox != NULL) mailbox_notify_changes_stop(client->mailbox); if (done_ok) client_send_tagline(ctx->cmd, "OK Idle completed."); else client_send_tagline(ctx->cmd, "BAD Expected DONE."); o_stream_uncork(client->output); if (free_cmd) client_command_free(&ctx->cmd); }
client_auth_result(struct client *client, enum client_auth_result result, const struct client_auth_reply *reply, const char *text) { o_stream_cork(client->output); client->v.auth_result(client, result, reply, text); o_stream_uncork(client->output); }
static void server_input(struct login_proxy *proxy) { unsigned char buf[OUTBUF_THRESHOLD]; ssize_t ret, ret2; proxy->last_io = ioloop_time; if (o_stream_get_buffer_used_size(proxy->client_output) > OUTBUF_THRESHOLD) { /* client's output buffer is already quite full. don't send more until we're below threshold. */ io_remove(&proxy->server_io); return; } ret = net_receive(proxy->server_fd, buf, sizeof(buf)); if (ret < 0) { login_proxy_free_errno(&proxy, errno, TRUE); return; } o_stream_cork(proxy->client_output); ret2 = o_stream_send(proxy->client_output, buf, ret); o_stream_uncork(proxy->client_output); if (ret2 != ret) login_proxy_free_ostream(&proxy, proxy->client_output, FALSE); }
static void client_input(struct client *client) { const char *const *args, *error; int ret; if (client->to_pending != NULL) timeout_remove(&client->to_pending); switch (i_stream_read(client->input)) { case -2: i_error("BUG: Stats client sent too much data"); client_destroy(&client); return; case -1: client_destroy(&client); return; } o_stream_cork(client->output); while ((args = client_read_next_line(client)) != NULL) { ret = client_handle_request(client, args, &error); if (ret < 0) { i_error("Stats client input error: %s", error); client_destroy(&client); return; } if (ret == 0) { o_stream_set_flush_pending(client->output, TRUE); io_remove(&client->io); break; } client->cmd_more = NULL; } o_stream_uncork(client->output); }
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_data(proxy->client_input, &data, &size, 0) < 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); }
void client_cmd_starttls(struct client *client) { if (client->tls) { client->v.notify_starttls(client, FALSE, "TLS is already active."); return; } if (!client_is_tls_enabled(client)) { client->v.notify_starttls(client, FALSE, "TLS support isn't enabled."); return; } /* remove input handler, SSL proxy gives us a new fd. we also have to remove it in case we have to wait for buffer to be flushed */ if (client->io != NULL) io_remove(&client->io); client->v.notify_starttls(client, TRUE, "Begin TLS negotiation now."); /* uncork the old fd */ o_stream_uncork(client->output); if (o_stream_flush(client->output) <= 0) { /* the buffer has to be flushed */ o_stream_set_flush_pending(client->output, TRUE); o_stream_set_flush_callback(client->output, client_output_starttls, client); } else { client_start_tls(client); } }
int client_output(struct client *client) { int ret; i_assert(!client->destroyed); client->last_output = ioloop_time; timeout_reset(client->to_idle); if (client->to_idle_output != NULL) timeout_reset(client->to_idle_output); o_stream_cork(client->output); if ((ret = o_stream_flush(client->output)) < 0) { client_destroy(client, NULL); return 1; } client_output_commands(client); (void)cmd_sync_delayed(client); o_stream_uncork(client->output); imap_refresh_proctitle(); if (client->disconnected) client_destroy(client, NULL); else client_continue_pending_input(client); return ret; }
static void client_input_append(struct client_command_context *cmd) { struct cmd_append_context *ctx = cmd->context; struct client *client = cmd->client; const char *reason; bool finished; uoff_t lit_offset; i_assert(!client->destroyed); client->last_input = ioloop_time; timeout_reset(client->to_idle); switch (i_stream_read(client->input)) { case -1: /* disconnected */ lit_offset = ctx->litinput == NULL ? 0 : ctx->litinput->v_offset; reason = get_disconnect_reason(ctx, lit_offset); cmd_append_finish(cmd->context); /* Reset command so that client_destroy() doesn't try to call cmd_append_continue_message() anymore. */ client_command_free(&cmd); client_destroy(client, reason); return; case -2: if (ctx->message_input) { /* message data, this is handled internally by mailbox_save_continue() */ break; } cmd_append_finish(cmd->context); /* parameter word is longer than max. input buffer size. this is most likely an error, so skip the new data until newline is found. */ client->input_skip_line = TRUE; if (!ctx->failed) client_send_command_error(cmd, "Too long argument."); cmd->param_error = TRUE; client_command_free(&cmd); return; } o_stream_cork(client->output); finished = command_exec(cmd); if (!finished) (void)client_handle_unfinished_cmd(cmd); else client_command_free(&cmd); cmd_sync_delayed(client); o_stream_uncork(client->output); if (client->disconnected) client_destroy(client, NULL); else client_continue_pending_input(client); }
void lmtp_client_send_more(struct lmtp_client *client) { if (client->input_state == LMTP_INPUT_STATE_DATA) { o_stream_cork(client->output); lmtp_client_send_data(client); o_stream_uncork(client->output); } }
static void proxy_input(struct client *client) { struct istream *input; struct ostream *output; const char *line; unsigned int duration; if (client->login_proxy == NULL) { /* we're just freeing the proxy */ return; } input = login_proxy_get_istream(client->login_proxy); if (input == NULL) { if (client->destroyed) { /* we came here from client_destroy() */ return; } /* failed for some reason, probably server disconnected */ client_proxy_failed(client, TRUE); return; } i_assert(!client->destroyed); switch (i_stream_read(input)) { case -2: client_log_err(client, "proxy: Remote input buffer full"); client_proxy_failed(client, TRUE); return; case -1: line = i_stream_next_line(input); duration = ioloop_time - client->created; client_log_err(client, t_strdup_printf( "proxy: Remote %s:%u disconnected: %s " "(state=%u, duration=%us)%s", login_proxy_get_host(client->login_proxy), login_proxy_get_port(client->login_proxy), get_disconnect_reason(input), client->proxy_state, duration, line == NULL ? "" : t_strdup_printf( " - BUG: line not read: %s", line))); client_proxy_failed(client, TRUE); return; } output = client->output; o_stream_ref(output); o_stream_cork(output); while ((line = i_stream_next_line(input)) != NULL) { if (client->v.proxy_parse_line(client, line) != 0) break; } o_stream_uncork(output); o_stream_unref(&output); }
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 cmd_setmetadata_entry(struct imap_setmetadata_context *ctx, const char *entry_name, const struct imap_arg *entry_value) { struct istream *inputs[2]; struct mail_attribute_value value; string_t *path; int ret; switch (entry_value->type) { case IMAP_ARG_NIL: case IMAP_ARG_ATOM: case IMAP_ARG_STRING: /* we have the value already */ if (ctx->failed) return 1; memset(&value, 0, sizeof(value)); value.value = imap_arg_as_nstring(entry_value); ret = imap_metadata_set(ctx->trans, entry_name, &value); if (ret < 0) { /* delay reporting the failure so we'll finish reading the command input */ ctx->storage_failure = TRUE; ctx->failed = TRUE; } return 1; case IMAP_ARG_LITERAL_SIZE: o_stream_nsend(ctx->cmd->client->output, "+ OK\r\n", 6); o_stream_nflush(ctx->cmd->client->output); o_stream_uncork(ctx->cmd->client->output); o_stream_cork(ctx->cmd->client->output); /* fall through */ case IMAP_ARG_LITERAL_SIZE_NONSYNC: i_free(ctx->entry_name); ctx->entry_name = i_strdup(entry_name); ctx->entry_value_len = imap_arg_as_literal_size(entry_value); inputs[0] = i_stream_create_limit(ctx->cmd->client->input, ctx->entry_value_len); inputs[1] = NULL; path = t_str_new(128); mail_user_set_get_temp_prefix(path, ctx->cmd->client->user->set); ctx->input = i_stream_create_seekable_path(inputs, METADATA_MAX_INMEM_SIZE, str_c(path)); i_stream_set_name(ctx->input, i_stream_get_name(inputs[0])); i_stream_unref(&inputs[0]); return cmd_setmetadata_entry_read_stream(ctx); case IMAP_ARG_LITERAL: case IMAP_ARG_LIST: case IMAP_ARG_EOL: break; } i_unreached(); }
static void o_stream_zlib_cork(struct ostream_private *stream, bool set) { struct zlib_ostream *zstream = (struct zlib_ostream *)stream; stream->corked = set; if (set) o_stream_cork(zstream->output); else { (void)o_stream_flush(&stream->ostream); o_stream_uncork(zstream->output); } }
static void o_stream_default_cork(struct ostream_private *_stream, bool set) { _stream->corked = set; if (set) { if (_stream->parent != NULL) o_stream_cork(_stream->parent); } else { (void)o_stream_flush(&_stream->ostream); if (_stream->parent != NULL) o_stream_uncork(_stream->parent); } }
static int server_connection_output(struct server_connection *conn) { int ret; o_stream_cork(conn->output); ret = o_stream_flush(conn->output); if (ret > 0 && conn->cmd_input != NULL && conn->delayed_cmd == NULL) ret = server_connection_send_cmd_input_more(conn); o_stream_uncork(conn->output); if (ret < 0) server_connection_destroy(&conn); return ret; }
static bool cmd_append_send_literal_continue(struct cmd_append_context *ctx) { if (ctx->failed) { /* tagline was already sent, we can abort here */ return FALSE; } o_stream_nsend(ctx->client->output, "+ OK\r\n", 6); o_stream_nflush(ctx->client->output); o_stream_uncork(ctx->client->output); o_stream_cork(ctx->client->output); return TRUE; }
static bool openssl_iostream_bio_output(struct ssl_iostream *ssl_io) { size_t bytes, max_bytes; ssize_t sent; unsigned char buffer[IO_BLOCK_SIZE]; bool bytes_sent = FALSE; int ret; o_stream_cork(ssl_io->plain_output); while ((bytes = BIO_ctrl_pending(ssl_io->bio_ext)) > 0) { /* bytes contains how many SSL encrypted bytes we should be sending out */ max_bytes = o_stream_get_buffer_avail_size(ssl_io->plain_output); if (bytes > max_bytes) { if (max_bytes == 0) { /* wait until output buffer clears */ o_stream_set_flush_pending(ssl_io->plain_output, TRUE); break; } bytes = max_bytes; } if (bytes > sizeof(buffer)) bytes = sizeof(buffer); /* BIO_read() is guaranteed to return all the bytes that BIO_ctrl_pending() returned */ ret = BIO_read(ssl_io->bio_ext, buffer, bytes); i_assert(ret == (int)bytes); /* we limited number of read bytes to plain_output's available size. this send() is guaranteed to either fully succeed or completely fail due to some error. */ sent = o_stream_send(ssl_io->plain_output, buffer, bytes); if (sent < 0) { i_assert(ssl_io->plain_output->closed || ssl_io->plain_output->stream_errno != 0); i_free(ssl_io->plain_stream_errstr); ssl_io->plain_stream_errstr = i_strdup(o_stream_get_error(ssl_io->plain_output)); ssl_io->plain_stream_errno = ssl_io->plain_output->stream_errno; ssl_io->closed = TRUE; break; } i_assert(sent == (ssize_t)bytes); bytes_sent = TRUE; } o_stream_uncork(ssl_io->plain_output); return bytes_sent; }
static void rawlog_proxy_destroy(struct rawlog_proxy *proxy) { if (proxy->in_output != NULL) { o_stream_uncork(proxy->in_output); if (o_stream_nfinish(proxy->in_output) < 0) { i_error("write(in) failed: %s", o_stream_get_error(proxy->in_output)); } o_stream_destroy(&proxy->in_output); } if (proxy->out_output != NULL) { o_stream_uncork(proxy->out_output); if (o_stream_nfinish(proxy->out_output) < 0) { i_error("write(out) failed: %s", o_stream_get_error(proxy->out_output)); } o_stream_destroy(&proxy->out_output); } if (proxy->client_io != NULL) io_remove(&proxy->client_io); if (proxy->server_io != NULL) io_remove(&proxy->server_io); if (proxy->to_flush != NULL) timeout_remove(&proxy->to_flush); o_stream_destroy(&proxy->client_output); o_stream_destroy(&proxy->server_output); if (close(proxy->client_in_fd) < 0) i_error("close(client_in_fd) failed: %m"); if (close(proxy->client_out_fd) < 0) i_error("close(client_out_fd) failed: %m"); if (close(proxy->server_fd) < 0) i_error("close(server_fd) failed: %m"); i_free(proxy); io_loop_stop(ioloop); }
static int lmtp_client_output(struct lmtp_client *client) { int ret; lmtp_client_ref(client); o_stream_cork(client->output); if ((ret = o_stream_flush(client->output)) < 0) lmtp_client_fail(client, ERRSTR_TEMP_REMOTE_FAILURE " (disconnected in output)"); else if (client->input_state == LMTP_INPUT_STATE_DATA) lmtp_client_send_data(client); o_stream_uncork(client->output); lmtp_client_unref(&client); return ret; }
static void client_add_input(struct client *client, const buffer_t *buf) { struct ostream *output; if (buf != NULL && buf->used > 0) { if (!i_stream_add_data(client->input, buf->data, buf->used)) i_panic("Couldn't add client input to stream"); } output = client->output; o_stream_ref(output); o_stream_cork(output); (void)client_handle_input(client); o_stream_uncork(output); o_stream_unref(&output); }
static void dns_client_input(struct dns_client *client) { const char *line; int ret = 0; o_stream_cork(client->output); while ((line = i_stream_read_next_line(client->input)) != NULL) { if (dns_client_input_line(client, line) < 0) { ret = -1; break; } } o_stream_uncork(client->output); if (client->input->eof || client->input->stream_errno != 0 || ret < 0) dns_client_destroy(&client); }
static void test_ostream_file_random(void) { struct ostream *output; string_t *path = t_str_new(128); char buf[MAX_BUFSIZE*4], buf2[MAX_BUFSIZE*4], randbuf[MAX_BUFSIZE]; unsigned int i, offset, size; ssize_t ret; int fd; memset(buf, 0, sizeof(buf)); fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1); if (fd == -1) i_fatal("safe_mkstemp(%s) failed: %m", str_c(path)); if (unlink(str_c(path)) < 0) i_fatal("unlink(%s) failed: %m", str_c(path)); output = o_stream_create_fd(fd, MAX_BUFSIZE, FALSE); o_stream_cork(output); size = (rand() % MAX_BUFSIZE) + 1; random_fill_weak(randbuf, size); memcpy(buf, randbuf, size); test_assert(o_stream_send(output, buf, size) > 0); for (i = 0; i < 10; i++) { offset = rand() % (MAX_BUFSIZE*3); size = (rand() % MAX_BUFSIZE) + 1; random_fill_weak(randbuf, size); memcpy(buf + offset, randbuf, size); test_assert(o_stream_pwrite(output, randbuf, size, offset) == 0); if (rand() % 10 == 0) test_assert(o_stream_flush(output) > 0); } test_assert(o_stream_flush(output) > 0); o_stream_uncork(output); ret = pread(fd, buf2, sizeof(buf2), 0); if (ret < 0) i_fatal("pread() failed: %m"); else { i_assert(ret > 0); test_assert(memcmp(buf, buf2, ret) == 0); } o_stream_unref(&output); i_close_fd(&fd); }
void client_input(struct client *client) { struct client_command_context *cmd; struct ostream *output = client->output; ssize_t bytes; i_assert(client->io != NULL); client->last_input = ioloop_time; timeout_reset(client->to_idle); if (client->to_delayed_input != NULL) timeout_remove(&client->to_delayed_input); bytes = i_stream_read(client->input); if (bytes == -1) { /* disconnected */ client_destroy(client, NULL); return; } o_stream_ref(output); o_stream_cork(output); if (!client_handle_input(client) && bytes == -2) { /* parameter word is longer than max. input buffer size. this is most likely an error, so skip the new data until newline is found. */ client->input_skip_line = TRUE; cmd = client->input_lock != NULL ? client->input_lock : client_command_new(client); cmd->param_error = TRUE; client_send_command_error(cmd, "Too long argument."); client_command_free(&cmd); } o_stream_uncork(output); o_stream_unref(&output); imap_refresh_proctitle(); if (client->disconnected) client_destroy(client, NULL); else client_continue_pending_input(client); }
void client_disconnect(struct client *client, const char *reason) { i_assert(reason != NULL); if (client->disconnected) return; i_info("Disconnected: %s %s", reason, client_stats(client)); client->disconnected = TRUE; o_stream_nflush(client->output); o_stream_uncork(client->output); i_stream_close(client->input); o_stream_close(client->output); if (client->to_idle != NULL) timeout_remove(&client->to_idle); client->to_idle = timeout_add(0, client_destroy_timeout, client); }
static int client_output(struct client *client) { int ret = 1; o_stream_cork(client->output); if (o_stream_flush(client->output) < 0) { client_destroy(&client); return 1; } if (client->cmd_more != NULL) ret = client->cmd_more(client); o_stream_uncork(client->output); if (ret > 0) { client->cmd_more = NULL; if (client->io == NULL) client_enable_io(client); } return ret; }
void connection_input_default(struct connection *conn) { const char *line; struct istream *input; struct ostream *output; int ret = 0; switch (connection_input_read(conn)) { case -1: return; case 0: /* allow calling this function for buffered input */ case 1: break; default: i_unreached(); } input = conn->input; output = conn->output; i_stream_ref(input); if (output != NULL) { o_stream_ref(output); o_stream_cork(output); } while (!input->closed && (line = i_stream_next_line(input)) != NULL) { T_BEGIN { ret = conn->list->v.input_line(conn, line); } T_END; if (ret <= 0) break; } if (output != NULL) { o_stream_uncork(output); o_stream_unref(&output); } if (ret < 0 && !input->closed) { conn->disconnect_reason = CONNECTION_DISCONNECT_DEINIT; conn->list->v.destroy(conn); } i_stream_unref(&input); }
static void client_add_input(struct client *client, const buffer_t *buf) { struct ostream *output; struct client_input input; if (buf != NULL && buf->used > 0) { client_parse_input(buf->data, buf->used, &input); if (input.input_size > 0 && !i_stream_add_data(client->input, input.input, input.input_size)) i_panic("Couldn't add client input to stream"); } else { /* IMAPLOGINTAG environment is compatible with mailfront */ memset(&input, 0, sizeof(input)); input.tag = getenv("IMAPLOGINTAG"); } output = client->output; o_stream_ref(output); o_stream_cork(output); if (input.tag == NULL) { client_send_line(client, t_strconcat( "* PREAUTH [CAPABILITY ", str_c(client->capability_string), "] " "Logged in as ", client->user->username, NULL)); } else if (input.send_untagged_capability) { /* client doesn't seem to understand tagged capabilities. send untagged instead and hope that it works. */ client_send_line(client, t_strconcat("* CAPABILITY ", str_c(client->capability_string), NULL)); client_send_line(client, t_strconcat(input.tag, " OK Logged in", NULL)); } else { client_send_line(client, t_strconcat( input.tag, " OK [CAPABILITY ", str_c(client->capability_string), "] Logged in", NULL)); } (void)client_handle_input(client); o_stream_uncork(output); o_stream_unref(&output); }
static void keepalive_timeout(struct cmd_idle_context *ctx) { if (ctx->client->output_cmd_lock != NULL) { /* it's busy sending output */ return; } if (o_stream_get_buffer_used_size(ctx->client->output) == 0) { /* Sending this keeps NATs/stateful firewalls alive. Sending this also catches dead connections. Don't send anything if there is already data waiting in output buffer. */ o_stream_cork(ctx->client->output); client_send_line(ctx->client, "* OK Still here"); o_stream_uncork(ctx->client->output); } /* Make sure idling connections don't get disconnected. There are several clients that really want to IDLE forever and there's not much harm in letting them do so. */ timeout_reset(ctx->client->to_idle); /* recalculate time for the next keepalive timeout */ idle_add_keepalive_timeout(ctx); }
static int client_output(struct client *client) { o_stream_cork(client->output); if (o_stream_flush(client->output) < 0) { if (client->ctrl_output != NULL) (void)o_stream_send_str(client->ctrl_output, "DISCONNECTED\n"); client_destroy(client); return 1; } timeout_reset(client->to_idle); if (client->url != NULL) { if (client_run_url(client) < 0) { client_destroy(client); return 1; } if (client->url == NULL && client->waiting_input) { if (!client_handle_input(client)) { /* client got destroyed */ return 1; } } } o_stream_uncork(client->output); if (client->url != NULL) { /* url not finished yet */ return 0; } else if (client->io == NULL) { /* data still in output buffer, get back here to add IO */ return 0; } else { return 1; } }