#include "write-full.h"
#include "array.h"
#include "aqueue.h"
+#include "connection.h"
#include "mail-user.h"
#include "imap-urlauth-fetch.h"
enum imap_urlauth_state {
IMAP_URLAUTH_STATE_DISCONNECTED = 0,
+ IMAP_URLAUTH_STATE_CONNECTING,
IMAP_URLAUTH_STATE_AUTHENTICATING,
IMAP_URLAUTH_STATE_AUTHENTICATED,
IMAP_URLAUTH_STATE_SELECTING_TARGET,
};
struct imap_urlauth_connection {
- int refcount;
+ struct connection conn;
struct event *event;
char *path, *service, *session_id;
struct mail_user *user;
- int fd;
- struct istream *input;
- struct ostream *output;
- struct io *io;
-
struct timeout *to_reconnect, *to_idle, *to_response;
time_t last_reconnect;
unsigned int reconnect_attempts;
#define IMAP_URLAUTH_RESPONSE_TIMEOUT_MSECS 2*60*1000
-#define IMAP_URLAUTH_HANDSHAKE "VERSION\timap-urlauth\t2\t0\n"
-
#define IMAP_URLAUTH_MAX_INLINE_LITERAL_SIZE (1024*32)
+static struct connection_list *imap_urlauth_connections = NULL;
+
+static void imap_urlauth_connection_connected(struct connection *_conn,
+ bool success);
+static void imap_urlauth_connection_input(struct connection *_conn);
+static void imap_urlauth_connection_destroy(struct connection *_conn);
+
static void
imap_urlauth_connection_disconnect(struct imap_urlauth_connection *conn,
const char *reason);
static void
imap_urlauth_connection_fail(struct imap_urlauth_connection *conn);
+static const struct connection_vfuncs imap_urlauth_connection_vfuncs = {
+ .destroy = imap_urlauth_connection_destroy,
+ .input = imap_urlauth_connection_input,
+ .client_connected = imap_urlauth_connection_connected,
+};
+
+static const struct connection_settings imap_urlauth_connection_set = {
+ .service_name_in = "imap-urlauth",
+ .service_name_out = "imap-urlauth",
+ .major_version = IMAP_URLAUTH_PROTOCOL_MAJOR_VERSION,
+ .minor_version = IMAP_URLAUTH_PROTOCOL_MINOR_VERSION,
+ .input_max_size = SIZE_MAX,
+ .output_max_size = SIZE_MAX,
+ .client = TRUE,
+};
+
struct imap_urlauth_connection *
imap_urlauth_connection_init(const char *path, const char *service,
struct mail_user *user, const char *session_id,
{
struct imap_urlauth_connection *conn;
+ if (imap_urlauth_connections == NULL) {
+ imap_urlauth_connections =
+ connection_list_init(&imap_urlauth_connection_set,
+ &imap_urlauth_connection_vfuncs);
+ }
+
conn = i_new(struct imap_urlauth_connection, 1);
- conn->refcount = 1;
conn->service = i_strdup(service);
conn->path = i_strdup(path);
if (session_id != NULL)
conn->session_id = i_strdup(session_id);
conn->user = user;
- conn->fd = -1;
conn->literal_fd = -1;
conn->idle_timeout_msecs = idle_timeout_msecs;
+
conn->event = event_create(user->event);
event_set_append_log_prefix(conn->event, "imap-urlauth: ");
+
+ conn->conn.event_parent = conn->event;
+ connection_init_client_unix(imap_urlauth_connections,
+ &conn->conn, conn->path);
return conn;
}
i_assert(conn->to_reconnect == NULL);
i_assert(conn->to_response == NULL);
+ connection_deinit(&conn->conn);
event_unref(&conn->event);
i_free(conn);
+
+ if (imap_urlauth_connections->connections == NULL)
+ connection_list_deinit(&imap_urlauth_connections);
}
static void
conn->state = IMAP_URLAUTH_STATE_SELECTING_TARGET;
cmd = t_strdup_printf("USER\t%s\n", str_tabescape(target->userid));
- if (o_stream_send_str(conn->output, cmd) < 0) {
+ if (o_stream_send_str(conn->conn.output, cmd) < 0) {
e_warning(conn->event,
"Error sending USER request to imap-urlauth server: %m");
imap_urlauth_connection_fail(conn);
conn->state = IMAP_URLAUTH_STATE_UNSELECTING_TARGET;
imap_urlauth_target_free(conn, conn->targets_head);
- if (o_stream_send_str(conn->output, "END\n") < 0) {
+ if (o_stream_send_str(conn->conn.output, "END\n") < 0) {
e_warning(conn->event,
"Error sending END request to imap-urlauth server: %m");
imap_urlauth_connection_fail(conn);
str_append_c(cmd, '\n');
conn->state = IMAP_URLAUTH_STATE_REQUEST_PENDING;
- if (o_stream_send(conn->output, str_data(cmd), str_len(cmd)) < 0) {
+ if (o_stream_send(conn->conn.output, str_data(cmd), str_len(cmd)) < 0) {
e_warning(conn->event,
"Error sending URL request to imap-urlauth server: %m");
imap_urlauth_connection_fail(conn);
size_t size;
/* Read data */
- data = i_stream_get_data(conn->input, &size);
+ data = i_stream_get_data(conn->conn.input, &size);
if (size > conn->literal_bytes_left)
size = conn->literal_bytes_left;
i_assert(conn->literal_buf != NULL);
buffer_append(conn->literal_buf, data, size);
}
- i_stream_skip(conn->input, size);
+ i_stream_skip(conn->conn.input, size);
conn->literal_bytes_left -= size;
}
return 0;
/* Read LF guard */
- data = i_stream_get_data(conn->input, &size);
+ data = i_stream_get_data(conn->conn.input, &size);
if (size < 1)
return 0;
data[0]);
return -1;
}
- i_stream_skip(conn->input, 1);
+ i_stream_skip(conn->conn.input, 1);
return 1;
}
return 1;
}
+static void imap_urlauth_connection_destroy(struct connection *_conn)
+{
+ struct imap_urlauth_connection *conn =
+ container_of(_conn, struct imap_urlauth_connection, conn);
+
+ switch (_conn->disconnect_reason) {
+ case CONNECTION_DISCONNECT_HANDSHAKE_FAILED:
+ imap_urlauth_connection_disconnect(
+ conn, "Handshake with imap-urlauth service failed");
+ break;
+ case CONNECTION_DISCONNECT_BUFFER_FULL:
+ i_unreached();
+ default:
+ /* Disconnected */
+ imap_urlauth_connection_reconnect(conn);
+ }
+}
+
static int imap_urlauth_input_pending(struct imap_urlauth_connection *conn)
{
struct imap_urlauth_request *urlreq;
/* "OK"[<metadata-items>]"\t"<literal-size>"\n" or
"NO"["\terror="<error>]"\n" */
- if ((response = i_stream_next_line(conn->input)) == NULL)
+ if ((response = i_stream_next_line(conn->conn.input)) == NULL)
return 0;
imap_urlauth_stop_response_timeout(conn);
{
string_t *str;
+ conn->state = IMAP_URLAUTH_STATE_AUTHENTICATING;
+
str = t_str_new(128);
- str_printfa(str, IMAP_URLAUTH_HANDSHAKE"AUTH\t%s\t%s\t",
- conn->service, my_pid);
+ str_printfa(str, "AUTH\t%s\t%s\t", conn->service, my_pid);
str_append_tabescaped(str, conn->user->username);
str_append_c(str, '\t');
if (conn->session_id != NULL)
str_append_c(str, '\t');
str_append_tabescaped(str, conn->user->auth_token);
str_append_c(str, '\n');
- if (o_stream_send(conn->output, str_data(str), str_len(str)) < 0) {
+ if (o_stream_send(conn->conn.output, str_data(str), str_len(str)) < 0) {
e_warning(conn->event,
"Error sending handshake to imap-urlauth server: %m");
imap_urlauth_connection_abort(conn, NULL);
return -1;
}
+
return 0;
}
int ret;
switch (conn->state) {
+ case IMAP_URLAUTH_STATE_CONNECTING:
+ break;
case IMAP_URLAUTH_STATE_AUTHENTICATING:
case IMAP_URLAUTH_STATE_UNSELECTING_TARGET:
- if ((response = i_stream_next_line(conn->input)) == NULL)
+ if ((response = i_stream_next_line(conn->conn.input)) == NULL)
return 0;
imap_urlauth_stop_response_timeout(conn);
imap_urlauth_connection_select_target(conn);
return 0;
case IMAP_URLAUTH_STATE_SELECTING_TARGET:
- if ((response = i_stream_next_line(conn->input)) == NULL)
+ if ((response = i_stream_next_line(conn->conn.input)) == NULL)
return 0;
imap_urlauth_stop_response_timeout(conn);
case IMAP_URLAUTH_STATE_AUTHENTICATED:
case IMAP_URLAUTH_STATE_READY:
case IMAP_URLAUTH_STATE_REQUEST_WAIT:
- if ((response = i_stream_next_line(conn->input)) == NULL)
+ if ((response = i_stream_next_line(conn->conn.input)) == NULL)
return 0;
e_error(conn->event,
i_unreached();
}
-static void imap_urlauth_input(struct imap_urlauth_connection *conn)
+static void imap_urlauth_connection_input(struct connection *_conn)
{
+ struct imap_urlauth_connection *conn =
+ container_of(_conn, struct imap_urlauth_connection, conn);
+
i_assert(conn->state != IMAP_URLAUTH_STATE_DISCONNECTED);
- if (conn->input->closed) {
+ if (conn->conn.input->closed) {
/* Disconnected */
e_error(conn->event, "Service disconnected unexpectedly");
imap_urlauth_connection_fail(conn);
return;
}
- switch (i_stream_read(conn->input)) {
+ switch (i_stream_read(conn->conn.input)) {
case -1:
/* Disconnected */
e_error(conn->event, "Service disconnected unexpectedly");
return;
}
- while (!conn->input->closed) {
+ while (!conn->conn.input->closed) {
if (imap_urlauth_input_next(conn) <= 0)
break;
}
}
+static void
+imap_urlauth_connection_connected(struct connection *_conn, bool success)
+{
+ struct imap_urlauth_connection *conn =
+ container_of(_conn, struct imap_urlauth_connection, conn);
+
+ /* Cannot get here unless UNIX socket connect() was successful */
+ i_assert(success);
+
+ if (imap_urlauth_authenticate(conn) < 0)
+ return;
+
+ imap_urlauth_start_response_timeout(conn);
+}
+
static int
imap_urlauth_connection_do_connect(struct imap_urlauth_connection *conn)
{
- int fd;
-
- if (conn->state != IMAP_URLAUTH_STATE_DISCONNECTED) {
+ if (conn->state >= IMAP_URLAUTH_STATE_AUTHENTICATED) {
imap_urlauth_connection_send_request(conn);
return 1;
}
+ if (conn->state >= IMAP_URLAUTH_STATE_CONNECTING)
+ return 1;
if (conn->user->auth_token == NULL) {
e_error(conn->event,
e_debug(conn->event, "Connecting to service at %s", conn->path);
- i_assert(conn->fd == -1);
- fd = net_connect_unix(conn->path);
- if (fd == -1) {
+ timeout_remove(&conn->to_reconnect);
+
+ conn->state = IMAP_URLAUTH_STATE_CONNECTING;
+ imap_urlauth_start_response_timeout(conn);
+
+ if (connection_client_connect(&conn->conn) < 0) {
e_error(conn->event, "net_connect_unix(%s) failed: %m",
conn->path);
imap_urlauth_connection_abort(conn, NULL);
return -1;
}
-
- timeout_remove(&conn->to_reconnect);
-
- conn->fd = fd;
- conn->input = i_stream_create_fd(fd, SIZE_MAX);
- conn->output = o_stream_create_fd(fd, SIZE_MAX);
- conn->io = io_add(fd, IO_READ, imap_urlauth_input, conn);
- conn->state = IMAP_URLAUTH_STATE_AUTHENTICATING;
-
- if (imap_urlauth_authenticate(conn) < 0)
- return -1;
-
- imap_urlauth_start_response_timeout(conn);
return 0;
}
return 0;
}
-static void imap_urlauth_connection_disconnect
-(struct imap_urlauth_connection *conn, const char *reason)
+static void
+imap_urlauth_connection_disconnect(struct imap_urlauth_connection *conn,
+ const char *reason)
{
conn->state = IMAP_URLAUTH_STATE_DISCONNECTED;
- if (conn->fd != -1) {
+ if (!conn->conn.disconnected) {
if (reason == NULL)
e_debug(conn->event, "Disconnecting from service");
else
e_debug(conn->event, "Disconnected: %s", reason);
- io_remove(&conn->io);
- i_stream_destroy(&conn->input);
- o_stream_destroy(&conn->output);
- net_disconnect(conn->fd);
- conn->fd = -1;
+ connection_disconnect(&conn->conn);
}
conn->reading_literal = FALSE;
bool imap_urlauth_connection_is_connected(struct imap_urlauth_connection *conn)
{
- return (conn->fd != -1 &&
+ return (conn->conn.disconnected &&
conn->state != IMAP_URLAUTH_STATE_DISCONNECTED);
}