bool client_handle_unfinished_cmd(struct client_command_context *cmd) { if (cmd->state == CLIENT_COMMAND_STATE_WAIT_INPUT) { /* need more input */ return FALSE; } if (cmd->state != CLIENT_COMMAND_STATE_WAIT_OUTPUT) { /* waiting for something */ if (cmd->state == CLIENT_COMMAND_STATE_WAIT_SYNC) { /* this is mainly for APPEND. */ client_add_missing_io(cmd->client); } return TRUE; } /* output is blocking, we can execute more commands */ o_stream_set_flush_pending(cmd->client->output, TRUE); if (cmd->client->to_idle_output == NULL) { /* disconnect sooner if client isn't reading our output */ cmd->client->to_idle_output = timeout_add(CLIENT_OUTPUT_TIMEOUT_MSECS, client_idle_output_timeout, cmd->client); } return TRUE; }
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 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; }
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); } }
static int replicator_output(struct replicator_connection *conn) { enum replication_priority p; if (o_stream_flush(conn->output) < 0) { replicator_connection_disconnect(conn); return 1; } for (p = REPLICATION_PRIORITY_SYNC;;) { if (o_stream_get_buffer_used_size(conn->output) > 0) { o_stream_set_flush_pending(conn->output, TRUE); break; } /* output buffer is empty, send more data */ if (conn->queue[p]->used > 0) { if (!replicator_send_buf(conn, conn->queue[p])) break; } else { if (p == REPLICATION_PRIORITY_LOW) break; p--; } } return 1; }
static void o_stream_ssl_flush_pending(struct ostream_private *_stream, bool set) { struct ssl_ostream *sstream = (struct ssl_ostream *)_stream; o_stream_set_flush_pending(sstream->ssl_io->plain_output, set); }
static int o_stream_ssl_flush(struct ostream_private *stream) { struct ssl_ostream *sstream = (struct ssl_ostream *)stream; int ret; if ((ret = openssl_iostream_more(sstream->ssl_io)) < 0) { /* handshake failed */ io_stream_set_error(&stream->iostream, "%s", sstream->ssl_io->last_error); stream->ostream.stream_errno = errno; } else if (ret > 0 && sstream->buffer != NULL && sstream->buffer->used > 0) { /* we can try to send some of our buffered data */ ret = o_stream_ssl_flush_buffer(sstream); } if (ret == 0 && sstream->ssl_io->want_read) { /* we need to read more data until we can continue. */ o_stream_set_flush_pending(sstream->ssl_io->plain_output, FALSE); sstream->ssl_io->ostream_flush_waiting_input = TRUE; ret = 1; } return ret; }
static bool openssl_iostream_bio_input(struct ssl_iostream *ssl_io) { const unsigned char *data; size_t bytes, size; int ret; bool bytes_read = FALSE; while ((bytes = BIO_ctrl_get_write_guarantee(ssl_io->bio_ext)) > 0) { /* bytes contains how many bytes we can write to bio_ext */ ssl_io->plain_input->real_stream->try_alloc_limit = bytes; ret = openssl_iostream_read_more(ssl_io, &data, &size); ssl_io->plain_input->real_stream->try_alloc_limit = 0; if (ret == -1 && size == 0 && !bytes_read) { ssl_io->plain_stream_errno = ssl_io->plain_input->stream_errno; ssl_io->closed = TRUE; return FALSE; } if (size == 0) { /* wait for more input */ break; } if (size > bytes) size = bytes; ret = BIO_write(ssl_io->bio_ext, data, size); i_assert(ret == (ssize_t)size); i_stream_skip(ssl_io->plain_input, size); bytes_read = TRUE; } if (bytes == 0 && !bytes_read && ssl_io->want_read) { /* shouldn't happen */ i_error("SSL BIO buffer size too small"); ssl_io->plain_stream_errno = EINVAL; ssl_io->closed = TRUE; return FALSE; } if (i_stream_get_data_size(ssl_io->plain_input) > 0) { i_error("SSL: Too much data in buffered plain input buffer"); ssl_io->plain_stream_errno = EINVAL; ssl_io->closed = TRUE; return FALSE; } if (bytes_read) { if (ssl_io->ostream_flush_waiting_input) { ssl_io->ostream_flush_waiting_input = FALSE; o_stream_set_flush_pending(ssl_io->plain_output, TRUE); } ssl_io->want_read = FALSE; } return bytes_read; }
static size_t o_stream_ssl_buffer(struct ssl_ostream *sstream, const struct const_iovec *iov, unsigned int iov_count, size_t bytes_sent) { size_t avail, skip_left, size; unsigned int i; if (sstream->buffer == NULL) sstream->buffer = buffer_create_dynamic(default_pool, 4096); skip_left = bytes_sent; for (i = 0; i < iov_count; i++) { if (skip_left < iov[i].iov_len) break; skip_left -= iov[i].iov_len; } if (sstream->ostream.max_buffer_size == 0) { /* we're requeted to use whatever space is available in the buffer */ avail = buffer_get_size(sstream->buffer) - sstream->buffer->used; } else { avail = sstream->ostream.max_buffer_size > sstream->buffer->used ? sstream->ostream.max_buffer_size - sstream->buffer->used : 0; } if (i < iov_count && skip_left > 0) { size = I_MIN(iov[i].iov_len - skip_left, avail); buffer_append(sstream->buffer, CONST_PTR_OFFSET(iov[i].iov_base, skip_left), size); bytes_sent += size; avail -= size; if (size != iov[i].iov_len) i = iov_count; } if (avail > 0) o_stream_set_flush_pending(sstream->ssl_io->plain_output, TRUE); for (; i < iov_count; i++) { size = I_MIN(iov[i].iov_len, avail); buffer_append(sstream->buffer, iov[i].iov_base, size); bytes_sent += size; avail -= size; if (size != iov[i].iov_len) break; } sstream->ostream.ostream.offset += bytes_sent; return bytes_sent; }
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 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; }
static void replicator_send(struct replicator_connection *conn, enum replication_priority priority, const char *data) { unsigned int data_len = strlen(data); if (conn->fd != -1 && o_stream_get_buffer_used_size(conn->output) == 0) { /* we can send data immediately */ o_stream_nsend(conn->output, data, data_len); } else if (conn->queue[priority]->used + data_len >= REPLICATOR_MEMBUF_MAX_SIZE) { /* FIXME: compress duplicates, start writing to file */ } else { /* queue internally to separate queues */ buffer_append(conn->queue[priority], data, data_len); if (conn->output != NULL) o_stream_set_flush_pending(conn->output, TRUE); } }
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; }
void iostream_pump_start(struct iostream_pump *pump) { i_assert(pump != NULL); i_assert(pump->callback != NULL); /* add flush handler */ if (!pump->output->blocking) { o_stream_set_flush_callback(pump->output, iostream_pump_flush, pump); } /* make IO objects */ if (pump->input->blocking) { i_assert(!pump->output->blocking); o_stream_set_flush_pending(pump->output, TRUE); } else { pump->io = io_add_istream(pump->input, iostream_pump_copy, pump); io_set_pending(pump->io); } }
static int plain_flush_callback(struct ssl_ostream *sstream) { struct ostream *ostream = &sstream->ostream.ostream; int ret, ret2; /* try to actually flush the pending data */ if ((ret = o_stream_flush(sstream->ssl_io->plain_output)) < 0) return -1; /* we may be able to copy more data, try it */ o_stream_ref(ostream); if (sstream->ostream.callback != NULL) ret2 = sstream->ostream.callback(sstream->ostream.context); else ret2 = o_stream_flush(&sstream->ostream.ostream); if (ret2 == 0) o_stream_set_flush_pending(sstream->ssl_io->plain_output, TRUE); o_stream_unref(&ostream); if (ret2 < 0) return -1; return ret > 0 && ret2 > 0 ? 1 : 0; }
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; }
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 void o_stream_default_set_flush_pending(struct ostream_private *_stream, bool set) { if (_stream->parent != NULL) o_stream_set_flush_pending(_stream->parent, set); }
static void 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; while ((ret = i_stream_read_data(client->data_input, &data, &size, 0)) > 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; } } if (add != '\0') { if (o_stream_send(client->output, &add, 1) < 0) break; client->output_last = add; } } 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; } 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; }
static int http_client_request_continue_payload(struct http_client_request **_req, const unsigned char *data, size_t size) { struct ioloop *prev_ioloop = current_ioloop; struct http_client_request *req = *_req; struct http_client_connection *conn = req->conn; struct http_client *client = req->client; int ret; i_assert(req->state == HTTP_REQUEST_STATE_NEW || req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT); i_assert(req->payload_input == NULL); if (conn != NULL) http_client_connection_ref(conn); http_client_request_ref(req); req->payload_wait = TRUE; if (data == NULL) { req->payload_input = NULL; if (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT) http_client_request_finish_payload_out(req); } else { req->payload_input = i_stream_create_from_data(data, size); i_stream_set_name(req->payload_input, "<HTTP request payload>"); } req->payload_size = 0; req->payload_chunked = TRUE; if (req->state == HTTP_REQUEST_STATE_NEW) http_client_request_submit(req); /* Wait for payload data to be written */ i_assert(client->ioloop == NULL); client->ioloop = io_loop_create(); http_client_switch_ioloop(client); if (client->set.dns_client != NULL) dns_client_switch_ioloop(client->set.dns_client); while (req->state < HTTP_REQUEST_STATE_PAYLOAD_IN) { http_client_request_debug(req, "Waiting for request to finish"); if (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT) o_stream_set_flush_pending(req->payload_output, TRUE); io_loop_run(client->ioloop); if (req->state == HTTP_REQUEST_STATE_PAYLOAD_OUT && req->payload_input->eof) { i_stream_unref(&req->payload_input); req->payload_input = NULL; break; } } io_loop_set_current(prev_ioloop); http_client_switch_ioloop(client); if (client->set.dns_client != NULL) dns_client_switch_ioloop(client->set.dns_client); io_loop_set_current(client->ioloop); io_loop_destroy(&client->ioloop); switch (req->state) { case HTTP_REQUEST_STATE_PAYLOAD_IN: case HTTP_REQUEST_STATE_FINISHED: ret = 1; break; case HTTP_REQUEST_STATE_ABORTED: ret = -1; break; default: ret = 0; break; } req->payload_wait = FALSE; /* callback may have messed with our pointer, so unref using local variable */ if (!http_client_request_unref(&req)) *_req = NULL; if (conn != NULL) http_client_connection_unref(&conn); /* Return status */ return ret; }
static int client_fetch_url(struct client *client, const char *url, enum imap_urlauth_fetch_flags url_flags) { string_t *response; const char *bpstruct, *errormsg; bool binary_with_nuls; int ret; i_assert(client->url == NULL); client->msg_part_size = 0; client->msg_part_input = NULL; if (client->debug) i_debug("Fetching URLAUTH %s", url); /* fetch URL */ ret = client_fetch_urlpart(client, url, url_flags, &bpstruct, &binary_with_nuls, &errormsg); if (ret <= 0) { /* fetch failed */ if (client->url != NULL) imap_msgpart_url_free(&client->url); /* don't send error details to anonymous users: just to be sure that no information about the target user account is unduly leaked. */ if (client->access_anonymous || errormsg == NULL) client_send_line(client, "NO"); else { client_send_line(client, "NO\terror=%s", str_tabescape(errormsg)); } if (ret < 0) { /* fetch failed badly */ client_abort(client, "Session aborted: Fatal failure while fetching URL"); } return 0; } response = t_str_new(256); str_append(response, "OK"); if (binary_with_nuls) str_append(response, "\thasnuls"); if (bpstruct != NULL) { str_append(response, "\tbpstruct="); str_append(response, str_tabescape(bpstruct)); if (client->debug) { i_debug("Fetched URLAUTH yielded BODYPARTSTRUCTURE (%s)", bpstruct); } } /* return content */ o_stream_cork(client->output); if (client->msg_part_size == 0 || client->msg_part_input == NULL) { /* empty */ str_append(response, "\t0"); client_send_line(client, "%s", str_c(response)); imap_msgpart_url_free(&client->url); client->url = NULL; if (client->debug) i_debug("Fetched URLAUTH yielded empty result"); } else { /* actual content */ str_printfa(response, "\t%"PRIuUOFF_T, client->msg_part_size); client_send_line(client, "%s", str_c(response)); if (client->debug) { i_debug("Fetched URLAUTH yielded %"PRIuUOFF_T" bytes " "of %smessage data", client->msg_part_size, (binary_with_nuls ? "binary " : "")); } if (client_run_url(client) < 0) { client_abort(client, "Session aborted: Fatal failure while transfering URL"); return 0; } } if (client->url != NULL) { /* URL not finished */ o_stream_set_flush_pending(client->output, TRUE); client->waiting_input = TRUE; } o_stream_uncork(client->output); return client->url != NULL ? 0 : 1; }