]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
quota: Introduce quota_alloc_result return type
authorMartti Rannanjärvi <martti.rannanjarvi@dovecot.fi>
Thu, 23 Mar 2017 11:48:04 +0000 (13:48 +0200)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Mon, 27 Mar 2017 09:57:06 +0000 (12:57 +0300)
src/plugins/quota/quota-status.c
src/plugins/quota/quota-storage.c
src/plugins/quota/quota.c
src/plugins/quota/quota.h

index 61a3510b3b3a9078144fa63a4c605b5af3d0713b..35d59dd92401d2eb2f7874919891b14a7abdaedf 100644 (file)
@@ -45,34 +45,29 @@ static void client_reset(struct quota_client *client)
        i_free_and_null(client->recipient);
 }
 
-static int
-quota_check(struct mail_user *user, uoff_t mail_size,
-           const char **error_r, bool *too_large_r)
+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;
-       int ret;
+       enum quota_alloc_result ret;
 
        if (quser == NULL) {
                /* no quota for user */
-               return 1;
+               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), too_large_r);
+       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);
-
-       if (ret < 0)
-               *error_r = "Internal quota calculation error";
-       else if (ret == 0)
-               *error_r = quser->quota->set->quota_exceeded_msg;
        return ret;
 }
 
@@ -82,7 +77,6 @@ static void client_handle_request(struct quota_client *client)
        struct mail_storage_service_user *service_user;
        struct mail_user *user;
        const char *value = NULL, *error;
