]> 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)
committerGitLab <gitlab@git.dovecot.net>
Thu, 23 Mar 2017 13:27:14 +0000 (15:27 +0200)
src/plugins/quota/quota-status.c
src/plugins/quota/quota-storage.c
src/plugins/quota/quota.c
src/plugins/quota/quota.h

index 46ff0a588045c7333e3239142617cf5f8fd328fc..9c1d3c255ecdbc42eab43e761f181392548cd5f3 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 392d636035471baed4553e6a64e75ea69ef2ccf4..2b2a165493b5cfa6fcdedc2c857544d98ed5f544 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 bcb3a1c9579b1db643eab824a541ee5c66a0ee5e..aced2a9fcff732c92620ac9df4a019cbb6a051f1 100644 (file)
@@ -220,6 +220,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)
@@ -1155,17 +1170,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;
@@ -1174,15 +1188,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
@@ -1191,22 +1205,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);