dovecot-2.0: imap, pop3: Added back ability to run post-login sc...
dovecot at dovecot.org
dovecot at dovecot.org
Sat Dec 12 01:38:38 EET 2009
details: http://hg.dovecot.org/dovecot-2.0/rev/e9cbc461c525
changeset: 10459:e9cbc461c525
user: Timo Sirainen <tss at iki.fi>
date: Fri Dec 11 18:38:33 2009 -0500
description:
imap, pop3: Added back ability to run post-login scripts.
diffstat:
6 files changed, 351 insertions(+), 19 deletions(-)
src/imap/main.c | 5 +
src/lib-master/master-login.c | 196 +++++++++++++++++++++++++++++++++++++----
src/lib-master/master-login.h | 1
src/pop3/main.c | 5 +
src/util/Makefile.am | 7 +
src/util/script.c | 156 ++++++++++++++++++++++++++++++++
diffs (truncated from 543 to 300 lines):
diff -r 395b53cebaac -r e9cbc461c525 src/imap/main.c
--- a/src/imap/main.c Fri Dec 11 18:36:19 2009 -0500
+++ b/src/imap/main.c Fri Dec 11 18:38:33 2009 -0500
@@ -4,6 +4,7 @@
#include "ioloop.h"
#include "istream.h"
#include "ostream.h"
+#include "abspath.h"
#include "str.h"
#include "base64.h"
#include "process-title.h"
@@ -252,6 +253,7 @@ int main(int argc, char *argv[])
};
enum master_service_flags service_flags = 0;
enum mail_storage_service_flags storage_service_flags = 0;
+ const char *postlogin_socket_path;
if (IS_STANDALONE() && getuid() == 0 &&
net_getpeername(1, NULL, NULL) == 0) {
@@ -273,6 +275,8 @@ int main(int argc, char *argv[])
&argc, &argv, NULL);
if (master_getopt(master_service) > 0)
return FATAL_DEFAULT;
+ postlogin_socket_path = t_abspath(argv[1]);
+
master_service_init_finish(master_service);
master_service_set_die_callback(master_service, imap_die);
@@ -294,6 +298,7 @@ int main(int argc, char *argv[])
} T_END;
} else {
master_login = master_login_init(master_service, "auth-master",
+ postlogin_socket_path,
login_client_connected);
io_loop_set_running(current_ioloop);
}
diff -r 395b53cebaac -r e9cbc461c525 src/lib-master/master-login.c
--- a/src/lib-master/master-login.c Fri Dec 11 18:36:19 2009 -0500
+++ b/src/lib-master/master-login.c Fri Dec 11 18:38:33 2009 -0500
@@ -6,12 +6,16 @@
#include "fdpass.h"
#include "fd-close-on-exec.h"
#include "llist.h"
+#include "str.h"
+#include "strescape.h"
#include "master-service-private.h"
#include "master-login.h"
#include "master-login-auth.h"
#include <sys/stat.h>
#include <unistd.h>
+
+#define MASTER_LOGIN_POSTLOGIN_TIMEOUT_MSECS (60*1000)
struct master_login_connection {
struct master_login_connection *prev, *next;
@@ -22,17 +26,28 @@ struct master_login_connection {
struct ostream *output;
};
+struct master_login_postlogin {
+ struct master_login_client *client;
+
+ int fd;
+ struct io *io;
+ struct timeout *to;
+ string_t *input;
+};
+
struct master_login {
struct master_service *service;
master_login_callback_t *callback;
struct master_login_connection *conns;
struct master_login_auth *auth;
+ char *postlogin_socket_path;
};
static void master_login_conn_deinit(struct master_login_connection **_conn);
struct master_login *
master_login_init(struct master_service *service, const char *auth_socket_path,
+ const char *postlogin_socket_path,
master_login_callback_t *callback)
{
struct master_login *login;
@@ -41,6 +56,7 @@ master_login_init(struct master_service
login->service = service;
login->callback = callback;
login->auth = master_login_auth_init(auth_socket_path);
+ login->postlogin_socket_path = i_strdup(postlogin_socket_path);
service->login_connections = TRUE;
return login;
}
@@ -57,6 +73,7 @@ void master_login_deinit(struct master_l
master_login_conn_deinit(&conn);
}
+ i_free(login->postlogin_socket_path);
i_free(login);
}
@@ -126,44 +143,185 @@ master_login_conn_read_request(struct ma
return 1;
}
-static void
-master_login_auth_callback(const char *const *auth_args, void *context)
-{
- struct master_login_client *client = context;
- struct master_auth_reply reply;
+static void master_login_auth_finish(struct master_login_client *client,
+ const char *const *auth_args)
+{
struct master_login_connection *conn = client->conn;
struct master_service *service = conn->login->service;
bool close_config;
+
+ close_config = service->master_status.available_count == 0 &&
+ service->service_count_left == 1;
+
+ conn->login->callback(client, auth_args[0], auth_args+1);
+ i_free(client);
+
+ if (close_config) {
+ /* we're dying as soon as this connection closes. */
+ master_service_close_config_fd(service);
+ master_login_conn_deinit(&conn);
+ }
+}
+
+static void master_login_client_free(struct master_login_client *client)
+{
+ if (close(client->fd) < 0)
+ i_error("close(fd_read client) failed: %m");
+ i_free(client);
+}
+
+static void master_login_postlogin_free(struct master_login_postlogin *pl)
+{
+ timeout_remove(&pl->to);
+ io_remove(&pl->io);
+ if (close(pl->fd) < 0)
+ i_error("close(postlogin) failed: %m");
+ str_free(&pl->input);
+ i_free(pl);
+}
+
+static void master_login_postlogin_input(struct master_login_postlogin *pl)
+{
+ struct master_login *login = pl->client->conn->login;
+ char buf[1024];
+ const char **auth_args, **p;
+ unsigned int len;
+ ssize_t ret;
+ int fd = -1;
+
+ while ((ret = fd_read(pl->fd, buf, sizeof(buf), &fd)) > 0) {
+ if (fd != -1) {
+ /* post-login script replaced fd */
+ if (close(pl->client->fd) < 0)
+ i_error("close(client) failed: %m");
+ pl->client->fd = fd;
+ }
+ str_append_n(pl->input, buf, ret);
+ }
+
+ len = str_len(pl->input);
+ if (len > 0 && str_c(pl->input)[len-1] == '\n') {
+ /* finished reading the input */
+ str_truncate(pl->input, len-1);
+ } else {
+ if (ret < 0) {
+ if (errno == EAGAIN)
+ return;
+
+ i_error("fd_read(%s) failed: %m",
+ login->postlogin_socket_path);
+ } else {
+ i_error("fd_read(%s) failed: disconnected",
+ login->postlogin_socket_path);
+ }
+ master_login_client_free(pl->client);
+ master_login_postlogin_free(pl);
+ master_service_client_connection_destroyed(login->service);
+ return;
+ }
+
+ auth_args = t_strsplit(str_c(pl->input), "\t");
+ for (p = auth_args; *p != NULL; p++)
+ *p = str_tabunescape(t_strdup_noconst(*p));
+
+ master_login_auth_finish(pl->client, auth_args);
+ master_login_postlogin_free(pl);
+}
+
+static void master_login_postlogin_timeout(struct master_login_postlogin *pl)
+{
+ struct master_login *login = pl->client->conn->login;
+
+ i_error("%s: Timeout waiting for post-login script to finish, aborting",
+ login->postlogin_socket_path);
+
+ master_login_client_free(pl->client);
+ master_login_postlogin_free(pl);
+ master_service_client_connection_destroyed(login->service);
+}
+
+static int master_login_postlogin(struct master_login_client *client,
+ const char *const *auth_args)
+{
+ struct master_login *login = client->conn->login;
+ struct master_login_postlogin *pl;
+ string_t *str;
+ unsigned int i;
+ int fd;
+ ssize_t ret;
+
+ fd = net_connect_unix_with_retries(login->postlogin_socket_path, 1000);
+ if (fd == -1) {
+ i_error("net_connect_unix(%s) failed: %m",
+ login->postlogin_socket_path);
+ return -1;
+ }
+
+ str = t_str_new(256);
+ str_printfa(str, "%s\t%s", net_ip2addr(&client->auth_req.local_ip),
+ net_ip2addr(&client->auth_req.remote_ip));
+ for (i = 0; auth_args[i] != NULL; i++) {
+ str_append_c(str, '\t');
+ str_tabescape_write(str, auth_args[i]);
+ }
+ str_append_c(str, '\n');
+ ret = fd_send(fd, client->fd, str_data(str), str_len(str));
+ if (ret != (ssize_t)str_len(str)) {
+ if (ret < 0) {
+ i_error("write(%s) failed: %m",
+ login->postlogin_socket_path);
+ } else {
+ i_error("write(%s) failed: partial write",
+ login->postlogin_socket_path);
+ }
+ (void)close(fd);
+ return -1;
+ }
+ net_set_nonblock(fd, TRUE);
+
+ pl = i_new(struct master_login_postlogin, 1);
+ pl->client = client;
+ pl->fd = fd;
+ pl->io = io_add(fd, IO_READ, master_login_postlogin_input, pl);
+ pl->to = timeout_add(MASTER_LOGIN_POSTLOGIN_TIMEOUT_MSECS,
+ master_login_postlogin_timeout, pl);
+ pl->input = str_new(default_pool, 512);
+ return 0;
+}
+
+static void
+master_login_auth_callback(const char *const *auth_args, void *context)
+{
+ struct master_login_client *client = context;
+ struct master_auth_reply reply;
+ struct master_service *service = client->conn->login->service;
memset(&reply, 0, sizeof(reply));
reply.tag = client->auth_req.tag;
reply.status = auth_args != NULL ? MASTER_AUTH_STATUS_OK :
MASTER_AUTH_STATUS_INTERNAL_ERROR;
reply.mail_pid = getpid();
- o_stream_send(conn->output, &reply, sizeof(reply));
+ o_stream_send(client->conn->output, &reply, sizeof(reply));
if (auth_args == NULL || auth_args[0] == NULL) {
if (auth_args != NULL)
i_error("login client: Username missing from auth reply");
- if (close(client->fd) < 0)
- i_error("close(fd_read client) failed: %m");
- i_free(client);
+ master_login_client_free(client);
return;
}
i_assert(service->master_status.available_count > 0);
service->master_status.available_count--;
master_status_update(service);
- close_config = service->master_status.available_count == 0 &&
- service->service_count_left == 1;
-
- conn->login->callback(client, auth_args[0], auth_args+1);
- i_free(client);
-
- if (close_config) {
- /* we're dying as soon as this connection closes. */
- master_service_close_config_fd(service);
- master_login_conn_deinit(&conn);
+
+ if (client->conn->login->postlogin_socket_path == NULL)
+ master_login_auth_finish(client, auth_args);
+ else {
+ /* execute post-login scripts before finishing auth */
+ if (master_login_postlogin(client, auth_args) < 0) {
More information about the dovecot-cvs
mailing list