Mercurial > dovecot > original-hg > dovecot-2.1
changeset 14908:980be1dc80c2
Added quota-status service for asking if user is over quota.
Implemented Postfix-compatible policy server protocol initially. Usage:
service quota-status {
executable = quota-status -p postfix
unix_listener /var/spool/postfix/private/quota-status {
user = postfix
}
client_limit = 1
}
Postfix:
smtpd_recipient_restrictions =
...
check_policy_service unix:private/quota-status
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 19 Feb 2013 17:36:59 +0200 |
parents | b96df105ec55 |
children | f12f2f2f7459 |
files | .hgignore src/plugins/quota/Makefile.am src/plugins/quota/quota-status.c |
diffstat | 3 files changed, 243 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Sun Feb 17 12:03:06 2013 +0200 +++ b/.hgignore Tue Feb 19 17:36:59 2013 +0200 @@ -97,6 +97,7 @@ src/plugins/fts/xml2text src/plugins/quota/rquota_xdr.c src/plugins/quota/rquota.h +src/plugins/quota/quota-status syntax: regexp src/.*/test-[^\.]*$
--- a/src/plugins/quota/Makefile.am Sun Feb 17 12:03:06 2013 +0200 +++ b/src/plugins/quota/Makefile.am Tue Feb 19 17:36:59 2013 +0200 @@ -1,7 +1,10 @@ doveadm_moduledir = $(moduledir)/doveadm +pkglibexec_PROGRAMS = quota-status + AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-master \ -I$(top_srcdir)/src/lib-dict \ -I$(top_srcdir)/src/lib-index \ -I$(top_srcdir)/src/lib-mail \ @@ -27,6 +30,17 @@ quota-plugin.c \ quota-storage.c +quota_common_objects = \ + quota.lo \ + quota-count.lo \ + quota-fs.lo \ + quota-dict.lo \ + quota-dirsize.lo \ + quota-maildir.lo \ + quota-plugin.lo \ + quota-storage.lo \ + $(RQUOTA_XDR_LO) + lib10_quota_plugin_la_SOURCES = $(quota_dist_sources) nodist_lib10_quota_plugin_la_SOURCES = $(RQUOTA_XDR) @@ -36,8 +50,22 @@ lib10_doveadm_quota_plugin_la_SOURCES = \ doveadm-quota.c +quota_status_SOURCES = \ + quota-status.c + +quota_status_LDADD = \ + $(quota_common_objects) \ + $(LIBDOVECOT_STORAGE) \ + $(LIBDOVECOT) \ + $(MODULE_LIBS) +quota_status_DEPENDENCIES = \ + $(quota_common_objects) \ + $(LIBDOVECOT_STORAGE_DEPS) \ + $(LIBDOVECOT_DEPS) + if HAVE_RQUOTA RQUOTA_XDR = rquota_xdr.c +RQUOTA_XDR_LO = rquota_xdr.lo #RQUOTA_X = /usr/include/rpcsvc/rquota.x RQUOTA_X = $(srcdir)/rquota.x rquota_xdr.c: Makefile $(RQUOTA_X)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/quota/quota-status.c Tue Feb 19 17:36:59 2013 +0200 @@ -0,0 +1,214 @@ +/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "module-dir.h" +#include "ostream.h" +#include "connection.h" +#include "restrict-access.h" +#include "master-service.h" +#include "mail-namespace.h" +#include "mail-storage.h" +#include "mail-storage-settings.h" +#include "mail-storage-service.h" +#include "quota-private.h" +#include "quota-plugin.h" + +enum quota_protocol { + QUOTA_PROTOCOL_UNKNOWN = 0, + QUOTA_PROTOCOL_POSTFIX +}; + +struct quota_client { + struct connection conn; + + char *recipient; +}; + +static enum quota_protocol protocol; +static struct mail_storage_service_ctx *storage_service; +static struct connection_list *clients; + +static struct module quota_module = { + .path = "quota", + .name = "quota" +}; + +static void client_connected(struct master_service_connection *conn) +{ + struct quota_client *client; + + client = i_new(struct quota_client, 1); + connection_init_server(clients, &client->conn, + "(quota client)", conn->fd, conn->fd); + master_service_client_connection_accept(conn); +} + +static void client_reset(struct quota_client *client) +{ + i_free_and_null(client->recipient); +} + +static int quota_check(struct mail_user *user, const char **error_r) +{ + struct quota_user *quser = QUOTA_USER_CONTEXT(user); + struct mail_namespace *ns; + struct mailbox *box; + struct quota_transaction_context *ctx; + bool too_large; + int ret; + + if (quser == NULL) { + /* no quota for user */ + return 1; + } + + ns = mail_namespace_find_inbox(user->namespaces); + box = mailbox_alloc(ns->list, "INBOX", 0); + + ctx = quota_transaction_begin(box); + ret = quota_test_alloc(ctx, 1, &too_large); + quota_transaction_rollback(&ctx); + + mailbox_free(&box); + + if (ret < 0) + *error_r = "Internal quota calculation error"; + else if (ret == 0) + *error_r = quser->quota->set->quota_exceeded_msg; + return ret; +} + +static void client_handle_request(struct quota_client *client) +{ + struct mail_storage_service_input input; + struct mail_storage_service_user *service_user; + struct mail_user *user; + const char *error; + int ret; + + if (client->recipient == NULL) { + o_stream_send_str(client->conn.output, "action=DUNNO\n\n"); + return; + } + + memset(&input, 0, sizeof(input)); + input.username = client->recipient; + + ret = mail_storage_service_lookup_next(storage_service, &input, + &service_user, &user, &error); + restrict_access_allow_coredumps(TRUE); + if (ret == 0) { + o_stream_send_str(client->conn.output, + "action=REJECT Unknown user\n\n"); + } else if (ret > 0) { + if ((ret = quota_check(user, &error)) > 0) + o_stream_send_str(client->conn.output, "action=OK\n\n"); + else if (ret == 0) { + o_stream_send_str(client->conn.output, t_strdup_printf( + "action=552 5.2.2 %s\n\n", error)); + } + mail_user_unref(&user); + mail_storage_service_user_free(&service_user); + } + if (ret < 0) { + o_stream_send_str(client->conn.output, t_strdup_printf( + "action=DEFER_IF_PERMIT %s\n\n", error)); + } +} + +static int client_input_line(struct connection *conn, const char *line) +{ + struct quota_client *client = (struct quota_client *)conn; + + if (*line == '\0') { + o_stream_cork(conn->output); + client_handle_request(client); + o_stream_uncork(conn->output); + client_reset(client); + return 1; + } + if (client->recipient == NULL && + strncmp(line, "recipient=", 10) == 0) + client->recipient = i_strdup(line + 10); + return 1; +} + +static void client_destroy(struct connection *conn) +{ + struct quota_client *client = (struct quota_client *)conn; + + connection_deinit(&client->conn); + client_reset(client); + i_free(client); + + master_service_client_connection_destroyed(master_service); +} + +static struct connection_settings client_set = { + .input_max_size = (size_t)-1, + .output_max_size = (size_t)-1, + .client = FALSE +}; + +static const struct connection_vfuncs client_vfuncs = { + .destroy = client_destroy, + .input_line = client_input_line +}; + +static void main_preinit(void) +{ + restrict_access_by_env(NULL, FALSE); + restrict_access_allow_coredumps(TRUE); +} + +static void main_init(void) +{ + clients = connection_list_init(&client_set, &client_vfuncs); + storage_service = mail_storage_service_init(master_service, NULL, + MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP | + MAIL_STORAGE_SERVICE_FLAG_TEMP_PRIV_DROP | + MAIL_STORAGE_SERVICE_FLAG_ENABLE_CORE_DUMPS | + MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR | + MAIL_STORAGE_SERVICE_FLAG_NO_PLUGINS); + quota_plugin_init("a_module); +} + +static void main_deinit(void) +{ + connection_list_deinit(&clients); + quota_plugin_deinit(); + mail_storage_service_deinit(&storage_service); +} + +int main(int argc, char *argv[]) +{ + int c; + + protocol = QUOTA_PROTOCOL_UNKNOWN; + master_service = master_service_init("quota-status", 0, + &argc, &argv, "p:"); + while ((c = master_getopt(master_service)) > 0) { + switch (c) { + case 'p': + if (strcmp(optarg, "postfix") == 0) + protocol = QUOTA_PROTOCOL_POSTFIX; + else + i_fatal("Unknown -p parameter: '%s'", optarg); + break; + default: + return FATAL_DEFAULT; + } + } + if (protocol == QUOTA_PROTOCOL_UNKNOWN) + i_fatal("Missing -p parameter"); + + master_service_init_log(master_service, "doveadm: "); + main_preinit(); + master_service_init_finish(master_service); + + main_init(); + master_service_run(master_service, client_connected); + main_deinit(); + master_service_deinit(&master_service); + return 0; +}