imap-urlauth-worker.c revision 3858a7a5da361c35f1e6e50c8e3214dc0cf379d6
/* Copyright (c) 2013-2016 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "ioloop.h"
#include "net.h"
#include "fdpass.h"
#include "istream.h"
#include "ostream.h"
#include "str.h"
#include "str-sanitize.h"
#include "strescape.h"
#include "llist.h"
#include "hostpid.h"
#include "var-expand.h"
#include "process-title.h"
#include "randgen.h"
#include "restrict-access.h"
#include "settings-parser.h"
#include "master-service.h"
#include "master-interface.h"
#include "mail-storage.h"
#include "mail-storage-service.h"
#include "mail-namespace.h"
#include "imap-url.h"
#include "imap-msgpart-url.h"
#include "imap-urlauth.h"
#include "imap-urlauth-fetch.h"
#include "imap-urlauth-worker-settings.h"
#include <unistd.h>
#include <sysexits.h>
#define MAX_CTRL_HANDSHAKE 255
/* max. length of input lines (URLs) */
#define MAX_INBUF_SIZE 2048
/* Disconnect client after idling this many milliseconds */
#define CLIENT_IDLE_TIMEOUT_MSECS (10*60*1000)
#define IS_STANDALONE() \
(getenv(MASTER_IS_PARENT_ENV) == NULL)
#define IMAP_URLAUTH_WORKER_PROTOCOL_MAJOR_VERSION 1
#define IMAP_URLAUTH_WORKER_PROTOCOL_MINOR_VERSION 0
struct client {
struct client *prev, *next;
int fd_in, fd_out, fd_ctrl;
struct io *io, *ctrl_io;
struct istream *input, *ctrl_input;
struct ostream *output, *ctrl_output;
struct timeout *to_idle;
char *access_user;
ARRAY_TYPE(string) access_apps;
struct mail_storage_service_user *service_user;
struct mail_user *mail_user;
struct imap_urlauth_context *urlauth_ctx;
struct imap_msgpart_url *url;
struct istream *msg_part_input;
uoff_t msg_part_size;
/* settings: */
const struct imap_urlauth_worker_settings *set;
const struct mail_storage_settings *mail_set;
unsigned int debug:1;
unsigned int finished:1;
unsigned int waiting_input:1;
unsigned int version_received:1;
unsigned int access_received:1;
unsigned int access_anonymous:1;
};
static bool verbose_proctitle = FALSE;
static struct mail_storage_service_ctx *storage_service;
struct client *imap_urlauth_worker_clients;
unsigned int imap_urlauth_worker_client_count;
static void client_destroy(struct client *client);
static void client_abort(struct client *client, const char *reason);
static int client_run_url(struct client *client);
static void client_input(struct client *client);
static bool client_handle_input(struct client *client);
static int client_output(struct client *client);
static void client_ctrl_input(struct client *client);
static void imap_urlauth_worker_refresh_proctitle(void)
{
struct client *client = imap_urlauth_worker_clients;
string_t *title;
if (!verbose_proctitle)
return;
title = t_str_new(128);
str_append_c(title, '[');
switch (imap_urlauth_worker_client_count) {
case 0:
str_append(title, "idling");
break;
case 1:
if (client->mail_user == NULL)
str_append(title, client->access_user);
else {
str_append(title, client->access_user);
str_append(title, "->");
str_append(title, client->mail_user->username);
}
break;
default:
str_printfa(title, "%u connections",
imap_urlauth_worker_client_count);
break;
}
str_append_c(title, ']');
process_title_set(str_c(title));
}
static void client_idle_timeout(struct client *client)
{
if (client->url != NULL) {
client_abort(client,
"Session closed for inactivity in reading our output");
} else {
client_destroy(client);
}
}
static struct client *client_create(int fd)
{
struct client *client;
/* always use nonblocking I/O */
net_set_nonblock(fd, TRUE);
client = i_new(struct client, 1);
i_array_init(&client->access_apps, 16);
client->fd_in = -1;
client->fd_out = -1;
client->fd_ctrl = fd;
client->access_anonymous = TRUE; /* default until overridden */
client->ctrl_io = io_add(fd, IO_READ, client_ctrl_input, client);
client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
client_idle_timeout, client);
imap_urlauth_worker_client_count++;
DLLIST_PREPEND(&imap_urlauth_worker_clients, client);
imap_urlauth_worker_refresh_proctitle();
return client;
}
static struct client *
client_create_standalone(const char *access_user,
const char *const *access_applications,
int fd_in, int fd_out, bool debug)
{
struct client *client;
/* always use nonblocking I/O */
net_set_nonblock(fd_in, TRUE);
net_set_nonblock(fd_out, TRUE);
client = i_new(struct client, 1);
i_array_init(&client->access_apps, 16);
client->fd_in = fd_in;
client->fd_out = fd_out;
client->fd_ctrl = -1;
if (access_user != NULL && *access_user != '\0')
client->access_user = i_strdup(access_user);
else {
client->access_user = i_strdup("anonymous");
client->access_anonymous = TRUE;
}
if (access_applications != NULL) {
const char *const *apps = access_applications;
for (; *apps != NULL; apps++) {
char *app = i_strdup(*apps);
array_append(&client->access_apps, &app, 1);
}
}
client->debug = debug;
client->input = i_stream_create_fd(fd_in, MAX_INBUF_SIZE, FALSE);
client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE);
client->io = io_add(fd_in, IO_READ, client_input, client);
client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
client_idle_timeout, client);
o_stream_set_flush_callback(client->output, client_output, client);
imap_urlauth_worker_client_count++;
DLLIST_PREPEND(&imap_urlauth_worker_clients, client);
i_set_failure_prefix("imap-urlauth[%s](%s): ",
my_pid, client->access_user);
return client;
}
static void client_abort(struct client *client, const char *reason)
{
i_error("%s", reason);
client_destroy(client);
}
static void client_destroy(struct client *client)
{
char **app;
i_set_failure_prefix("imap-urlauth[%s](%s): ",
my_pid, client->access_user);
if (client->url != NULL) {
/* deinitialize url */
i_stream_close(client->input);
o_stream_close(client->output);
(void)client_run_url(client);
i_assert(client->url == NULL);
}
imap_urlauth_worker_client_count--;
DLLIST_REMOVE(&imap_urlauth_worker_clients, client);
if (client->urlauth_ctx != NULL)
imap_urlauth_deinit(&client->urlauth_ctx);
if (client->mail_user != NULL)
mail_user_unref(&client->mail_user);
if (client->io != NULL)
io_remove(&client->io);
if (client->ctrl_io != NULL)
io_remove(&client->ctrl_io);
if (client->to_idle != NULL)
timeout_remove(&client->to_idle);
if (client->input != NULL)
i_stream_destroy(&client->input);
if (client->output != NULL)
o_stream_destroy(&client->output);
if (client->ctrl_input != NULL)
i_stream_destroy(&client->ctrl_input);
if (client->ctrl_output != NULL)
o_stream_destroy(&client->ctrl_output);
if (client->fd_in >= 0)
net_disconnect(client->fd_in);
if (client->fd_out >= 0 && client->fd_in != client->fd_out)
net_disconnect(client->fd_out);
if (client->fd_ctrl >= 0)
net_disconnect(client->fd_ctrl);
if (client->service_user != NULL)
mail_storage_service_user_free(&client->service_user);
i_free(client->access_user);
array_foreach_modifiable(&client->access_apps, app)
i_free(*app);
array_free(&client->access_apps);
i_free(client);
imap_urlauth_worker_refresh_proctitle();
master_service_client_connection_destroyed(master_service);
}
static int client_run_url(struct client *client)
{
const unsigned char *data;
size_t size;
ssize_t ret = 0;
while (i_stream_read_more(client->msg_part_input, &data, &size) > 0) {
if ((ret = o_stream_send(client->output, data, size)) < 0)
break;
i_stream_skip(client->msg_part_input, ret);
if (o_stream_get_buffer_used_size(client->output) >= 4096) {
if ((ret = o_stream_flush(client->output)) < 0)
break;
if (ret == 0)
return 0;
}
}
if (client->output->closed || ret < 0) {
imap_msgpart_url_free(&client->url);
return -1;
}
if (client->msg_part_input->eof) {
o_stream_send(client->output, "\n", 1);
imap_msgpart_url_free(&client->url);
return 1;
}
return 0;
}
static void clients_destroy_all(void)
{
while (imap_urlauth_worker_clients != NULL)
client_destroy(imap_urlauth_worker_clients);
}
static void ATTR_FORMAT(2, 3)
client_send_line(struct client *client, const char *fmt, ...)
{
va_list va;
ssize_t ret;
if (client->output->closed)
return;
va_start(va, fmt);
T_BEGIN {
string_t *str;
str = t_str_new(256);
str_vprintfa(str, fmt, va);
str_append(str, "\n");
ret = o_stream_send(client->output,
str_data(str), str_len(str));
i_assert(ret < 0 || (size_t)ret == str_len(str));
} T_END;
va_end(va);
}
static int
client_fetch_urlpart(struct client *client, const char *url,
enum imap_urlauth_fetch_flags url_flags,
const char **bpstruct_r, bool *binary_with_nuls_r,
const char **errormsg_r)
{
const char *error;
struct imap_msgpart_open_result mpresult;
enum mail_error error_code;
int ret;
*bpstruct_r = NULL;
*errormsg_r = NULL;
*binary_with_nuls_r = FALSE;
ret = imap_urlauth_fetch(client->urlauth_ctx, url,
&client->url, &error_code, &error);
if (ret <= 0) {
if (ret < 0)
return -1;
error = t_strdup_printf("Failed to fetch URLAUTH \"%s\": %s",
url, error);
if (client->debug)
i_debug("%s", error);
/* don't leak info about existence/accessibility
of mailboxes */
if (error_code == MAIL_ERROR_PARAMS)
*errormsg_r = error;
return 0;
}
if ((url_flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0)
imap_msgpart_url_set_decode_to_binary(client->url);
if ((url_flags & IMAP_URLAUTH_FETCH_FLAG_BODYPARTSTRUCTURE) != 0) {
ret = imap_msgpart_url_get_bodypartstructure(client->url,
bpstruct_r, &error);
if (ret <= 0) {
*errormsg_r = t_strdup_printf(
"Failed to read URLAUTH \"%s\": %s", url, error);
if (client->debug)
i_debug("%s", *errormsg_r);
return ret;
}
}
/* if requested, read the message part the URL points to */
if ((url_flags & IMAP_URLAUTH_FETCH_FLAG_BODY) != 0 ||
(url_flags & IMAP_URLAUTH_FETCH_FLAG_BINARY) != 0) {
ret = imap_msgpart_url_read_part(client->url, &mpresult, &error);
if (ret <= 0) {
*errormsg_r = t_strdup_printf(
"Failed to read URLAUTH \"%s\": %s", url, error);
if (client->debug)
i_debug("%s", *errormsg_r);
return ret;
}
client->msg_part_size = mpresult.size;
client->msg_part_input = mpresult.input;
*binary_with_nuls_r = mpresult.binary_decoded_input_has_nuls;
}
return 1;
}
static int client_fetch_url(struct client *client, const char *url,
enum imap_urlauth_fetch_flags url_flags)
{
string_t *response;
const char *bpstruct, *errormsg;
bool binary_with_nuls;
int ret;
i_assert(client->url == NULL);
client->msg_part_size = 0;
client->msg_part_input = NULL;
if (client->debug)
i_debug("Fetching URLAUTH %s", url);
/* fetch URL */
ret = client_fetch_urlpart(client, url, url_flags, &bpstruct,
&binary_with_nuls, &errormsg);
if (ret <= 0) {
/* fetch failed */
if (client->url != NULL)
imap_msgpart_url_free(&client->url);
/* don't send error details to anonymous users: just to be sure
that no information about the target user account is unduly
leaked. */
if (client->access_anonymous || errormsg == NULL)
client_send_line(client, "NO");
else {
client_send_line(client, "NO\terror=%s",
str_tabescape(errormsg));
}
if (ret < 0) {
/* fetch failed badly */
client_abort(client, "Session aborted: Fatal failure while fetching URL");
}
return 0;
}
response = t_str_new(256);
str_append(response, "OK");
if (binary_with_nuls)
str_append(response, "\thasnuls");
if (bpstruct != NULL) {
str_append(response, "\tbpstruct=");
str_append(response, str_tabescape(bpstruct));
if (client->debug) {
i_debug("Fetched URLAUTH yielded BODYPARTSTRUCTURE (%s)",
bpstruct);
}
}
/* return content */
o_stream_cork(client->output);
if (client->msg_part_size == 0 || client->msg_part_input == NULL) {
/* empty */
str_append(response, "\t0");
client_send_line(client, "%s", str_c(response));
imap_msgpart_url_free(&client->url);
client->url = NULL;
if (client->debug)
i_debug("Fetched URLAUTH yielded empty result");
} else {
/* actual content */
str_printfa(response, "\t%"PRIuUOFF_T, client->msg_part_size);
client_send_line(client, "%s", str_c(response));
if (client->debug) {
i_debug("Fetched URLAUTH yielded %"PRIuUOFF_T" bytes "
"of %smessage data", client->msg_part_size,
(binary_with_nuls ? "binary " : ""));
}
if (client_run_url(client) < 0) {
client_abort(client,
"Session aborted: Fatal failure while transferring URL");
return 0;
}
}
if (client->url != NULL) {
/* URL not finished */
o_stream_set_flush_pending(client->output, TRUE);
client->waiting_input = TRUE;
}
o_stream_uncork(client->output);
return client->url != NULL ? 0 : 1;
}
static int
client_handle_command(struct client *client, const char *cmd,
const char *const *args, const char **error_r)
{
int ret;
*error_r = NULL;
/* "URL"["\tbody"]["\tbinary"]["\tbpstruct"]"\t"<url>:
fetch URL (meta)data */
if (strcmp(cmd, "URL") == 0) {
enum imap_urlauth_fetch_flags url_flags = 0;
const char *url;
if (*args == NULL) {
*error_r = "URL: Missing URL parameter";
return -1;
}
url = *args;
args++;
while (*args != NULL) {
if (strcasecmp(*args, "body") == 0)
url_flags |= IMAP_URLAUTH_FETCH_FLAG_BODY;
else if (strcasecmp(*args, "binary") == 0)
url_flags |= IMAP_URLAUTH_FETCH_FLAG_BINARY;
else if (strcasecmp(*args, "bpstruct") == 0)
url_flags |= IMAP_URLAUTH_FETCH_FLAG_BODYPARTSTRUCTURE;
args++;
}
if (url_flags == 0)
url_flags = IMAP_URLAUTH_FETCH_FLAG_BODY;
T_BEGIN {
ret = client_fetch_url(client, url, url_flags);
} T_END;
return ret;
}
/* "END": unselect current user (closes worker) */
if (strcmp(cmd, "END") == 0) {
if (args[0] != NULL) {
*error_r = "END: Invalid number of parameters";
return -1;
}
client->finished = TRUE;
if (client->ctrl_output != NULL)
(void)o_stream_send_str(client->ctrl_output, "FINISHED\n");
client_destroy(client);
return 0;
}
*error_r = t_strconcat("Unknown or inappropriate command: ", cmd, NULL);
return -1;
}
static int
client_handle_user_command(struct client *client, const char *cmd,
const char *const *args, const char **error_r)
{
struct mail_storage_service_input input;
struct imap_urlauth_worker_settings *set;
struct mail_storage_service_user *user;
struct imap_urlauth_config config;
struct mail_user *mail_user;
const char *error;
unsigned int count;
int ret;
/* "USER\t"<username> */
*error_r = NULL;
/* check command syntax */
if (strcmp(cmd, "USER") != 0) {
*error_r = t_strconcat("Unknown or inappropriate command: ",
cmd, NULL);
return -1;
}
if (args[0] == NULL || args[1] != NULL) {
*error_r = "USER: Invalid number of parameters";
return -1;
}
/* lookup user */
memset(&input, 0, sizeof(input));
input.module = "imap-urlauth-worker";
input.service = "imap-urlauth-worker";
input.username = args[0];
if (client->debug)
i_debug("Looking up user %s", input.username);
ret = mail_storage_service_lookup_next(storage_service, &input,
&user, &mail_user, &error);
if (ret < 0) {
i_error("Failed to lookup user %s: %s", input.username, error);
client_abort(client, "Session aborted: Failed to lookup user");
return 0;
} else if (ret == 0) {
if (client->debug)
i_debug("User %s doesn't exist", input.username);
client_send_line(client, "NO");
return 1;
}
client->debug = mail_user->mail_debug =
client->debug || mail_user->mail_debug;
/* drop privileges */
restrict_access_allow_coredumps(TRUE);
set = mail_storage_service_user_get_set(user)[1];
settings_var_expand(&imap_urlauth_worker_setting_parser_info, set,
mail_user->pool,
mail_user_var_expand_table(mail_user));
if (set->verbose_proctitle) {
verbose_proctitle = TRUE;
imap_urlauth_worker_refresh_proctitle();
}
client->service_user = user;
client->mail_user = mail_user;
client->set = set;
if (client->debug) {
i_debug("Found user account `%s' on behalf of user `%s'",
mail_user->username, client->access_user);
}
/* initialize urlauth context */
if (*set->imap_urlauth_host == '\0') {
i_error("imap_urlauth_host setting is not configured for user %s",
mail_user->username);
client_send_line(client, "NO");
client_abort(client, "Session aborted: URLAUTH not configured");
return 0;
}
memset(&config, 0, sizeof(config));
config.url_host = set->imap_urlauth_host;
config.url_port = set->imap_urlauth_port;
config.access_user = client->access_user;
config.access_anonymous = client->access_anonymous;
config.access_applications =
(const void *)array_get(&client->access_apps, &count);
client->urlauth_ctx = imap_urlauth_init(client->mail_user, &config);
if (client->debug) {
i_debug("Providing access to user account `%s' on behalf of `%s'",
mail_user->username, client->access_user);
}
i_set_failure_prefix("imap-urlauth[%s](%s->%s): ",
my_pid, client->access_user, mail_user->username);
client_send_line(client, "OK");
return 1;
}
static bool client_handle_input(struct client *client)
{
const char *line, *cmd, *error;
int ret;
if (client->url != NULL) {
/* we're still processing a URL. wait until it's
finished. */
io_remove(&client->io);
client->io = NULL;
client->waiting_input = TRUE;
return TRUE;
}
if (client->io == NULL) {
client->io = io_add(client->fd_in, IO_READ,
client_input, client);
}
client->waiting_input = FALSE;
timeout_reset(client->to_idle);
switch (i_stream_read(client->input)) {
case -1:
/* disconnected */
if (client->ctrl_output != NULL)
(void)o_stream_send_str(client->ctrl_output, "DISCONNECTED\n");
client_destroy(client);
return FALSE;
case -2:
/* line too long, kill it */
client_abort(client, "Session aborted: Input line too long");
return FALSE;
}
while ((line = i_stream_next_line(client->input)) != NULL) {
const char *const *args = t_strsplit_tabescaped(line);
if (args[0] == NULL)
continue;
cmd = args[0]; args++;
if (client->mail_user == NULL)
ret = client_handle_user_command(client, cmd, args, &error);
else
ret = client_handle_command(client, cmd, args, &error);
if (ret <= 0) {
if (ret == 0)
break;
i_error("Client input error: %s", error);
client_abort(client, "Session aborted: Unexpected input");
return FALSE;
}
}
return TRUE;
}
static void client_input(struct client *client)
{
(void)client_handle_input(client);
}
static int client_output(struct client *client)
{
o_stream_cork(client->output);
if (o_stream_flush(client->output) < 0) {
if (client->ctrl_output != NULL)
(void)o_stream_send_str(client->ctrl_output, "DISCONNECTED\n");
client_destroy(client);
return 1;
}
timeout_reset(client->to_idle);
if (client->url != NULL) {
if (client_run_url(client) < 0) {
client_destroy(client);
return 1;
}
if (client->url == NULL && client->waiting_input) {
if (!client_handle_input(client)) {
/* client got destroyed */
return 1;
}
}
}
o_stream_uncork(client->output);
if (client->url != NULL) {
/* url not finished yet */
return 0;
} else if (client->io == NULL) {
/* data still in output buffer, get back here to add IO */
return 0;
} else {
return 1;
}
}
static int
client_ctrl_read_fds(struct client *client)
{
unsigned char data = 0;
ssize_t ret = 1;
if (client->fd_in == -1) {
ret = fd_read(client->fd_ctrl, &data,
sizeof(data), &client->fd_in);
if (ret > 0 && data == '0')
client->fd_out = client->fd_in;
}
if (ret > 0 && client->fd_out == -1) {
ret = fd_read(client->fd_ctrl, &data,
sizeof(data), &client->fd_out);
}
if (ret == 0) {
/* unexpectedly disconnected */
client_destroy(client);
return 0;
} else if (ret < 0) {
if (errno == EAGAIN)
return 0;
i_error("fd_read() failed: %m");
return -1;
} else if (data != '0') {
i_error("fd_read() returned invalid byte 0x%2x", data);
return -1;
}
if (client->fd_in == -1 || client->fd_out == -1) {
i_error("Handshake is missing a file descriptor");
return -1;
}
client->ctrl_input =
i_stream_create_fd(client->fd_ctrl, MAX_INBUF_SIZE, FALSE);
client->ctrl_output =
o_stream_create_fd(client->fd_ctrl, (size_t)-1, FALSE);
return 1;
}
static void client_ctrl_input(struct client *client)
{
const char *const *args;
const char *line;
int ret;
timeout_reset(client->to_idle);
if (client->fd_in == -1 || client->fd_out == -1) {
if ((ret = client_ctrl_read_fds(client)) <= 0) {
if (ret < 0)
client_abort(client, "FD Transfer failed");
return;
}
}
switch (i_stream_read(client->ctrl_input)) {
case -1:
/* disconnected */
client_destroy(client);
return;
case -2:
/* line too long, kill it */
client_abort(client,
"Control session aborted: Input line too long");
return;
}
if (!client->version_received) {
if ((line = i_stream_next_line(client->ctrl_input)) == NULL)
return;
if (!version_string_verify(line, "imap-urlauth-worker",
IMAP_URLAUTH_WORKER_PROTOCOL_MAJOR_VERSION)) {
i_error("imap-urlauth-worker client not compatible with this server "
"(mixed old and new binaries?) %s", line);
client_abort(client, "Control session aborted: Version mismatch");
return;
}
client->version_received = TRUE;
if (o_stream_send_str(client->ctrl_output, "OK\n") < 0) {
client_destroy(client);
return;
}
}
if (client->access_received) {
client_abort(client, "Control session aborted: Unexpected input");
return;
}
if ((line = i_stream_next_line(client->ctrl_input)) == NULL)
return;
args = t_strsplit_tabescaped(line);
if (*args == NULL || strcmp(*args, "ACCESS") != 0) {
i_error("Invalid control command: %s", str_sanitize(line, 80));
client_abort(client, "Control session aborted: Invalid command");
return;
}
args++;
if (*args == NULL) {
i_error("Invalid ACCESS command: %s", str_sanitize(line, 80));
client_abort(client, "Control session aborted: Invalid command");
return;
}
i_assert(client->access_user == NULL);
if (**args != '\0') {
client->access_user = i_strdup(*args);
client->access_anonymous = FALSE;
} else {
client->access_user = i_strdup("anonymous");
client->access_anonymous = TRUE;
}
i_set_failure_prefix("imap-urlauth[%s](%s): ",
my_pid, client->access_user);
args++;
while (*args != NULL) {
/* debug */
if (strcasecmp(*args, "debug") == 0) {
client->debug = TRUE;
/* apps=<access-application>[,<access-application,...] */
} else if (strncasecmp(*args, "apps=", 5) == 0 &&
(*args)[5] != '\0') {
const char *const *apps = t_strsplit(*args+5, ",");
while (*apps != NULL) {
char *app = i_strdup(*apps);
array_append(&client->access_apps, &app, 1);
if (client->debug) {
i_debug("User %s has URLAUTH %s access",
client->access_user, app);
}
apps++;
}
} else {
i_error("Invalid ACCESS parameter: %s", str_sanitize(*args, 80));
client_abort(client, "Control session aborted: Invalid command");
return;
}
args++;
}
client->access_received = TRUE;
if (o_stream_send_str(client->ctrl_output, "OK\n") < 0) {
client_destroy(client);
return;
}
client->input = i_stream_create_fd(client->fd_in, MAX_INBUF_SIZE, FALSE);
client->output = o_stream_create_fd(client->fd_out, (size_t)-1, FALSE);
client->io = io_add(client->fd_in, IO_READ, client_input, client);
o_stream_set_flush_callback(client->output, client_output, client);
if (client->debug) {
i_debug("Worker activated for access by user %s",
client->access_user);
}
}
static void imap_urlauth_worker_die(void)
{
/* do nothing */
}
static void main_stdio_run(const char *access_user,
const char *const *access_applications)
{
bool debug;
debug = getenv("DEBUG") != NULL;
access_user = access_user != NULL ? access_user : getenv("USER");
if (access_user == NULL && IS_STANDALONE())
access_user = getlogin();
if (access_user == NULL)
i_fatal("USER environment missing");
(void)client_create_standalone(access_user, access_applications,
STDIN_FILENO, STDOUT_FILENO, debug);
}
static void client_connected(struct master_service_connection *conn)
{
master_service_client_connection_accept(conn);
(void)client_create(conn->fd);
}
int main(int argc, char *argv[])
{
static const struct setting_parser_info *set_roots[] = {
&imap_urlauth_worker_setting_parser_info,
NULL
};
enum master_service_flags service_flags = 0;
enum mail_storage_service_flags storage_service_flags =
MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP |
MAIL_STORAGE_SERVICE_FLAG_NO_LOG_INIT;
ARRAY_TYPE (const_string) access_apps;
const char *access_user = NULL;
int c;
if (IS_STANDALONE()) {
service_flags |= MASTER_SERVICE_FLAG_STANDALONE |
MASTER_SERVICE_FLAG_STD_CLIENT;
} else {
service_flags |= MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN;
storage_service_flags |=
MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT;
}
master_service = master_service_init("imap-urlauth-worker", service_flags,
&argc, &argv, "a:");
t_array_init(&access_apps, 4);
while ((c = master_getopt(master_service)) > 0) {
switch (c) {
case 'a': {
const char *app = t_strdup(optarg);
array_append(&access_apps, &app, 1);
break;
}
default:
return FATAL_DEFAULT;
}
}
if ( optind < argc ) {
access_user = argv[optind++];
}
if (optind != argc) {
i_fatal_status(EX_USAGE, "Unknown argument: %s", argv[optind]);
}
master_service_init_log(master_service,
t_strdup_printf("imap-urlauth[%s]: ", my_pid));
master_service_set_die_callback(master_service, imap_urlauth_worker_die);
random_init();
storage_service =
mail_storage_service_init(master_service,
set_roots, storage_service_flags);
master_service_init_finish(master_service);
/* fake that we're running, so we know if client was destroyed
while handling its initial input */
io_loop_set_running(current_ioloop);
if (IS_STANDALONE()) {
T_BEGIN {
if (array_count(&access_apps) > 0) {
(void)array_append_space(&access_apps);
main_stdio_run(access_user, array_idx(&access_apps,0));
} else {
main_stdio_run(access_user, NULL);
}
} T_END;
} else {
io_loop_set_running(current_ioloop);
}
if (io_loop_is_running(current_ioloop))
master_service_run(master_service, client_connected);
clients_destroy_all();
mail_storage_service_deinit(&storage_service);
random_deinit();
master_service_deinit(&master_service);
return 0;
}