server-connection.c revision 8c2f2e90b746c6d50ffa92a2083cd1bbe5c6c2db
1516N/A/* Copyright (c) 2010-2011 Dovecot authors, see the included COPYING file */
565N/A
565N/A#include "lib.h"
565N/A#include "array.h"
565N/A#include "base64.h"
565N/A#include "ioloop.h"
565N/A#include "network.h"
565N/A#include "istream.h"
565N/A#include "ostream.h"
565N/A#include "str.h"
565N/A#include "strescape.h"
565N/A#include "master-service.h"
565N/A#include "master-service-settings.h"
565N/A#include "settings-parser.h"
565N/A#include "doveadm-print.h"
565N/A#include "doveadm-util.h"
565N/A#include "doveadm-server.h"
565N/A#include "doveadm-settings.h"
565N/A#include "server-connection.h"
565N/A
565N/A#include <unistd.h>
926N/A
926N/A#define MAX_INBUF_SIZE (1024*32)
2197N/A
926N/Aenum server_reply_state {
565N/A SERVER_REPLY_STATE_DONE = 0,
2026N/A SERVER_REPLY_STATE_PRINT,
1050N/A SERVER_REPLY_STATE_RET
2524N/A};
926N/A
926N/Astruct server_connection {
2339N/A struct doveadm_server *server;
2339N/A
2339N/A pool_t pool;
926N/A struct doveadm_settings *set;
926N/A
926N/A int fd;
838N/A struct io *io;
565N/A struct istream *input;
2034N/A struct ostream *output;
2034N/A
2034N/A const char *delayed_cmd;
1540N/A server_cmd_callback_t *callback;
1540N/A void *context;
1540N/A
1540N/A enum server_reply_state state;
1540N/A
1968N/A unsigned int handshaked:1;
1540N/A unsigned int authenticated:1;
2034N/A unsigned int streaming:1;
2034N/A};
2200N/A
2034N/Astatic struct server_connection *printing_conn = NULL;
2034N/A
2034N/Astatic void server_connection_input(struct server_connection *conn);
565N/A
2339N/Astatic void print_connection_released(void)
2339N/A{
2339N/A struct doveadm_server *server = printing_conn->server;
2339N/A struct server_connection *const *conns;
2339N/A unsigned int i, count;
2339N/A
2524N/A printing_conn = NULL;
2524N/A
2524N/A conns = array_get(&server->connections, &count);
2524N/A for (i = 0; i < count; i++) {
2524N/A if (conns[i]->io != NULL)
2524N/A continue;
2524N/A
2524N/A conns[i]->io = io_add(conns[i]->fd, IO_READ,
2524N/A server_connection_input, conns[i]);
2524N/A server_connection_input(conns[i]);
2524N/A if (printing_conn != NULL)
2524N/A break;
2524N/A }
2524N/A}
2524N/A
2524N/Astatic void
2524N/Aserver_connection_callback(struct server_connection *conn,
2524N/A enum server_cmd_reply reply)
2524N/A{
2524N/A server_cmd_callback_t *callback = conn->callback;
2524N/A
2524N/A conn->callback = NULL;
2524N/A callback(reply, conn->context);
2524N/A}
2524N/A
2524N/Astatic void stream_data(string_t *str, const unsigned char *data, size_t size)
2524N/A{
2524N/A const char *text;
2524N/A
2524N/A str_truncate(str, 0);
2524N/A str_append_n(str, data, size);
2524N/A text = str_tabunescape(str_c_modifiable(str));
2524N/A doveadm_print_stream(text, strlen(text));
2524N/A}
2524N/A
2524N/Astatic void server_flush_field(struct server_connection *conn, string_t *str,
2524N/A const unsigned char *data, size_t size)
2524N/A{
2524N/A if (conn->streaming) {
2524N/A conn->streaming = FALSE;
2524N/A stream_data(str, data, size);
2524N/A doveadm_print_stream("", 0);
2524N/A } else {
2524N/A const char *text;
2524N/A
2524N/A str_truncate(str, 0);
2524N/A str_append_n(str, data, size);
2524N/A text = str_tabunescape(str_c_modifiable(str));
2524N/A doveadm_print(text);
1710N/A }
1710N/A}
1710N/A
1710N/Astatic void
1710N/Aserver_handle_input(struct server_connection *conn,
1710N/A const unsigned char *data, size_t size)
1710N/A{
1710N/A string_t *str;
1710N/A size_t i, start;
1710N/A
1710N/A if (printing_conn == conn) {
1710N/A /* continue printing */
1710N/A } else if (printing_conn == NULL) {
1710N/A printing_conn = conn;
1710N/A } else {
1710N/A /* someone else is printing. don't continue until it
1710N/A goes away */
1710N/A io_remove(&conn->io);
1710N/A return;
1710N/A }
1710N/A
1710N/A if (data[size-1] == '\001') {
1710N/A /* last character is an escape */
1710N/A size--;
1710N/A }
1710N/A
1710N/A str = t_str_new(128);
1710N/A for (i = start = 0; i < size; i++) {
1710N/A if (data[i] == '\n') {
1710N/A if (i != start)
1710N/A i_error("doveadm server sent broken input");
565N/A conn->state = SERVER_REPLY_STATE_RET;
565N/A i_stream_skip(conn->input, i + 1);
565N/A
565N/A print_connection_released();
565N/A return;
565N/A }
565N/A if (data[i] == '\t') {
565N/A server_flush_field(conn, str, data + start, i - start);
2144N/A start = i + 1;
2144N/A }
2144N/A }
2144N/A if (start != size) {
2144N/A conn->streaming = TRUE;
2144N/A stream_data(str, data + start, size - start);
2144N/A }
2144N/A i_stream_skip(conn->input, size);
2144N/A}
2144N/A
2144N/Astatic void server_connection_authenticated(struct server_connection *conn)
2144N/A{
2144N/A conn->authenticated = TRUE;
2144N/A if (conn->delayed_cmd != NULL) {
2407N/A o_stream_send_str(conn->output, conn->delayed_cmd);
2407N/A conn->delayed_cmd = NULL;
2407N/A }
2407N/A}
2407N/A
2407N/Astatic int
2407N/Aserver_connection_authenticate(struct server_connection *conn)
2407N/A{
2407N/A string_t *plain = t_str_new(128);
2407N/A string_t *cmd = t_str_new(128);
2407N/A
2407N/A if (*conn->set->doveadm_password == '\0') {
2407N/A i_error("doveadm_password not set, "
2407N/A "can't authenticate to remote server");
2407N/A return -1;
2407N/A }
2144N/A
565N/A str_append_c(plain, '\0');
565N/A str_append(plain, "doveadm");
565N/A str_append_c(plain, '\0');
565N/A str_append(plain, conn->set->doveadm_password);
565N/A
565N/A str_append(cmd, "PLAIN\t");
2144N/A base64_encode(plain->data, plain->used, cmd);
565N/A str_append_c(cmd, '\n');
565N/A
565N/A o_stream_send(conn->output, cmd->data, cmd->used);
565N/A server_connection_authenticated(conn);
565N/A return 0;
1618N/A}
1618N/A
1618N/Astatic void server_connection_input(struct server_connection *conn)
1618N/A{
1618N/A const unsigned char *data;
1618N/A size_t size;
1755N/A const char *line;
1755N/A
1755N/A if (!conn->handshaked) {
1755N/A if ((line = i_stream_read_next_line(conn->input)) == NULL) {
1755N/A if (conn->input->eof || conn->input->stream_errno != 0)
1755N/A server_connection_destroy(&conn);
1755N/A return;
1755N/A }
1755N/A
1755N/A conn->handshaked = TRUE;
1755N/A if (strcmp(line, "+") == 0)
1755N/A server_connection_authenticated(conn);
1755N/A else if (strcmp(line, "-") == 0) {
1755N/A if (server_connection_authenticate(conn) < 0) {
1755N/A server_connection_destroy(&conn);
1755N/A return;
1755N/A }
1618N/A } else {
1618N/A i_error("doveadm server sent invalid handshake: %s",
1618N/A line);
1618N/A server_connection_destroy(&conn);
1618N/A return;
1618N/A }
1618N/A }
1618N/A
1618N/A if (i_stream_read(conn->input) == -1) {
1618N/A /* disconnected */
1618N/A server_connection_destroy(&conn);
1618N/A return;
1618N/A }
1618N/A
1618N/A data = i_stream_get_data(conn->input, &size);
1618N/A if (size == 0)
1618N/A return;
1618N/A
1618N/A switch (conn->state) {
1618N/A case SERVER_REPLY_STATE_DONE:
1618N/A i_error("doveadm server sent unexpected input");
1618N/A server_connection_destroy(&conn);
1618N/A return;
1618N/A case SERVER_REPLY_STATE_PRINT:
1618N/A server_handle_input(conn, data, size);
1618N/A if (conn->state != SERVER_REPLY_STATE_RET)
1618N/A break;
1618N/A /* fall through */
1618N/A data = i_stream_get_data(conn->input, &size);
1618N/A case SERVER_REPLY_STATE_RET:
1618N/A if (size < 2)
1618N/A return;
1618N/A if (data[0] == '+' && data[1] == '\n')
1618N/A server_connection_callback(conn, SERVER_CMD_REPLY_OK);
1618N/A else if (data[0] == '-' && data[1] == '\n')
1618N/A server_connection_callback(conn, SERVER_CMD_REPLY_FAIL);
1618N/A else
1618N/A i_error("doveadm server sent broken input");
1618N/A /* we're finished, close the connection */
1618N/A server_connection_destroy(&conn);
1618N/A break;
1618N/A }
1618N/A}
1618N/A
1618N/Astatic int server_connection_read_settings(struct server_connection *conn)
1618N/A{
1618N/A const struct setting_parser_info *set_roots[] = {
1618N/A &doveadm_setting_parser_info,
1618N/A NULL
1618N/A };
1618N/A struct master_service_settings_input input;
1618N/A struct master_service_settings_output output;
1618N/A const char *error;
1618N/A unsigned int port;
1618N/A void *set;
1618N/A
1618N/A memset(&input, 0, sizeof(input));
1618N/A input.roots = set_roots;
1618N/A input.service = "doveadm";
1618N/A
1618N/A (void)net_getsockname(conn->fd, &input.local_ip, &port);
1618N/A (void)net_getpeername(conn->fd, &input.remote_ip, &port);
1618N/A
1618N/A if (master_service_settings_read(master_service, &input,
1618N/A &output, &error) < 0) {
1618N/A i_error("Error reading configuration: %s", error);
1618N/A return -1;
1618N/A }
1618N/A set = master_service_settings_get_others(master_service)[0];
1618N/A conn->set = settings_dup(&doveadm_setting_parser_info, set, conn->pool);
1019N/A return 0;
1019N/A}
1019N/A
1019N/Astruct server_connection *
1019N/Aserver_connection_create(struct doveadm_server *server)
1019N/A{
1019N/A#define DOVEADM_SERVER_HANDSHAKE "VERSION\tdoveadm-server\t1\t0\n"
1019N/A struct server_connection *conn;
1618N/A pool_t pool;
565N/A
565N/A pool = pool_alloconly_create("doveadm server connection", 1024*16);
565N/A conn = p_new(pool, struct server_connection, 1);
1618N/A conn->pool = pool;
1618N/A conn->server = server;
565N/A conn->fd = doveadm_connect(server->name);
565N/A net_set_nonblock(conn->fd, TRUE);
1618N/A conn->io = io_add(conn->fd, IO_READ, server_connection_input, conn);
565N/A conn->input = i_stream_create_fd(conn->fd, MAX_INBUF_SIZE, FALSE);
565N/A conn->output = o_stream_create_fd(conn->fd, (size_t)-1, FALSE);
565N/A conn->state = SERVER_REPLY_STATE_DONE;
1618N/A o_stream_send_str(conn->output, DOVEADM_SERVER_HANDSHAKE);
565N/A
565N/A array_append(&conn->server->connections, &conn, 1);
565N/A server_connection_read_settings(conn);
565N/A return conn;
565N/A}
1369N/A
1710N/Avoid server_connection_destroy(struct server_connection **_conn)
1710N/A{
1710N/A struct server_connection *conn = *_conn;
1710N/A struct server_connection *const *conns;
1710N/A unsigned int i, count;
1710N/A
1710N/A *_conn = NULL;
1710N/A
1710N/A conns = array_get(&conn->server->connections, &count);
1710N/A for (i = 0; i < count; i++) {
1372N/A if (conns[i] == conn) {
1369N/A array_delete(&conn->server->connections, i, 1);
1369N/A break;
1369N/A }
1369N/A }
1369N/A
1369N/A if (conn->callback != NULL) {
1369N/A server_connection_callback(conn,
1369N/A SERVER_CMD_REPLY_INTERNAL_FAILURE);
1372N/A }
1372N/A if (printing_conn == conn)
1369N/A print_connection_released();
1369N/A
565N/A i_stream_destroy(&conn->input);
565N/A o_stream_destroy(&conn->output);
565N/A if (conn->io != NULL)
565N/A io_remove(&conn->io);
565N/A if (close(conn->fd) < 0)
565N/A i_error("close(server) failed: %m");
1328N/A pool_unref(&conn->pool);
1328N/A}
1328N/A
565N/Astruct doveadm_server *
565N/Aserver_connection_get_server(struct server_connection *conn)
565N/A{
565N/A return conn->server;
565N/A}
565N/A
565N/Avoid server_connection_cmd(struct server_connection *conn, const char *line,
565N/A server_cmd_callback_t *callback, void *context)
565N/A{
685N/A i_assert(conn->delayed_cmd == NULL);
685N/A
685N/A conn->state = SERVER_REPLY_STATE_PRINT;
685N/A if (conn->authenticated)
685N/A o_stream_send_str(conn->output, line);
685N/A else
685N/A conn->delayed_cmd = p_strdup(conn->pool, line);
926N/A conn->callback = callback;
2126N/A conn->context = context;
2126N/A}
2126N/A
685N/Abool server_connection_is_idle(struct server_connection *conn)
685N/A{
2126N/A return conn->callback == NULL;
2126N/A}
685N/A