view src/lib-storage/index/imapc/imapc-client.c @ 12617:7b7434fef6ff

imapc: Added initial support for SSL.
author Timo Sirainen <tss@iki.fi>
date Mon, 31 Jan 2011 18:41:04 +0200
parents 3dde816d945d
children 4244c828b59d
line wrap: on
line source

/* Copyright (c) 2011 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "array.h"
#include "str.h"
#include "ioloop.h"
#include "safe-mkstemp.h"
#include "iostream-ssl.h"
#include "imapc-seqmap.h"
#include "imapc-connection.h"
#include "imapc-client-private.h"

#include <unistd.h>

struct imapc_client_command_context {
	struct imapc_client_mailbox *box;

	imapc_command_callback_t *callback;
	void *context;
};

const struct imapc_capability_name imapc_capability_names[] = {
	{ "SASL-IR", IMAPC_CAPABILITY_SASL_IR },
	{ "LITERAL+", IMAPC_CAPABILITY_LITERALPLUS },
	{ "QRESYNC", IMAPC_CAPABILITY_QRESYNC },
	{ "IDLE", IMAPC_CAPABILITY_IDLE },
	{ "UIDPLUS", IMAPC_CAPABILITY_UIDPLUS },

	{ "IMAP4REV1", IMAPC_CAPABILITY_IMAP4REV1 },
	{ NULL, 0 }
};

struct imapc_client *
imapc_client_init(const struct imapc_client_settings *set)
{
	struct imapc_client *client;
	struct ssl_iostream_settings ssl_set;
	const char *source;
	pool_t pool;

	pool = pool_alloconly_create("imapc client", 1024);
	client = p_new(pool, struct imapc_client, 1);
	client->pool = pool;

	client->set.host = p_strdup(pool, set->host);
	client->set.port = set->port;
	client->set.master_user = p_strdup(pool, set->master_user);
	client->set.username = p_strdup(pool, set->username);
	client->set.password = p_strdup(pool, set->password);
	client->set.dns_client_socket_path =
		p_strdup(pool, set->dns_client_socket_path);
	client->set.temp_path_prefix =
		p_strdup(pool, set->temp_path_prefix);

	if (set->ssl_mode != IMAPC_CLIENT_SSL_MODE_NONE) {
		client->set.ssl_mode = set->ssl_mode;
		client->set.ssl_ca_dir = p_strdup(pool, set->ssl_ca_dir);

		memset(&ssl_set, 0, sizeof(ssl_set));
		ssl_set.ca_dir = set->ssl_ca_dir;
		ssl_set.verify_remote_cert = TRUE;

		source = t_strdup_printf("%s:%u", set->host, set->port);
		if (ssl_iostream_context_init_client(source, &ssl_set,
						     &client->ssl_ctx) < 0) {
			i_error("imapc(%s): Couldn't initialize SSL context",
				source);
		}
	}

	p_array_init(&client->conns, pool, 8);
	return client;
}

void imapc_client_deinit(struct imapc_client **_client)
{
	struct imapc_client *client = *_client;
	struct imapc_client_connection **connp;

	*_client = NULL;

	if (client->ssl_ctx != NULL)
		ssl_iostream_context_deinit(&client->ssl_ctx);
	array_foreach_modifiable(&client->conns, connp)
		imapc_connection_deinit(&(*connp)->conn);
	pool_unref(&client->pool);
}

void imapc_client_register_untagged(struct imapc_client *client,
				    imapc_untagged_callback_t *callback,
				    void *context)
{
	client->untagged_callback = callback;
	client->untagged_context = context;
}

void imapc_client_run(struct imapc_client *client)
{
	struct imapc_client_connection *const *connp;
	struct ioloop *prev_ioloop = current_ioloop;
	bool handle_pending = client->stop_now;

	i_assert(client->ioloop == NULL);

	client->stop_now = FALSE;

	client->ioloop = io_loop_create();
	io_loop_set_running(client->ioloop);

	array_foreach(&client->conns, connp) {
		imapc_connection_ioloop_changed((*connp)->conn);
		imapc_connection_connect((*connp)->conn);
		if (handle_pending)
			imapc_connection_input_pending((*connp)->conn);
	}

	if (io_loop_is_running(client->ioloop))
		io_loop_run(client->ioloop);

	current_ioloop = prev_ioloop;
	array_foreach(&client->conns, connp)
		imapc_connection_ioloop_changed((*connp)->conn);

	current_ioloop = client->ioloop;
	io_loop_destroy(&client->ioloop);
}

void imapc_client_stop(struct imapc_client *client)
{
	if (client->ioloop != NULL)
		io_loop_stop(client->ioloop);
}

void imapc_client_stop_now(struct imapc_client *client)
{
	client->stop_now = TRUE;
	imapc_client_stop(client);
}

static struct imapc_client_connection *
imapc_client_add_connection(struct imapc_client *client)
{
	struct imapc_client_connection *conn;

	conn = i_new(struct imapc_client_connection, 1);
	conn->conn = imapc_connection_init(client);
	array_append(&client->conns, &conn, 1);
	return conn;
}

static struct imapc_connection *
imapc_client_find_connection(struct imapc_client *client)
{
	struct imapc_client_connection *const *connp;

	/* FIXME: stupid algorithm */
	if (array_count(&client->conns) == 0)
		return imapc_client_add_connection(client)->conn;
	connp = array_idx(&client->conns, 0);
	return (*connp)->conn;
}

