struct imapc_connection *conn;
struct imapc_msgmap *msgmap;
+ void (*reopen_callback)(void *context);
+ void *reopen_context;
+
void *untagged_box_context;
unsigned int pending_box_command_count;
+
+ bool reconnect_ok;
};
void imapc_client_ref(struct imapc_client *client);
client->untagged_context = context;
}
-void imapc_client_run_pre(struct imapc_client *client)
+static void imapc_client_run_pre(struct imapc_client *client)
{
struct imapc_client_connection *const *connp;
struct ioloop *prev_ioloop = current_ioloop;
current_ioloop = prev_ioloop;
}
-void imapc_client_run_post(struct imapc_client *client)
+static void imapc_client_run_post(struct imapc_client *client)
{
struct imapc_client_connection *const *connp;
struct ioloop *ioloop = client->ioloop;
io_loop_destroy(&ioloop);
}
+void imapc_client_run(struct imapc_client *client)
+{
+ imapc_client_run_pre(client);
+ imapc_client_run_post(client);
+}
+
void imapc_client_stop(struct imapc_client *client)
{
if (client->ioloop != NULL)
return box;
}
-void imapc_client_mailbox_disconnect(struct imapc_client_mailbox *box)
+void imapc_client_mailbox_set_reopen_cb(struct imapc_client_mailbox *box,
+ void (*callback)(void *context),
+ void *context)
+{
+ box->reopen_callback = callback;
+ box->reopen_context = context;
+}
+
+static void
+imapc_client_reconnect_cb(const struct imapc_command_reply *reply,
+ void *context)
+{
+ struct imapc_client_mailbox *box = context;
+
+ if (reply->state == IMAPC_COMMAND_STATE_OK) {
+ /* reopen the mailbox */
+ box->reopen_callback(box->reopen_context);
+ }
+}
+
+void imapc_client_mailbox_reconnect(struct imapc_client_mailbox *box)
{
- if (box->conn != NULL)
- imapc_connection_disconnect(box->conn);
+ imapc_connection_disconnect(box->conn);
+ if (box->reopen_callback != NULL && box->reconnect_ok) {
+ imapc_connection_connect(box->conn,
+ imapc_client_reconnect_cb, box);
+ }
+ box->reconnect_ok = FALSE;
}
void imapc_client_mailbox_close(struct imapc_client_mailbox **_box)
struct imapc_client_mailbox *box = *_box;
struct imapc_client_connection *const *connp;
+ /* cancel any pending commands */
+ imapc_connection_unselect(box);
+
+ /* set this only after unselect, which may cancel some commands that
+ reference this box */
+ *_box = NULL;
+
array_foreach(&box->client->conns, connp) {
if ((*connp)->box == box) {
(*connp)->box = NULL;
}
}
- if (box->conn != NULL)
- imapc_connection_unselect(box);
imapc_msgmap_deinit(&box->msgmap);
i_free(box);
-
- /* set this only after unselect, which may cancel some commands that
- reference this box */
- *_box = NULL;
}
struct imapc_command *
void imapc_client_mailbox_idle(struct imapc_client_mailbox *box)
{
- if (imapc_client_mailbox_is_connected(box))
+ if (imapc_client_mailbox_is_opened(box))
imapc_connection_idle(box->conn);
+ box->reconnect_ok = TRUE;
}
-bool imapc_client_mailbox_is_connected(struct imapc_client_mailbox *box)
+bool imapc_client_mailbox_is_opened(struct imapc_client_mailbox *box)
{
struct imapc_client_mailbox *selected_box;
- selected_box = box->conn == NULL ? NULL :
- imapc_connection_get_mailbox(box->conn);
- if (selected_box == box)
- return TRUE;
-
- if (selected_box != NULL)
- i_error("imapc: Selected mailbox changed unexpectedly");
+ if (imapc_connection_get_state(box->conn) != IMAPC_CONNECTION_STATE_DONE)
+ return FALSE;
- box->conn = NULL;
- return FALSE;
+ selected_box = imapc_connection_get_mailbox(box->conn);
+ if (selected_box != box) {
+ if (selected_box != NULL)
+ i_error("imapc: Selected mailbox changed unexpectedly");
+ return FALSE;
+ }
+ return TRUE;
}
enum imapc_capability
enum imapc_command_flags {
/* The command changes the selected mailbox (SELECT, EXAMINE) */
- IMAPC_COMMAND_FLAG_SELECT = 0x01
+ IMAPC_COMMAND_FLAG_SELECT = 0x01,
+ /* The command is sent to server before login (or is the login
+ command itself). Non-prelogin commands will be queued until login
+ is successful. */
+ IMAPC_COMMAND_FLAG_PRELOGIN = 0x02,
+ /* Allow command to be automatically retried if disconnected before it
+ finishes. */
+ IMAPC_COMMAND_FLAG_RETRIABLE = 0x04
};
enum imapc_client_ssl_mode {
imapc_untagged_callback_t *callback,
void *context);
-void imapc_client_run_pre(struct imapc_client *client);
-void imapc_client_run_post(struct imapc_client *client);
+void imapc_client_run(struct imapc_client *client);
void imapc_client_stop(struct imapc_client *client);
bool imapc_client_is_running(struct imapc_client *client);
struct imapc_client_mailbox *
imapc_client_mailbox_open(struct imapc_client *client,
void *untagged_box_context);
+void imapc_client_mailbox_set_reopen_cb(struct imapc_client_mailbox *box,
+ void (*callback)(void *context),
+ void *context);
void imapc_client_mailbox_close(struct imapc_client_mailbox **box);
-void imapc_client_mailbox_disconnect(struct imapc_client_mailbox *box);
+void imapc_client_mailbox_reconnect(struct imapc_client_mailbox *box);
struct imapc_command *
imapc_client_mailbox_cmd(struct imapc_client_mailbox *box,
imapc_command_callback_t *callback, void *context);
imapc_client_mailbox_get_msgmap(struct imapc_client_mailbox *box);
void imapc_client_mailbox_idle(struct imapc_client_mailbox *box);
-bool imapc_client_mailbox_is_connected(struct imapc_client_mailbox *box);
+bool imapc_client_mailbox_is_opened(struct imapc_client_mailbox *box);
enum imapc_capability
imapc_client_get_capabilities(struct imapc_client *client);
static int imapc_connection_output(struct imapc_connection *conn);
static int imapc_connection_ssl_init(struct imapc_connection *conn);
static void imapc_command_free(struct imapc_command *cmd);
-static void imapc_command_send_more(struct imapc_connection *conn,
- struct imapc_command *cmd);
+static void imapc_command_send_more(struct imapc_connection *conn);
struct imapc_connection *
imapc_connection_init(struct imapc_client *client)
static void imapc_connection_set_state(struct imapc_connection *conn,
enum imapc_connection_state state)
{
- if (state == IMAPC_CONNECTION_STATE_DISCONNECTED) {
- struct imapc_command_reply reply;
+ struct imapc_command_reply reply;
+
+ conn->state = state;
+ switch (state) {
+ case IMAPC_CONNECTION_STATE_DISCONNECTED:
memset(&reply, 0, sizeof(reply));
reply.state = IMAPC_COMMAND_STATE_DISCONNECTED;
reply.text_without_resp = reply.text_full =
conn->selecting_box = NULL;
conn->selected_box = NULL;
+ break;
+ case IMAPC_CONNECTION_STATE_DONE:
+ imapc_command_send_more(conn);
+ break;
+ default:
+ break;
}
- if (state == IMAPC_CONNECTION_STATE_DONE) {
- if (array_count(&conn->cmd_send_queue) > 0) {
- struct imapc_command *const *cmd_p =
- array_idx(&conn->cmd_send_queue, 0);
- imapc_command_send_more(conn, *cmd_p);
- }
- }
- conn->state = state;
}
static void imapc_connection_lfiles_free(struct imapc_connection *conn)
imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_DISCONNECTED);
}
+static void imapc_connection_reconnect(struct imapc_connection *conn)
+{
+ if (conn->selected_box != NULL)
+ imapc_client_mailbox_reconnect(conn->selected_box);
+ else
+ imapc_connection_disconnect(conn);
+}
+
static void ATTR_FORMAT(2, 3)
imapc_connection_input_error(struct imapc_connection *conn,
const char *fmt, ...)
va_start(va, fmt);
i_error("imapc(%s): Server sent invalid input: %s",
conn->name, t_strdup_vprintf(fmt, va));
- sleep(3600);
imapc_connection_disconnect(conn);
va_end(va);
}
timeout_remove(&conn->to);
imapc_connection_set_state(conn, IMAPC_CONNECTION_STATE_DONE);
imapc_login_callback(conn, reply);
+
+ imapc_command_send_more(conn);
}
static const char *
cmd = imapc_connection_cmd(conn, imapc_connection_login_cb,
conn);
+ imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN);
if ((set->master_user == NULL &&
need_literal(set->username) && need_literal(set->password)) ||
}
cmd = imapc_connection_cmd(conn, imapc_connection_starttls_cb,
conn);
+ imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN);
imapc_command_send(cmd, "STARTTLS");
return;
}
/* capabilities weren't sent in the banner. ask for them. */
cmd = imapc_connection_cmd(conn, imapc_connection_capability_cb,
conn);
+ imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_PRELOGIN);
imapc_command_send(cmd, "CAPABILITY");
} else {
imapc_connection_starttls(conn);
} else if (cmds_count > 0 && cmds[0]->wait_for_literal) {
/* reply for literal */
cmds[0]->wait_for_literal = FALSE;
- imapc_command_send_more(conn, cmds[0]);
+ imapc_command_send_more(conn);
} else {
imapc_connection_input_error(conn, "Unexpected '+': %s", line);
return -1;
i_error("imapc(%s): Server disconnected: %s",
conn->name, errstr != NULL ? errstr : "");
}
- imapc_connection_disconnect(conn);
+ imapc_connection_reconnect(conn);
}
imapc_connection_unref(&conn);
}
}
void imapc_connection_connect(struct imapc_connection *conn,
- imapc_command_callback_t *callback, void *context)
+ imapc_command_callback_t *login_callback,
+ void *login_context)
{
struct dns_lookup_settings dns_set;
- i_assert(conn->login_callback == NULL);
if (conn->fd != -1) {
- i_assert(callback == NULL);
+ i_assert(login_callback == NULL);
return;
}
- conn->login_callback = callback;
- conn->login_context = context;
+ i_assert(conn->login_callback == NULL);
+ conn->login_callback = login_callback;
+ conn->login_context = login_context;
imapc_connection_input_reset(conn);
array_delete(&conn->cmd_send_queue, 0, 1);
array_append(&conn->cmd_wait_list, &cmd, 1);
- if (array_count(&conn->cmd_send_queue) > 0 &&
- conn->state == IMAPC_CONNECTION_STATE_DONE) {
- /* send the next command in queue */
- struct imapc_command *const *cmd2_p =
- array_idx(&conn->cmd_send_queue, 0);
- imapc_command_send_more(conn, *cmd2_p);
- }
+ /* send the next command in queue */
+ imapc_command_send_more(conn);
}
static struct imapc_command_stream *
}
}
-static void imapc_command_send_more(struct imapc_connection *conn,
- struct imapc_command *cmd)
+static void imapc_command_send_more(struct imapc_connection *conn)
{
+ struct imapc_command *const *cmds, *cmd;
struct imapc_command_reply reply;
const unsigned char *p, *data;
- unsigned int seek_pos, start_pos, end_pos, size;
+ unsigned int count, seek_pos, start_pos, end_pos, size;
int ret;
- i_assert(!cmd->wait_for_literal);
+ cmds = array_get(&conn->cmd_send_queue, &count);
+ if (count == 0)
+ return;
+ cmd = cmds[0];
+
+ if ((cmd->flags & IMAPC_COMMAND_FLAG_PRELOGIN) == 0 &&
+ conn->state != IMAPC_CONNECTION_STATE_DONE) {
+ /* wait until we're fully connected */
+ return;
+ }
+ if (cmd->wait_for_literal) {
+ /* wait until we received '+' */
+ return;
+ }
+
i_assert(cmd->send_pos < cmd->data->used);
if (cmd->box == NULL) {
(cmd->flags & IMAPC_COMMAND_FLAG_SELECT) != 0) {
/* SELECT/EXAMINE command */
imapc_connection_set_selecting(cmd->box);
- } else if (!imapc_client_mailbox_is_connected(cmd->box)) {
+ } else if (!imapc_client_mailbox_is_opened(cmd->box)) {
/* shouldn't normally happen */
memset(&reply, 0, sizeof(reply));
reply.text_without_resp = reply.text_full = "Mailbox not open";
reply.state = IMAPC_COMMAND_STATE_BAD;
+
+ array_delete(&conn->cmd_send_queue, 0, 1);
imapc_command_reply_free(cmd, &reply);
return;
}
switch (conn->state) {
case IMAPC_CONNECTION_STATE_AUTHENTICATING:
array_insert(&conn->cmd_send_queue, 0, &cmd, 1);
- imapc_command_send_more(conn, cmd);
+ imapc_command_send_more(conn);
break;
case IMAPC_CONNECTION_STATE_DONE:
if (cmd->idle) {
}
array_append(&conn->cmd_send_queue, &cmd, 1);
- if (array_count(&conn->cmd_send_queue) == 1)
- imapc_command_send_more(conn, cmd);
+ imapc_command_send_more(conn);
break;
default:
array_append(&conn->cmd_send_queue, &cmd, 1);
if (imapc_command_get_sending_stream(cmds[0]) != NULL &&
!cmds[0]->wait_for_literal) {
/* we're sending a stream. send more. */
- imapc_command_send_more(conn, cmds[0]);
+ imapc_command_send_more(conn);
}
}
o_stream_uncork(conn->output);
}
}
- if (conn->selected_box == NULL && conn->selecting_box == NULL) {
- i_assert(conn->state == IMAPC_CONNECTION_STATE_DISCONNECTED);
- } else {
+ if (conn->selected_box != NULL || conn->selecting_box != NULL) {
i_assert(conn->selected_box == box ||
conn->selecting_box == box);
void imapc_connection_deinit(struct imapc_connection **conn);
void imapc_connection_connect(struct imapc_connection *conn,
- imapc_command_callback_t *callback,
- void *context);
+ imapc_command_callback_t *login_callback,
+ void *login_context);
void imapc_connection_disconnect(struct imapc_connection *conn);
void imapc_connection_ioloop_changed(struct imapc_connection *conn);
void imapc_connection_input_pending(struct imapc_connection *conn);
array_delete(&msgmap->uids, rseq-1, 1);
}
+
+void imapc_msgmap_reset(struct imapc_msgmap *msgmap)
+{
+ array_clear(&msgmap->uids);
+ msgmap->uid_next = 1;
+}
void imapc_msgmap_append(struct imapc_msgmap *msgmap,
uint32_t rseq, uint32_t uid);
void imapc_msgmap_expunge(struct imapc_msgmap *msgmap, uint32_t rseq);
+void imapc_msgmap_reset(struct imapc_msgmap *msgmap);
#endif
if (mail->expunged || imapc_mail_is_expunged(mail))
mail_set_expunged(mail);
- else if (!imapc_client_mailbox_is_connected(mbox->client_box)) {
+ else if (!imapc_client_mailbox_is_opened(mbox->client_box)) {
/* we've already logged a disconnection error */
mail_storage_set_internal_error(mail->box->storage);
} else {
/* maybe the remote server is buggy and has become confused.
try reconnecting. */
}
- imapc_client_mailbox_disconnect(mbox->client_box);
+ imapc_client_mailbox_reconnect(mbox->client_box);
}
static struct mail_index_view *
#include "imap-arg.h"
#include "imap-resp-code.h"
#include "mailbox-tree.h"
-#include "imapc-mail.h"
#include "imapc-client.h"
#include "imapc-connection.h"
+#include "imapc-msgmap.h"
+#include "imapc-mail.h"
#include "imapc-list.h"
#include "imapc-sync.h"
#include "imapc-settings.h"
void imapc_storage_run(struct imapc_storage *storage)
{
- imapc_client_run_pre(storage->client);
- imapc_client_run_post(storage->client);
+ do {
+ imapc_client_run(storage->client);
+ } while (storage->reopen_count > 0);
}
void imapc_simple_callback(const struct imapc_command_reply *reply,
return 0;
}
+static bool imapc_mailbox_want_examine(struct imapc_mailbox *mbox)
+{
+ return (mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) == 0 &&
+ ((mbox->box.flags & MAILBOX_FLAG_READONLY) != 0 ||
+ (mbox->box.flags & MAILBOX_FLAG_SAVEONLY) != 0);
+}
+
+static void
+imapc_mailbox_reopen_callback(const struct imapc_command_reply *reply,
+ void *context)
+{
+ struct imapc_mailbox *mbox = context;
+
+ i_assert(mbox->storage->reopen_count > 0);
+ mbox->storage->reopen_count--;
+ mbox->selecting = FALSE;
+ if (reply->state != IMAPC_COMMAND_STATE_OK) {
+ mail_storage_set_critical(mbox->box.storage,
+ "imapc: Reopening mailbox '%s' failed: %s",
+ mbox->box.name, reply->text_full);
+ imapc_client_mailbox_reconnect(mbox->client_box);
+ }
+ imapc_client_stop(mbox->storage->client);
+}
+
+static void imapc_mailbox_reopen(void *context)
+{
+ struct imapc_mailbox *mbox = context;
+ struct imapc_command *cmd;
+
+ /* we're reconnecting and need to reopen the mailbox */
+ mbox->initial_sync_done = FALSE;
+ mbox->selecting = TRUE;
+ imapc_msgmap_reset(imapc_client_mailbox_get_msgmap(mbox->client_box));
+
+ cmd = imapc_client_mailbox_cmd(mbox->client_box,
+ imapc_mailbox_reopen_callback, mbox);
+ imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_SELECT);
+ if (imapc_mailbox_want_examine(mbox))
+ imapc_command_sendf(cmd, "EXAMINE %s", mbox->box.name);
+ else
+ imapc_command_sendf(cmd, "SELECT %s", mbox->box.name);
+ mbox->storage->reopen_count++;
+}
+
static void
imapc_mailbox_open_callback(const struct imapc_command_reply *reply,
void *context)
{
struct imapc_open_context *ctx = context;
+ ctx->mbox->selecting = FALSE;
if (reply->state == IMAPC_COMMAND_STATE_OK)
ctx->ret = 0;
else if (reply->state == IMAPC_COMMAND_STATE_NO) {
{
struct imapc_command *cmd;
struct imapc_open_context ctx;
- bool examine = TRUE;
i_assert(mbox->client_box == NULL);
- examine = (mbox->box.flags & MAILBOX_FLAG_DROP_RECENT) == 0 &&
- ((mbox->box.flags & MAILBOX_FLAG_READONLY) != 0 ||
- (mbox->box.flags & MAILBOX_FLAG_SAVEONLY) != 0);
-
mbox->client_box =
imapc_client_mailbox_open(mbox->storage->client, mbox);
+ imapc_client_mailbox_set_reopen_cb(mbox->client_box,
+ imapc_mailbox_reopen, mbox);
mbox->selecting = TRUE;
ctx.mbox = mbox;
cmd = imapc_client_mailbox_cmd(mbox->client_box,
imapc_mailbox_open_callback, &ctx);
imapc_command_set_flags(cmd, IMAPC_COMMAND_FLAG_SELECT);
- if (examine)
+ if (imapc_mailbox_want_examine(mbox))
imapc_command_sendf(cmd, "EXAMINE %s", mbox->box.name);
else
imapc_command_sendf(cmd, "SELECT %s", mbox->box.name);
while (ctx.ret == -2)
imapc_storage_run(mbox->storage);
- mbox->selecting = FALSE;
return ctx.ret;
}
if (mail_index_view_is_inconsistent(box->view))
return TRUE;
- return !imapc_client_mailbox_is_connected(mbox->client_box);
+ return !imapc_client_mailbox_is_opened(mbox->client_box);
}
struct mail_storage imapc_storage = {
struct imapc_mailbox *cur_status_box;
struct mailbox_status *cur_status;
+ unsigned int reopen_count;
ARRAY_DEFINE(untagged_callbacks, struct imapc_storage_event_callback);
};