imap-client.c revision 1ef9fe877817de76eb38883ccf3833fae60e6865
/* Copyright (c) 2014-2017 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "ioloop.h"
#include "fdpass.h"
#include "hostpid.h"
#include "connection.h"
#include "iostream.h"
#include "istream.h"
#include "ostream.h"
#include "llist.h"
#include "priorityq.h"
#include "base64.h"
#include "str.h"
#include "strescape.h"
#include "var-expand.h"
#include "master-service.h"
#include "master-service-settings.h"
#include "imap-keepalive.h"
#include "imap-master-connection.h"
#include "imap-client.h"
#include <unistd.h>
#define IMAP_MASTER_SOCKET_NAME "imap-master"
/* we only need enough for "DONE\r\n<tag> IDLE\r\n" */
#define IMAP_MAX_OUTBUF 1024
/* If client has sent input and we can't recreate imap process in this
many seconds, disconnect the client. */
/* If there's a change notification and we can't recreate imap process in this
many seconds, disconnect the client. */
/* How often to try to unhibernate clients. */
#define IMAP_UNHIBERNATE_RETRY_MSECS 10
#define IMAP_CLIENT_BUFFER_FULL_ERROR "Client output buffer is full"
enum imap_client_input_state {
};
struct imap_client_notify {
int fd;
};
struct imap_client {
struct priorityq_item item;
struct imap_client_state state;
struct timeout *to_move_back;
int fd;
struct timeout *to_keepalive;
struct imap_master_connection *master_conn;
struct ioloop_context *ioloop_ctx;
const char *log_prefix;
unsigned int next_read_threshold;
bool unhibernate_queued;
bool input_pending;
};
static struct imap_client *imap_clients;
static struct priorityq *unhibernate_queue;
static struct timeout *to_unhibernate;
static const char imap_still_here_text[] = "* OK Still here\r\n";
static void imap_clients_unhibernate(void *context);
{
const char *reason;
}
static void
const char **auth_user_r)
{
const char *const *field;
unsigned int i;
*auth_user_r = NULL;
return;
}
}
static void
{
const unsigned char *input_data;
}
if (state->session_created != 0) {
}
}
}
if (state->state_size > 0) {
}
if (input_size > 0) {
}
/* IDLE continues after sending changes */
}
/* send the fd first */
if (ret < 0) {
i_error("fd_send(%s) failed: %m",
return;
}
}
static void
{
if (line[0] != '+') {
/* failed - FIXME: retry later? */
} else {
}
}
{
const struct master_service_settings *master_set;
int ret;
/* there is data buffered, so we have to disconnect you */
return TRUE;
}
if (ret > 0) {
/* success */
return TRUE;
} else if (ret < 0) {
/* failed to connect to the imap-master socket */
return TRUE;
}
/* we've waited long enough */
return TRUE;
}
return FALSE;
}
{
return;
/* imap-master socket is busy. retry in a while. */
if (client->move_back_start == 0)
if (to_unhibernate == NULL) {
}
}
static enum imap_client_input_state
{
/* skip over DONE[\r]\n */
return IMAP_CLIENT_INPUT_STATE_BAD;
if (size <= 4)
return IMAP_CLIENT_INPUT_STATE_UNKNOWN;
if (data[0] == '\r') {
}
if (size == 0)
return IMAP_CLIENT_INPUT_STATE_UNKNOWN;
if (data[0] != '\n')
return IMAP_CLIENT_INPUT_STATE_BAD;
if (size == 0)
return state;
/* skip over tag */
while(data[0] != ' ' &&
data[0] != '\r' &&
if (size == 0)
return state;
if (data[0] != ' ')
return IMAP_CLIENT_INPUT_STATE_BAD;
/* skip over IDLE[\r]\n - checking this assumes that the DONE and IDLE
are sent in the same IP packet, otherwise we'll unnecessarily
recreate the imap process and immediately resume IDLE there. if this
becomes an issue we could add a small delay to the imap process
creation and wait for the IDLE command during it. */
return state;
if (data[0] == '\r') {
}
return IMAP_CLIENT_INPUT_STATE_DONEIDLE;
}
return state;
}
{
char *old_tag;
const char *new_tag;
const char *output;
const unsigned char *data;
int ret;
/* we should read either DONE or disconnection. also handle if client
sends DONE\nIDLE simply to recreate the IDLE. */
if (size == 0) {
if (ret < 0)
return;
}
client->next_read_threshold = 0;
/* we haven't received a full DONE[\r]\n yet - wait */
return;
/* invalid input - return this to the imap process */
break;
break;
break;
/* we received DONE+IDLE, so the client simply wanted to notify
us that it's still there. continue hibernation. */
if (ret > 0)
if (ret < 0) {
return;
}
/* disconnect */
return;
} else {
}
break;
}
if (done) {
} else
}
{
else {
}
}
{
}
{
/* do not send this if there is data buffered */
return;
} else if (ret == 0)
return;
if (ret < 0) {
return;
}
/* ostream buffer size is definitely large enough for this text */
}
{
if (interval == 0)
return;
interval);
}
static const struct var_expand_table *
{
} else {
}
const struct var_expand_table stack_tab[] = {
{ 's', "imap-hibernate", "service" },
/* NOTE: keep this synced with lib-storage's
mail_user_var_expand_table() */
};
struct var_expand_table *tab;
return tab;
}
static int
{
break;
}
}
return 1;
}
{
}
{
i_set_failure_prefix("imap-hibernate: ");
}
{
return NULL;
}
struct imap_client *
{
const struct var_expand_func_table funcs[] = {
{ "userdb", imap_client_var_expand_func_userdb },
};
struct imap_client *client;
void *statebuf;
const char *ident, *error;
if (state->state_size > 0) {
}
T_BEGIN {
i_error("Failed to expand mail_log_prefix=%s: %s",
}
} T_END;
if (ident != NULL) {
}
return client;
}
{
struct imap_client_notify *notify;
if (client->unhibernate_queued)
}
}
{
imap-hibernate, but that shouldn't matter much. */
}
"\n", NULL));
}
}
}
{
struct imap_client_notify *notify;
}
{
struct imap_client_notify *notify;
} else {
}
}
}
{
(c1->input_pending ?
(c2->input_pending ?
return -1;
return 1;
return 0;
}
{
struct priorityq_item *item;
if (!imap_client_try_move_back(client))
return;
}
}
void imap_clients_init(void)
{
}
void imap_clients_deinit(void)
{
while (imap_clients != NULL) {
}
}