client.c revision c979eeda1f46483d9c963e265786b701d7683d77
bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (C) 2002 Timo Sirainen */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen/* max. length of input command line (spec says 512) */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen/* Stop reading input when output buffer has this many bytes. Once the buffer
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen size has dropped to half of it, start reading input again. */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen/* If we can't send anything for 10 minutes, disconnect the client */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen/* Disconnect client when it sends too many bad commands in a row */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen/* Disconnect client after idling this many seconds */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic struct client *my_client; /* we don't need more than one currently */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic void client_input(struct client *client);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic int client_output(struct client *client);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenstatic int sync_mailbox(struct mailbox *box, struct mailbox_status *status)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen ctx = mailbox_sync_init(box, MAILBOX_SYNC_FLAG_FULL_READ);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return mailbox_sync_deinit(&ctx, STATUS_UIDVALIDITY, status);
091a2dea9d89734a7c1225eed511b3851693a757Timo Sirainen message_sizes_buf = buffer_create_dynamic(default_pool, 512);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen for (i = 0; i < 2; i++) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if (sync_mailbox(client->mailbox, &status) < 0) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen t = mailbox_transaction_begin(client->mailbox, 0);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen ctx = mailbox_search_init(t, NULL, &search_arg, NULL);
15dd18994a8a4933e39e2f87233255d0ca82ba3eTimo Sirainen mail = mail_alloc(t, MAIL_FETCH_VIRTUAL_SIZE, NULL);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen buffer_append(message_sizes_buf, &size, sizeof(size));
a5b64f1abb1cb0a9718d5bf7f0ae808072237259Timo Sirainen /* well, sync and try again. we might have cached virtual
a5b64f1abb1cb0a9718d5bf7f0ae808072237259Timo Sirainen sizes, make sure they get committed. */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen client_send_line(client, "-ERR [IN-USE] Couldn't sync mailbox.");
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Boschstruct client *client_create(int fd_in, int fd_out,
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch /* always use nonblocking I/O */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen client->input = i_stream_create_fd(fd_in, MAX_INBUF_SIZE, FALSE);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen o_stream_set_flush_callback(client->output, client_output, client);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen client->io = io_add(fd_in, IO_READ, client_input, client);
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen client->inbox_ns = mail_namespace_find(namespaces, &inbox);
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi client_send_line(client, "-ERR No INBOX namespace for user.");
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi client_destroy(client, "No INBOX namespace for user.");
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi client->mailbox = mailbox_open(storage, "INBOX", NULL, flags);
fa33df8230c2f27ae863ff83d4251923428d53c7Aki Tuomi errmsg = t_strdup_printf("Couldn't open INBOX: %s",
d48e40d6c77d673ad402d96571198d1cce4da225Timo Sirainen client_send_line(client, "-ERR [IN-USE] %s", errmsg);
086b73efd1a5812a64acc951366a499d325509a6Stephan Bosch mail_storage_get_last_error(storage, &error));
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen client_destroy(client, "Mailbox init failed");
ed567dac7e55ab3e8dd53d9c86c31ebb2032d4dfTimo Sirainenstatic const char *client_stats(struct client *client)
c85f67cfb8e1cef2de2b681debf4703d5818dc01Stephan Bosch static struct var_expand_table static_tab[] = {
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch tab[4].value = dec2str(client->expunged_count);
086b73efd1a5812a64acc951366a499d325509a6Stephan Bosch tab[5].value = dec2str(client->messages_count);
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch tab[7].value = dec2str(client->input->v_offset);
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Bosch tab[8].value = dec2str(client->output->offset);
4b9e7a8752803928aa0897f8cc1fc34592452f07Stephan Boschvoid client_destroy(struct client *client, const char *reason)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_info("%s %s", reason, client_stats(client));
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* deinitialize command */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen /* client didn't QUIT, but we still want to save any changes
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen done in this transaction. especially the cached virtual
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen message sizes. */
54c6a8135c0ded324f7ae7d0cdf1ef177004ee2dStephan Bosch (void)mailbox_transaction_commit(&client->trans, 0);
54c6a8135c0ded324f7ae7d0cdf1ef177004ee2dStephan Bosch /* quit the program */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenvoid client_disconnect(struct client *client, const char *reason)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen i_info("Disconnected: %s %s", reason, client_stats(client));
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Boschint client_send_line(struct client *client, const char *fmt, ...)
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch ret = o_stream_send(client->output, str_data(str), str_len(str));
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch if (o_stream_get_buffer_used_size(client->output) <
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch /* no more input until client has read
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch our output */
67e0afe62b26d222614b8d817155bf5c74bd7fe0Stephan Bosch /* If someone happens to flush output,
6793538c389d3e725456e3eabb697e2743233646Stephan Bosch we want to get our IO handler back in
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen flush callback */
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen return (int)ret;
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainenvoid client_send_storage_error(struct client *client)
54c6a8135c0ded324f7ae7d0cdf1ef177004ee2dStephan Bosch if (mailbox_is_inconsistent(client->mailbox)) {
54c6a8135c0ded324f7ae7d0cdf1ef177004ee2dStephan Bosch client_send_line(client, "-ERR Mailbox is in inconsistent "
54c6a8135c0ded324f7ae7d0cdf1ef177004ee2dStephan Bosch "state, please relogin.");
54c6a8135c0ded324f7ae7d0cdf1ef177004ee2dStephan Bosch client_disconnect(client, "Mailbox is in inconsistent state.");
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen mail_storage_get_last_error(client->inbox_ns->storage,
cb3ab2fd5668700a89b274a43595cfbfa1616e4bTimo Sirainenstatic void client_input(struct client *client)
086b73efd1a5812a64acc951366a499d325509a6Stephan Bosch /* we're still processing a command. wait until it's
efe78d3ba24fc866af1c79b9223dc0809ba26cadStephan Bosch /* disconnected */
9a84b90d894a741ae6e090de104d31382a41d0aaJosef 'Jeff' Sipek /* line too long, kill it */
9a84b90d894a741ae6e090de104d31382a41d0aaJosef 'Jeff' Sipek client_send_line(client, "-ERR Input line too long.");
9a84b90d894a741ae6e090de104d31382a41d0aaJosef 'Jeff' Sipek client_destroy(client, "Input line too long");
a618726eb3eb09a3866fe93208baf923d593f4d3Timo Sirainen (line = i_stream_next_line(client->input)) != NULL) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen } else if (++client->bad_counter > CLIENT_MAX_BAD_COMMANDS) {
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen client_send_line(client, "-ERR Too many bad commands.");
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen client_disconnect(client, "Too many bad commands.");
48325adac125d7ff275ec69b05b7a92be9637630Timo Sirainenstatic int client_output(struct client *client)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen if ((ret = o_stream_flush(client->output)) < 0) {
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen if (o_stream_get_buffer_used_size(client->output) <
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen OUTBUF_THROTTLE_SIZE/2 && client->io == NULL) {
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen /* enable input again */
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen client->io = io_add(i_stream_get_fd(client->input),
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainen if (client->io != NULL && client->waiting_input)
c61779cfa45c1684b1f7c462011088bad0b8318cTimo Sirainenstatic void idle_timeout(void *context __attr_unused__)
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen client_destroy(my_client, "Disconnected for inactivity "
d951320d498ae0800b677b754dde71574102123bTimo Sirainen "in reading our output");
d951320d498ae0800b677b754dde71574102123bTimo Sirainen "-ERR Disconnected for inactivity.");
d951320d498ae0800b677b754dde71574102123bTimo Sirainen client_destroy(my_client, "Disconnected for inactivity");
d951320d498ae0800b677b754dde71574102123bTimo Sirainen to_idle = timeout_add(10000, idle_timeout, NULL);
086b73efd1a5812a64acc951366a499d325509a6Stephan Bosch client_send_line(my_client, "-ERR Server shutting down.");
96f89d51e8315f644f46804a9f0fc4f685ac48bfTimo Sirainen client_destroy(my_client, "Server shutting down");