Mercurial > dovecot > core-2.2
view src/plugins/quota/quota-status.c @ 21811:aadb3940e4df
quota: Add quota_max_mail_size setting
author | Martti Rannanjärvi <martti.rannanjarvi@dovecot.fi> |
---|---|
date | Wed, 22 Mar 2017 17:17:41 +0200 |
parents | 370b09372136 |
children | e78a42ead488 |
line wrap: on
line source
/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "ostream.h" #include "connection.h" #include "restrict-access.h" #include "master-service.h" #include "master-service-settings.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; uoff_t size; }; static enum quota_protocol protocol; static struct mail_storage_service_ctx *storage_service; static struct connection_list *clients; static char *nouser_reply; 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 enum quota_alloc_result quota_check(struct mail_user *user, uoff_t mail_size, const char **error_r) { struct quota_user *quser = QUOTA_USER_CONTEXT(user); struct mail_namespace *ns; struct mailbox *box; struct quota_transaction_context *ctx; enum quota_alloc_result ret; if (quser == NULL) { /* no quota for user */ return QUOTA_ALLOC_RESULT_OK; } ns = mail_namespace_find_inbox(user->namespaces); box = mailbox_alloc(ns->list, "INBOX", MAILBOX_FLAG_POST_SESSION); ctx = quota_transaction_begin(box); ret = quota_test_alloc(ctx, I_MAX(1, mail_size)); *error_r = quota_alloc_result_errstr(ret, ctx); quota_transaction_rollback(&ctx); mailbox_free(&box); 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 *value = NULL, *error; int ret; if (client->recipient == NULL) { o_stream_send_str(client->conn.output, "action=DUNNO\n\n"); return; } i_zero(&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) { value = nouser_reply; } else if (ret > 0) { enum quota_alloc_result qret = quota_check(user, client->size, &error); switch (qret) { case QUOTA_ALLOC_RESULT_OK: /* under quota */ value = mail_user_plugin_getenv(user, "quota_status_success"); if (value == NULL) value = "OK"; break; case QUOTA_ALLOC_RESULT_OVER_MAXSIZE: /* even over maximum quota */ case QUOTA_ALLOC_RESULT_OVER_QUOTA_LIMIT: value = mail_user_plugin_getenv(user, "quota_status_toolarge"); /* fall through */ case QUOTA_ALLOC_RESULT_OVER_QUOTA: if (value == NULL) value = mail_user_plugin_getenv(user, "quota_status_overquota"); if (value == NULL) value = t_strdup_printf("554 5.2.2 %s", error); break; case QUOTA_ALLOC_RESULT_TEMPFAIL: ret = -1; break; } value = t_strdup(value); /* user's pool is being freed */ mail_user_unref(&user); mail_storage_service_user_unref(&service_user); } if (ret < 0) { /* temporary failure */ o_stream_send_str(client->conn.output, t_strdup_printf( "action=DEFER_IF_PERMIT %s\n\n", error)); } else { o_stream_send_str(client->conn.output, t_strdup_printf("action=%s\n\n", value)); } } 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); else if (strncmp(line, "size=", 5) == 0) { if (str_to_uoff(line+5, &client->size) < 0) client->size = 0; } 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) { struct mail_storage_service_input input; const struct setting_parser_info *user_info; const struct setting_parser_context *set_parser; const struct mail_user_settings *user_set; const char *value, *error; pool_t pool; 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); i_zero(&input); input.service = "quota-status"; input.module = "mail"; input.username = ""; pool = pool_alloconly_create("service all settings", 4096); if (mail_storage_service_read_settings(storage_service, &input, pool, &user_info, &set_parser, &error) < 0) i_fatal("%s", error); user_set = master_service_settings_parser_get_others(master_service, set_parser)[0]; value = mail_user_set_plugin_getenv(user_set, "quota_status_nouser"); nouser_reply = value != NULL ? i_strdup(value) : i_strdup("REJECT Unknown user"); pool_unref(&pool); } static void main_deinit(void) { i_free(nouser_reply); connection_list_deinit(&clients); mail_storage_service_deinit(&storage_service); } int main(int argc, char *argv[]) { enum master_service_flags service_flags = MASTER_SERVICE_FLAG_KEEP_CONFIG_OPEN; int c; protocol = QUOTA_PROTOCOL_UNKNOWN; master_service = master_service_init("quota-status", service_flags, &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, "quota-status: "); main_preinit(); main_init(); master_service_init_finish(master_service); master_service_run(master_service, client_connected); main_deinit(); master_service_deinit(&master_service); return 0; }