/* Copyright (c) 2014-2018 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>
/* we only need enough for "DONE\r\n<tag> IDLE\r\n" */
/* 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. */
enum imap_client_input_state {
};
struct imap_client_notify {
int fd;
};
struct imap_client {
int fd;
const char *log_prefix;
unsigned int next_read_threshold;
bool unhibernate_queued;
bool input_pending;
};
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 {
}
}
{
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 {
}
{ 's', "imap-hibernate", "service" },
/* NOTE: keep this synced with lib-storage's
mail_user_var_expand_table() */
};
return tab;
}
static int
{
break;
}
}
return 1;
}
{
}
{
i_set_failure_prefix("imap-hibernate: ");
}
{
return NULL;
}
struct imap_client *
{
{ "userdb", imap_client_var_expand_func_userdb },
};
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;
}
{
if (client->unhibernate_queued)
}
}
{
imap-hibernate, but that shouldn't matter much. */
}
"\n", NULL));
}
}
}
{
}
{
} else {
}
}
}
{
(c1->input_pending ?
(c2->input_pending ?
return -1;
return 1;
return 0;
}
{
if (!imap_client_try_move_back(client))
return;
}
}
void imap_clients_init(void)
{
}
void imap_clients_deinit(void)
{
while (imap_clients != NULL) {
}
}