-       bool too_large;
        int ret;
 
        if (client->recipient == NULL) {
@@ -99,20 +93,30 @@ static void client_handle_request(struct quota_client *client)
        if (ret == 0) {
                value = nouser_reply;
        } else if (ret > 0) {
-               if ((ret = quota_check(user, client->size, &error, &too_large)) > 0) {
-                       /* under quota */
-                       value = mail_user_plugin_getenv(user, "quota_status_success");
+               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";
-               } else if (ret == 0) {
-                       if (too_large) {
-                               /* even over maximum quota */
-                               value = mail_user_plugin_getenv(user, "quota_status_toolarge");
-                       }
+                       break;
+               /* 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");
+                               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);
index ad9f390952fb4837cb329dc3b10389244d7df8a2..f19612062ec89c10061d0dec7fd6c7cd6426eaec 100644 (file)
@@ -46,6 +46,24 @@ static MODULE_CONTEXT_DEFINE_INIT(quota_mail_module, &mail_module_register);
 static MODULE_CONTEXT_DEFINE_INIT(quota_mailbox_list_module,
                                  &mailbox_list_module_register);
 
+static void quota_set_storage_error(struct quota_transaction_context *qt,
+                                   struct mail_storage *storage,
+                                   enum quota_alloc_result res)
+{
+       const char *errstr = quota_alloc_result_errstr(res, qt);
+       switch (res) {
+       case QUOTA_ALLOC_RESULT_OVER_QUOTA_LIMIT:
+       case QUOTA_ALLOC_RESULT_OVER_QUOTA:
+               mail_storage_set_error(storage, MAIL_ERROR_NOQUOTA, errstr);
+               break;
+       case QUOTA_ALLOC_RESULT_TEMPFAIL:
+               mail_storage_set_internal_error(storage);
+               break;
+       case QUOTA_ALLOC_RESULT_OK:
+               i_unreached();
+       }
+}
+
 static void quota_mail_expunge(struct mail *_mail)
 {
        struct mail_private *mail = (struct mail_private *)_mail;
@@ -95,14 +113,13 @@ quota_get_status(struct mailbox *box, enum mailbox_status_items items,
 {
        struct quota_mailbox *qbox = QUOTA_CONTEXT(box);
        struct quota_transaction_context *qt;
-       bool too_large;
        int ret = 0;
 
        if ((items & STATUS_CHECK_OVER_QUOTA) != 0) {
                qt = quota_transaction_begin(box);
-               if ((ret = quota_test_alloc(qt, 0, &too_large)) == 0) {
-                       mail_storage_set_error(box->storage, MAIL_ERROR_NOQUOTA,
-                                              qt->quota->set->quota_exceeded_msg);
+               enum quota_alloc_result qret = quota_test_alloc(qt, 0);
+               if (qret != QUOTA_ALLOC_RESULT_OK) {
+                       quota_set_storage_error(qt, box->storage, qret);
                        ret = -1;
                }
                quota_transaction_rollback(&qt);
@@ -216,8 +233,7 @@ static int quota_check(struct mail_save_context *ctx, struct mailbox *src_box)
 {
        struct mailbox_transaction_context *t = ctx->transaction;
        struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
-       int ret;
-       bool too_large;
+       enum quota_alloc_result ret;
 
        i_assert(!ctx->moving || src_box != NULL);
 
@@ -230,20 +246,20 @@ static int quota_check(struct mail_save_context *ctx, struct mailbox *src_box)
                return 0;
        }
 
-       ret = quota_try_alloc(qt, ctx->dest_mail, &too_large);
-       if (ret > 0)
+       ret = quota_try_alloc(qt, ctx->dest_mail);
+       switch (ret) {
+       case QUOTA_ALLOC_RESULT_OK:
                return 0;
-       else if (ret == 0) {
-               mail_storage_set_error(t->box->storage, MAIL_ERROR_NOQUOTA,
-                                      qt->quota->set->quota_exceeded_msg);
-               return -1;
-       } else {
+       case QUOTA_ALLOC_RESULT_TEMPFAIL:
                /* allow saving anyway. don't log an error, because at this
                   point we can't give very informative error without API
                   changes. the real error should have been logged already
                   (except if this was due to quota calculation on background,
                   then we intentionally don't want to log anything) */
                return 0;
+       default:
+               quota_set_storage_error(qt, t->box->storage, ret);
+               return -1;
        }
 }
 
@@ -279,7 +295,6 @@ quota_save_begin(struct mail_save_context *ctx, struct istream *input)
        struct quota_transaction_context *qt = QUOTA_CONTEXT(t);
        struct quota_mailbox *qbox = QUOTA_CONTEXT(t->box);
        uoff_t size;
-       int ret;
 
        if (!ctx->moving && i_stream_get_size(input, TRUE, &size) > 0) {
                /* Input size is known, check for quota immediately. This
@@ -291,17 +306,19 @@ quota_save_begin(struct mail_save_context *ctx, struct istream *input)
                   I think these don't really matter though compared to the
                   benefit of giving "out of quota" error before sending the
                   full mail. */
-               bool too_large;
 
-               ret = quota_test_alloc(qt, size, &too_large);
-               if (ret == 0) {
-                       mail_storage_set_error(t->box->storage,
-                               MAIL_ERROR_NOQUOTA,
-                               qt->quota->set->quota_exceeded_msg);
-                       return -1;
-               } else if (ret < 0) {
+               enum quota_alloc_result qret = quota_test_alloc(qt, size);
+               switch (qret) {
+               case QUOTA_ALLOC_RESULT_OK:
+                       /* Great, there is space. */
+                       break;
+               case QUOTA_ALLOC_RESULT_TEMPFAIL:
                        /* allow saving anyway. don't log an error - see
                           quota_check() for reasons. */
+                       break;
+               default:
+                       quota_set_storage_error(qt, t->box->storage, qret);
+                       return -1;
                }
        }
 
index f5c3a90d12ebb34733395e0dd8b0fa211759a201..63206eb9d5364dd52f9797dd3b00df13b3bea1f6 100644 (file)
@@ -218,6 +218,21 @@ quota_root_add(struct quota_settings *quota_set, struct mail_user *user,
        return 0;
 }
 
+const char *quota_alloc_result_errstr(enum quota_alloc_result res,
+               struct quota_transaction_context *qt)
+{
+       switch (res) {
+       case QUOTA_ALLOC_RESULT_OK:
+               return "OK";
+       case QUOTA_ALLOC_RESULT_TEMPFAIL:
+               return "Internal quota calculation error";
+       case QUOTA_ALLOC_RESULT_OVER_QUOTA_LIMIT:
+       case QUOTA_ALLOC_RESULT_OVER_QUOTA:
+               return qt->quota->set->quota_exceeded_msg;
+       }
+       i_unreached();
+}
+
 int quota_user_read_settings(struct mail_user *user,
                             struct quota_settings **set_r,
                             const char **error_r)
@@ -1150,17 +1165,16 @@ void quota_transaction_rollback(struct quota_transaction_context **_ctx)
        i_free(ctx);
 }
 
-int quota_try_alloc(struct quota_transaction_context *ctx,
-                   struct mail *mail, bool *too_large_r)
+enum quota_alloc_result quota_try_alloc(struct quota_transaction_context *ctx,
+                                       struct mail *mail)
 {
        uoff_t size;
-       int ret;
 
        if (quota_transaction_set_limits(ctx) < 0)
-               return -1;
+               return QUOTA_ALLOC_RESULT_TEMPFAIL;
 
        if (ctx->no_quota_updates)
-               return 1;
+               return QUOTA_ALLOC_RESULT_OK;
 
        if (mail_get_physical_size(mail, &size) < 0) {
                enum mail_error error;
@@ -1169,15 +1183,15 @@ int quota_try_alloc(struct quota_transaction_context *ctx,
                if (error == MAIL_ERROR_EXPUNGED) {
                        /* mail being copied was already expunged. it'll fail,
                           so just return success for the quota allocated. */
-                       return 1;
+                       return QUOTA_ALLOC_RESULT_OK;
                }
                i_error("quota: Failed to get mail size (box=%s, uid=%u): %s",
                        mail->box->vname, mail->uid, errstr);
-               return -1;
+               return QUOTA_ALLOC_RESULT_TEMPFAIL;
        }
 
-       ret = quota_test_alloc(ctx, size, too_large_r);
-       if (ret <= 0)
+       enum quota_alloc_result ret = quota_test_alloc(ctx, size);
+       if (ret != QUOTA_ALLOC_RESULT_OK)
                return ret;
        /* with quota_try_alloc() we want to keep track of how many bytes
           we've been adding/removing, so disable auto_updating=TRUE
@@ -1186,22 +1200,32 @@ int quota_try_alloc(struct quota_transaction_context *ctx,
           transaction, but that doesn't normally happen. */
        ctx->auto_updating = FALSE;
        quota_alloc(ctx, mail);
-       return 1;
+       return QUOTA_ALLOC_RESULT_OK;
 }
 
-int quota_test_alloc(struct quota_transaction_context *ctx,
-                    uoff_t size, bool *too_large_r)
+enum quota_alloc_result quota_test_alloc(struct quota_transaction_context *ctx,
+                                        uoff_t size)
 {
        if (ctx->failed)
-               return -1;
+               return QUOTA_ALLOC_RESULT_TEMPFAIL;
 
        if (quota_transaction_set_limits(ctx) < 0)
-               return -1;
+               return QUOTA_ALLOC_RESULT_TEMPFAIL;
        if (ctx->no_quota_updates)
-               return 1;
+               return QUOTA_ALLOC_RESULT_OK;
        /* this is a virtual function mainly for trash plugin and similar,
           which may automatically delete mails to stay under quota. */
-       return ctx->quota->set->test_alloc(ctx, size, too_large_r);
+       bool too_large = FALSE;
+       int ret = ctx->quota->set->test_alloc(ctx, size, &too_large);
+       if (ret < 0) {
+               return QUOTA_ALLOC_RESULT_TEMPFAIL;
+       } else if (ret == 0 && too_large) {
+               return QUOTA_ALLOC_RESULT_OVER_QUOTA_LIMIT;
+       } else if (ret == 0 && !too_large) {
+               return QUOTA_ALLOC_RESULT_OVER_QUOTA;
+       } else { /* (ret > 0) */
+               return QUOTA_ALLOC_RESULT_OK;
+       }
 }
 
 static int quota_default_test_alloc(struct quota_transaction_context *ctx,
index 1088f5a9b057f44ca61a0de89ee6f5f1c21a6e3b..6cc72df257b678991d20e66901b01f89ccc21fb5 100644 (file)
@@ -30,6 +30,17 @@ enum quota_recalculate {
        QUOTA_RECALCULATE_FORCED
 };
 
+enum quota_alloc_result {
+       QUOTA_ALLOC_RESULT_OK,
+       QUOTA_ALLOC_RESULT_TEMPFAIL,
+       QUOTA_ALLOC_RESULT_OVER_QUOTA,
+       /* Mail size is larger than even the maximum allowed quota. */
+       QUOTA_ALLOC_RESULT_OVER_QUOTA_LIMIT,
+};
+
+const char *quota_alloc_result_errstr(enum quota_alloc_result res,
+               struct quota_transaction_context *qt);
+
 int quota_user_read_settings(struct mail_user *user,
                             struct quota_settings **set_r,
                             const char **error_r);
@@ -78,14 +89,12 @@ int quota_transaction_commit(struct quota_transaction_context **ctx);
 /* Rollback quota transaction changes. */
 void quota_transaction_rollback(struct quota_transaction_context **ctx);
 
-/* Allocate from quota if there's space. Returns 1 if updated, 0 if not,
-   -1 if error. If mail size is larger than even maximum allowed quota,
-   too_large_r is set to TRUE. */
-int quota_try_alloc(struct quota_transaction_context *ctx,
-                   struct mail *mail, bool *too_large_r);
+/* Allocate from quota if there's space. */
+enum quota_alloc_result quota_try_alloc(struct quota_transaction_context *ctx,
+                                       struct mail *mail);
 /* Like quota_try_alloc(), but don't actually allocate anything. */
-int quota_test_alloc(struct quota_transaction_context *ctx,
-                    uoff_t size, bool *too_large_r);
+enum quota_alloc_result quota_test_alloc(struct quota_transaction_context *ctx,
+                                        uoff_t size);
 /* Update quota by allocating/freeing space used by mail. */
 void quota_alloc(struct quota_transaction_context *ctx, struct mail *mail);
 void quota_free(struct quota_transaction_context *ctx, struct mail *mail);