http://dovecot.org/list/dovecot/2007-April/021429.html diff -ru dovecot-1.0.rc27.orig/src/plugins/quota/quota-plugin.c dovecot-1.0.rc27/src/plugins/quota/quota-plugin.c --- dovecot-1.0.rc27.orig/src/plugins/quota/quota-plugin.c 2007-03-06 17:34:47.000000000 +0100 +++ dovecot-1.0.rc27/src/plugins/quota/quota-plugin.c 2007-03-31 22:18:55.000000000 +0200 @@ -21,9 +21,24 @@ env = getenv("QUOTA"); if (env != NULL) { + struct quota_setup *setup; + const char *name; + unsigned int i; quota_set = quota_init(); /* Currently we support only one quota setup */ - (void)quota_setup_init(quota_set, env, TRUE); + setup = quota_setup_init(quota_set, env, TRUE); + + name = "QUOTA_WARNING"; + env = getenv(name); + i = 1; + t_push(); + while (env != NULL) { + (void)quota_warning_init(setup, env); + + name = t_strdup_printf("QUOTA_WARNING%u", ++i); + env = getenv(name); + } + t_pop(); quota_next_hook_mail_storage_created = hook_mail_storage_created; diff -ru dovecot-1.0.rc27.orig/src/plugins/quota/quota-private.h dovecot-1.0.rc27/src/plugins/quota/quota-private.h --- dovecot-1.0.rc27.orig/src/plugins/quota/quota-private.h 2007-03-12 21:34:50.000000000 +0100 +++ dovecot-1.0.rc27/src/plugins/quota/quota-private.h 2007-03-17 00:06:23.000000000 +0100 @@ -22,6 +22,9 @@ /* List of quota roots. It's array because there shouldn't be many. */ array_t ARRAY_DEFINE(roots, struct quota_root *); + /* List of quota warnings. There should probably be few. */ + array_t ARRAY_DEFINE(warnings, struct quota_warning *); + unsigned int user_root:1; }; @@ -86,6 +89,21 @@ unsigned int idx; }; +enum quota_warning_limit_kind { + QUOTA_WARNING_NO_LIMIT, + QUOTA_WARNING_PERCENT_LIMIT, + QUOTA_WARNING_ABSOLUTE_LIMIT +}; + +struct quota_warning { + struct quota_setup *setup; + uint64_t storage_limit; + unsigned int count_limit; + enum quota_warning_limit_kind storage_limit_kind:2; + enum quota_warning_limit_kind count_limit_kind:2; + char *command; +}; + struct quota_transaction_context { array_t ARRAY_DEFINE(root_transactions, struct quota_root_transaction_context *); diff -ru dovecot-1.0.rc27.orig/src/plugins/quota/quota.c dovecot-1.0.rc27/src/plugins/quota/quota.c --- dovecot-1.0.rc27.orig/src/plugins/quota/quota.c 2007-03-06 17:34:47.000000000 +0100 +++ dovecot-1.0.rc27/src/plugins/quota/quota.c 2007-03-31 22:35:22.000000000 +0200 @@ -6,6 +6,8 @@ #include "quota-private.h" #include "quota-fs.h" +#include + unsigned int quota_module_id = 0; extern struct quota_backend quota_backend_dict; @@ -59,6 +61,7 @@ setup->data = i_strdup(data); setup->user_root = user_root; ARRAY_CREATE(&setup->roots, default_pool, struct quota_root *, 4); + ARRAY_CREATE(&setup->warnings, default_pool, struct quota_warning *, 4); t_push(); p = strchr(setup->data, ':'); @@ -107,6 +110,16 @@ } array_free(&setup->roots); + + while (array_count(&setup->warnings) > 0) { + struct quota_warning *const *warning; + + warning = array_idx(&setup->warnings, 0); + quota_warning_deinit(*warning); + } + + array_free(&setup->warnings); + i_free(setup->data); i_free(setup); } @@ -157,6 +170,82 @@ array_free(&module_contexts); } +struct quota_warning * +quota_warning_init(struct quota_setup *setup, const char *data) +{ + const char *p; + char *q; + const char *const *args; + unsigned long long val; + struct quota_warning *warning; + + warning = i_new(struct quota_warning, 1); + warning->setup = setup; + + p = strchr(data, ' '); + if (p == NULL) + i_fatal("quota warning: No command specified: %s", data); + warning->command = i_strdup(p+1); + + t_push(); + + args = t_strsplit(t_strdup_until(data, p), ":"); + for (; *args != '\0'; args++) { + if (strncmp(*args, "storage=", 8) == 0) { + val = strtoull(*args + 8, &q, 10); + if (q && (strcmp(q, "%") == 0)) { + warning->storage_limit = val; + warning->storage_limit_kind = + QUOTA_WARNING_PERCENT_LIMIT; + } else if (q && (strcmp(q, "k") == 0)) { + warning->storage_limit = val * 1024; + warning->storage_limit_kind = + QUOTA_WARNING_ABSOLUTE_LIMIT; + } else + i_error("quota warning: Malformed setting: %s", *args); + } else if (strncmp(*args, "messages=", 9) == 0) { + val = strtoull(*args + 8, &q, 10); + if (q && (strcmp(q, "%") == 0)) { + warning->count_limit = val; + warning->count_limit_kind = + QUOTA_WARNING_PERCENT_LIMIT; + } else if (q && (strcmp(q, "") == 0)) { + warning->count_limit = val; + warning->count_limit_kind = + QUOTA_WARNING_ABSOLUTE_LIMIT; + } else + i_error("quota warning: Malformed setting: %s", *args); + } else { + i_error("quota warning: Unknown setting: %s", *args); + } + } + + t_pop(); + + array_append(&setup->warnings, &warning, 1); + + return warning; +} + +void quota_warning_deinit(struct quota_warning *warning) +{ + struct quota_warning *const *warnings; + unsigned int i, count; + + /* remove from setup */ + warnings = array_get(&warning->setup->warnings, &count); + for (i = 0; i < count; i++) { + if (warnings[i] == warning) { + array_delete(&warning->setup->warnings, i, 1); + break; + } + } + i_assert(i != count); + + i_free(warning->command); + i_free(warning); +} + void quota_add_user_storage(struct quota *quota, struct mail_storage *storage) { struct quota_setup *const *setups; @@ -388,15 +477,62 @@ i_free(ctx); } +#define CHECK_PERCENT(val, cur, diff, limit) \ + (100 * ((limit) - (cur) - (diff)) > (val) * (limit)) +#define CHECK_ABSOLUTE(val, cur, diff, limit) \ + ((limit) - (cur) - (diff) > (val)) + +#define CHECK2_PERCENT(val, cur, diff, size, limit) \ + (CHECK_PERCENT(val, cur, diff, limit) \ + && !CHECK_PERCENT(val, cur, (diff)+(size), limit)) +#define CHECK2_ABSOLUTE(val, cur, diff, size, limit) \ + (CHECK_ABSOLUTE(val, cur, diff, limit) \ + && !CHECK_ABSOLUTE(val, cur, (diff)+(size), limit)) + +#define CHECK(kind, val, cur, diff, size, limit) \ + ((((kind) == QUOTA_WARNING_PERCENT_LIMIT) \ + && (CHECK2_PERCENT(val, cur, diff, size, limit))) \ + || (((kind) == QUOTA_WARNING_ABSOLUTE_LIMIT) \ + && (CHECK2_ABSOLUTE(val, cur, diff, size, limit)))) + int quota_default_try_alloc_bytes(struct quota_root_transaction_context *ctx, uoff_t size, bool *too_large_r) { + struct quota_warning *const * warnings; + unsigned int i, count; int ret; ret = quota_default_test_alloc_bytes(ctx, size, too_large_r); if (ret <= 0 || ctx->disabled) return ret; + warnings = array_get(&ctx->root->setup->warnings, &count); + + for (i = 0; i < count; i++) { + struct quota_warning *warning = warnings[i]; + bool run_command = FALSE; + + if (CHECK(warning->storage_limit_kind, + warning->storage_limit, + ctx->bytes_current, + ctx->bytes_diff, + size, + ctx->bytes_limit)) + run_command = TRUE; + + if (CHECK(warning->count_limit_kind, + warning->count_limit, + ctx->count_current, + ctx->count_diff, + 1, + ctx->count_limit)) + run_command = TRUE; + + if (run_command) + system(warning->command); + + } + ctx->count_diff++; ctx->bytes_diff += size; return 1; diff -ru dovecot-1.0.rc27.orig/src/plugins/quota/quota.h dovecot-1.0.rc27/src/plugins/quota/quota.h --- dovecot-1.0.rc27.orig/src/plugins/quota/quota.h 2007-03-06 17:34:47.000000000 +0100 +++ dovecot-1.0.rc27/src/plugins/quota/quota.h 2007-03-16 23:21:33.000000000 +0100 @@ -31,6 +31,10 @@ quota_root_init(struct quota_setup *setup, const char *name); void quota_root_deinit(struct quota_root *root); +struct quota_warning * +quota_warning_init(struct quota_setup *setup, const char *data); +void quota_warning_deinit(struct quota_warning *warning); + /* List all quota roots. Returned quota roots are freed by quota_deinit(). */ struct quota_root_iter *quota_root_iter_init(struct mailbox *box); struct quota_root *quota_root_iter_next(struct quota_root_iter *iter);