void *state_change_context;
ARRAY(struct imapc_client_connection *) conns;
+ bool logging_out;
struct ioloop *ioloop;
};
imapc_connection_connect(conn->conn, callback, context);
}
+struct imapc_logout_ctx {
+ struct imapc_client *client;
+ unsigned int logout_count;
+};
+
+static void
+imapc_client_logout_callback(const struct imapc_command_reply *reply ATTR_UNUSED,
+ void *context)
+{
+ struct imapc_logout_ctx *ctx = context;
+
+ i_assert(ctx->logout_count > 0);
+
+ if (--ctx->logout_count == 0)
+ imapc_client_stop(ctx->client);
+}
+
+void imapc_client_logout(struct imapc_client *client)
+{
+ struct imapc_logout_ctx ctx = { .client = client };
+ struct imapc_client_connection *const *connp;
+ struct imapc_command *cmd;
+
+ client->logging_out = TRUE;
+
+ /* send LOGOUT to all connections */
+ array_foreach(&client->conns, connp) {
+ imapc_connection_set_no_reconnect((*connp)->conn);
+ ctx.logout_count++;
+ cmd = imapc_connection_cmd((*connp)->conn,
+ imapc_client_logout_callback, &ctx);
+ imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN |
+ IMAPC_COMMAND_FLAG_LOGOUT);
+ imapc_command_send(cmd, "LOGOUT");
+ }
+
+ /* wait for LOGOUT to finish */
+ while (ctx.logout_count > 0)
+ imapc_client_run(client);
+
+ /* we should have disconnected all clients already, but if there were
+ any timeouts there may be some clients left. */
+ imapc_client_disconnect(client);
+}
+
struct imapc_client_mailbox *
imapc_client_mailbox_open(struct imapc_client *client,
void *untagged_box_context)
IMAPC_COMMAND_FLAG_PRELOGIN = 0x02,
/* Allow command to be automatically retried if disconnected before it
finishes. */
- IMAPC_COMMAND_FLAG_RETRIABLE = 0x04
+ IMAPC_COMMAND_FLAG_RETRIABLE = 0x04,
+ /* This is the LOGOUT command. Use a small timeout for it. */
+ IMAPC_COMMAND_FLAG_LOGOUT = 0x08
};
enum imapc_client_ssl_mode {
/* Explicitly login to server (also done automatically). */
void imapc_client_login(struct imapc_client *client,
imapc_command_callback_t *callback, void *context);
+/* Send a LOGOUT and wait for disconnection. */
+void imapc_client_logout(struct imapc_client *client);
struct imapc_command *
imapc_client_cmd(struct imapc_client *client,
#define IMAPC_COMMAND_STATE_AUTHENTICATE_CONTINUE 10000
#define IMAPC_MAX_INLINE_LITERAL_SIZE (1024*32)
#define IMAPC_RECONNECT_MIN_RETRY_SECS 10
+/* If LOGOUT reply takes longer than this, disconnect. */
+#define IMAPC_LOGOUT_TIMEOUT_MSECS 5000
enum imapc_input_state {
IMAPC_INPUT_STATE_NONE = 0,
imapc_connection_abort_commands(conn, NULL, reconnecting);
}
+void imapc_connection_set_no_reconnect(struct imapc_connection *conn)
+{
+ conn->reconnect_ok = FALSE;
+}
+
void imapc_connection_disconnect(struct imapc_connection *conn)
{
imapc_connection_disconnect_full(conn, FALSE);
static bool imapc_connection_can_reconnect(struct imapc_connection *conn)
{
+ if (conn->client->logging_out)
+ return FALSE;
if (conn->selected_box != NULL)
return imapc_client_mailbox_can_reconnect(conn->selected_box);
else {
while (conn->input != NULL && (ret = i_stream_read(conn->input)) > 0)
imapc_connection_input_pending(conn);
- if (ret < 0) {
+ if (ret < 0 && conn->client->logging_out &&
+ conn->disconnect_reason != NULL) {
+ /* expected disconnection */
+ } else if (ret < 0) {
/* disconnected or buffer full */
str = t_str_new(128);
if (conn->disconnect_reason != NULL) {
/* wait until we're fully connected */
return;
}
+ if ((cmd->flags & IMAPC_COMMAND_FLAG_LOGOUT) != 0 &&
+ array_count(&conn->cmd_wait_list) > 0) {
+ /* wait until existing commands have finished */
+ return;
+ }
if (cmd->wait_for_literal) {
/* wait until we received '+' */
return;
/* add timeout for commands if there's not one yet
(pre-login has its own timeout) */
- if (conn->to == NULL) {
+ if ((cmd->flags & IMAPC_COMMAND_FLAG_LOGOUT) != 0) {
+ /* LOGOUT has a shorter timeout */
+ if (conn->to != NULL)
+ timeout_remove(&conn->to);
+ conn->to = timeout_add(IMAPC_LOGOUT_TIMEOUT_MSECS,
+ imapc_command_timeout, conn);
+ } else if (conn->to == NULL) {
conn->to = timeout_add(conn->client->set.cmd_timeout_msecs,
imapc_command_timeout, conn);
}
void imapc_connection_connect(struct imapc_connection *conn,
imapc_command_callback_t *login_callback,
void *login_context) ATTR_NULL(2, 3);
+void imapc_connection_set_no_reconnect(struct imapc_connection *conn);
void imapc_connection_disconnect(struct imapc_connection *conn);
void imapc_connection_disconnect_full(struct imapc_connection *conn,
bool reconnecting);
deinitialized */
if (list->client != NULL) {
list->client->destroying = TRUE;
- imapc_client_disconnect(list->client->client);
+ imapc_client_logout(list->client->client);
imapc_storage_client_unref(&list->client);
}
if (list->index_list != NULL)
void imapc_simple_run(struct imapc_simple_context *sctx)
{
if (sctx->client->auth_failed) {
- imapc_client_disconnect(sctx->client->client);
+ imapc_client_logout(sctx->client->client);
sctx->ret = -1;
}
while (sctx->ret == -2)
/* make sure all pending commands are aborted before anything is
deinitialized */
- imapc_client_disconnect(storage->client->client);
+ imapc_client_logout(storage->client->client);
imapc_storage_client_unref(&storage->client);
index_storage_destroy(_storage);