]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added quota-status service for asking if user is over quota.
authorTimo Sirainen <tss@iki.fi>
Tue, 19 Feb 2013 15:36:59 +0000 (17:36 +0200)
committerTimo Sirainen <tss@iki.fi>
Tue, 19 Feb 2013 15:36:59 +0000 (17:36 +0200)
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

.hgignore
src/plugins/quota/Makefile.am
src/plugins/quota/quota-status.c [new file with mode: 0644]

index 74d5d9859096e334c5d2b9100855b82c62d1cf47..12c4beae515d2cb9db787edb97045759dddc8dd0 100644 (file)
--- a/.hgignore
+++ b/.hgignore
@@ -97,6 +97,7 @@ src/util/tcpwrap
 src/plugins/fts/xml2text
 src/plugins/quota/rquota_xdr.c
 src/plugins/quota/rquota.h
+src/plugins/quota/quota-status
 
 syntax: regexp
 src/.*/test-[^\.]*$
index cdf409e9d34c1cf247d5d33119751bec4774798a..8aeeaf49344bbab2da014b0ce294732cc0f29573 100644 (file)
@@ -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_dist_sources = \
         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 @@ doveadm_module_LTLIBRARIES = \
 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)
diff --git a/src/plugins/quota/quota-status.c b/src/plugins/quota/quota-status.c
new file mode 100644 (file)
index 0000000..fe7407e
--- /dev/null
@@ -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(&quota_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;
+}