view src/imap/main.c @ 9219:97cdfeb57129 HEAD

Renamed headers to prevent collision if they were flattened on an install.
author Mark Washenberger
date Tue, 05 May 2009 11:57:04 -0400
parents 6324a79d3ee1
children 5d0a69504867
line wrap: on
line source

/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */

#include "imap-common.h"
#include "ioloop.h"
#include "istream.h"
#include "ostream.h"
#include "str.h"
#include "base64.h"
#include "restrict-access.h"
#include "fd-close-on-exec.h"
#include "process-title.h"
#include "master-service.h"
#include "mail-user.h"
#include "mail-storage-service.h"
#include "imap-commands.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define IS_STANDALONE() \
        (getenv("CLIENT_INPUT") == NULL)

struct client_workaround_list {
	const char *name;
	enum client_workarounds num;
};

static struct client_workaround_list client_workaround_list[] = {
	{ "delay-newmail", WORKAROUND_DELAY_NEWMAIL },
	{ "outlook-idle", 0 }, /* only for backwards compatibility */
	{ "netscape-eoh", WORKAROUND_NETSCAPE_EOH },
	{ "tb-extra-mailbox-sep", WORKAROUND_TB_EXTRA_MAILBOX_SEP },
	{ NULL, 0 }
};

static struct io *log_io = NULL;

struct master_service *service;
void (*hook_client_created)(struct client **client) = NULL;

static void log_error_callback(void *context ATTR_UNUSED)
{
	/* the log fd is closed, don't die when trying to log later */
	i_set_failure_ignore_errors(TRUE);

	master_service_stop(service);
}

static enum client_workarounds
parse_workarounds(const struct imap_settings *set)
{
        enum client_workarounds client_workarounds = 0;
        struct client_workaround_list *list;
	const char *const *str;

        str = t_strsplit_spaces(set->imap_client_workarounds, " ,");
	for (; *str != NULL; str++) {
		list = client_workaround_list;
		for (; list->name != NULL; list++) {
			if (strcasecmp(*str, list->name) == 0) {
				client_workarounds |= list->num;
				break;
			}
		}
		if (list->name == NULL)
			i_fatal("Unknown client workaround: %s", *str);
	}

	return client_workarounds;
}

static void client_add_input(struct client *client, const char *input)
{
	buffer_t *buf;
	const char *tag;
	unsigned int data_pos;

	buf = input == NULL ? NULL : t_base64_decode_str(input);
	if (buf != NULL && buf->used > 0) {
		tag = t_strndup(buf->data, buf->used);
		data_pos = strlen(tag) + 1;
		if (data_pos > buf->used &&
		    !i_stream_add_data(client->input,
				       CONST_PTR_OFFSET(buf->data, data_pos),
				       buf->used - data_pos))
			i_panic("Couldn't add client input to stream");
	} else {
		/* IMAPLOGINTAG environment is compatible with mailfront */
		tag = getenv("IMAPLOGINTAG");
	}

	if (tag == NULL) {
		client_send_line(client, t_strconcat(
			"* PREAUTH [CAPABILITY ",
			str_c(client->capability_string), "] "
			"Logged in as ", client->user->username, NULL));
	} else {
		client_send_line(client, t_strconcat(
			tag, " OK [CAPABILITY ",
			str_c(client->capability_string), "] Logged in", NULL));
	}
	(void)client_handle_input(client);
}

static void main_init(const struct imap_settings *set, struct mail_user *user,
		      bool dump_capability)
{
	struct client *client;
	struct ostream *output;

	if (set->shutdown_clients && !dump_capability) {
		/* If master dies, the log fd gets closed and we'll quit */
		log_io = io_add(STDERR_FILENO, IO_ERROR,
				log_error_callback, NULL);
	}

	clients_init();

	client = client_create(0, 1, user, set);
        client->workarounds = parse_workarounds(set);

	if (dump_capability) {
		printf("%s\n", str_c(client->capability_string));
		exit(0);
	}

	output = client->output;
	o_stream_ref(output);
	o_stream_cork(output);
	client_add_input(client, getenv("CLIENT_INPUT"));
        o_stream_uncork(output);
	o_stream_unref(&output);
}

static void main_deinit(void)
{
	if (log_io != NULL)
		io_remove(&log_io);
	clients_deinit();
}

static void client_connected(const struct master_service_connection *conn)
{
	/* FIXME: we can't handle this yet */
	(void)close(conn->fd);
}

int main(int argc, char *argv[], char *envp[])
{
	const struct setting_parser_info *set_roots[] = {
		&imap_setting_parser_info,
		NULL
	};
	enum master_service_flags service_flags = 0;
	enum mail_storage_service_flags storage_service_flags = 0;
	struct mail_storage_service_input input;
	struct mail_user *mail_user;
	const struct imap_settings *set;
	bool dump_capability;
	const char *value;
	int c;

	if (IS_STANDALONE() && getuid() == 0 &&
	    net_getpeername(1, NULL, NULL) == 0) {
		printf("* BAD [ALERT] imap binary must not be started from "
		       "inetd, use imap-login instead.\n");
		return 1;
	}

	if (IS_STANDALONE())
		service_flags |= MASTER_SERVICE_FLAG_STANDALONE;
	else
		service_flags |= MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT;

	dump_capability = getenv("DUMP_CAPABILITY") != NULL;
	if (dump_capability) {
		storage_service_flags |=
			MAIL_STORAGE_SERVICE_FLAG_NO_RESTRICT_ACCESS;
	}

	service = master_service_init("imap", service_flags, argc, argv);
	while ((c = getopt(argc, argv, master_service_getopt_string())) > 0) {
		if (!master_service_parse_option(service, c, optarg))
			exit(FATAL_DEFAULT);
	}

	memset(&input, 0, sizeof(input));
	input.username = getenv("USER");
	if (input.username == NULL) {
		if (IS_STANDALONE())
			input.username = getlogin();
		if (input.username == NULL)
			i_fatal("USER environment missing");
	}
	if ((value = getenv("IP")) != NULL)
		net_addr2ip(value, &input.remote_ip);
	if ((value = getenv("LOCAL_IP")) != NULL)
		net_addr2ip(value, &input.local_ip);

	/* plugins may want to add commands, so this needs to be called early */
	commands_init();

	mail_user = mail_storage_service_init_user(service, &input, set_roots,
						   storage_service_flags);
	set = mail_storage_service_get_settings(service);
	restrict_access_allow_coredumps(TRUE);

        process_title_init(argv, envp);

	/* fake that we're running, so we know if client was destroyed
	   while initializing */
	io_loop_set_running(current_ioloop);

	T_BEGIN {
		main_init(set, mail_user, dump_capability);
	} T_END;
	if (io_loop_is_running(current_ioloop))
		master_service_run(service, client_connected);

	main_deinit();
	mail_storage_service_deinit_user();
	commands_deinit();

	master_service_deinit(&service);
	return 0;
}