void imapc_client_cmdf(struct imapc_client *client,
		       imapc_command_callback_t *callback, void *context,
		       const char *cmd_fmt, ...)
{
	struct imapc_connection *conn;
	va_list args;

	conn = imapc_client_find_connection(client);

	va_start(args, cmd_fmt);
	imapc_connection_cmdvf(conn, callback, context, cmd_fmt, args);
	va_end(args);
}

static struct imapc_client_connection *
imapc_client_get_unboxed_connection(struct imapc_client *client)
{
	struct imapc_client_connection *const *conns;
	unsigned int i, count;

	conns = array_get(&client->conns, &count);
	for (i = 0; i < count; i++) {
		if (conns[i]->box == NULL)
			return conns[i];
	}
	return imapc_client_add_connection(client);
}


struct imapc_client_mailbox *
imapc_client_mailbox_open(struct imapc_client *client, const char *name,
			  imapc_command_callback_t *callback, void *context,
			  void *untagged_box_context)
{
	struct imapc_client_mailbox *box;
	struct imapc_client_connection *conn;

	box = i_new(struct imapc_client_mailbox, 1);
	box->client = client;
	box->untagged_box_context = untagged_box_context;
	conn = imapc_client_get_unboxed_connection(client);
	conn->box = box;
	box->conn = conn->conn;
	box->seqmap = imapc_seqmap_init();

	imapc_connection_select(box, name, callback, context);
	return box;
}

void imapc_client_mailbox_close(struct imapc_client_mailbox **_box)
{
	struct imapc_client_mailbox *box = *_box;
	struct imapc_client_connection *const *connp;

	*_box = NULL;

	array_foreach(&box->client->conns, connp) {
		if ((*connp)->box == box) {
			(*connp)->box = NULL;
			break;
		}
	}

	imapc_connection_unselect(box);
	imapc_seqmap_deinit(&box->seqmap);
	i_free(box);
}

static void imapc_client_mailbox_cmd_cb(const struct imapc_command_reply *reply,
					void *context)
{
	struct imapc_client_command_context *ctx = context;

	ctx->box->pending_box_command_count--;

	ctx->callback(reply, ctx->context);
	i_free(ctx);
}

static struct imapc_client_command_context *
imapc_client_mailbox_cmd_common(struct imapc_client_mailbox *box,
				imapc_command_callback_t *callback,
				void *context)
{
	struct imapc_client_command_context *ctx;

	ctx = i_new(struct imapc_client_command_context, 1);
	ctx->box = box;
	ctx->callback = callback;
	ctx->context = context;

	box->pending_box_command_count++;
	return ctx;
}

void imapc_client_mailbox_cmd(struct imapc_client_mailbox *box,
			      const char *cmd,
			      imapc_command_callback_t *callback,
			      void *context)
{
	struct imapc_client_command_context *ctx;

	ctx = imapc_client_mailbox_cmd_common(box, callback, context);
	imapc_connection_cmd(box->conn, cmd, imapc_client_mailbox_cmd_cb, ctx);
}

void imapc_client_mailbox_cmdf(struct imapc_client_mailbox *box,
			       imapc_command_callback_t *callback,
			       void *context, const char *cmd_fmt, ...)
{
	struct imapc_client_command_context *ctx;
	va_list args;

	ctx = imapc_client_mailbox_cmd_common(box, callback, context);
	va_start(args, cmd_fmt);
	imapc_connection_cmdvf(box->conn, imapc_client_mailbox_cmd_cb,
			       ctx, cmd_fmt, args);
	va_end(args);
}

struct imapc_seqmap *
imapc_client_mailbox_get_seqmap(struct imapc_client_mailbox *box)
{
	return box->seqmap;
}

void imapc_client_mailbox_idle(struct imapc_client_mailbox *box)
{
	imapc_connection_idle(box->conn);
}

enum imapc_capability
imapc_client_get_capabilities(struct imapc_client *client)
{
	struct imapc_client_connection *const *connp;

	connp = array_idx(&client->conns, 0);
	return imapc_connection_get_capabilities((*connp)->conn);
}

int imapc_client_create_temp_fd(struct imapc_client *client,
				const char **path_r)
{
	string_t *path;
	int fd;

	path = t_str_new(128);
	str_append(path, client->set.temp_path_prefix);
	fd = safe_mkstemp(path, 0600, (uid_t)-1, (gid_t)-1);
	if (fd == -1) {
		i_error("safe_mkstemp(%s) failed: %m", str_c(path));
		return -1;
	}

	/* we just want the fd, unlink it */
	if (unlink(str_c(path)) < 0) {
		/* shouldn't happen.. */
		i_error("unlink(%s) failed: %m", str_c(path));
		(void)close(fd);
		return -1;
	}
	*path_r = str_c(path);
	return fd;
}