Re: [Exim] checking quota before accepting the message

Top Page
Delete this message
Reply to this message
Author: Jeremy Harris
Date:  
To: Nathan Ollerenshaw
CC: Exim users list
Subject: Re: [Exim] checking quota before accepting the message
Nathan Ollerenshaw wrote:
> Guys,
>
> right now I have a lot of messages sitting in queues, being deferred
> for local delivery because the mailbox they are trying to delivery to
> is over quota.
>
> Is there any way to check to see if the message is over quota before
> its 250 OK'd, and fail it? I don't want to generate a bounce if
> possible.
>
> I'm guessing not because if its being delivered to multiple addresses
> you don't want to fail it for all of them if just one will fail due to
> quota. Maybe there is a better way.


It's ugly (yes, I'd like a builtin in Exim too!) but all sorts of evil
things are possible with Exim. Here's mine (any critiques welcome):

# This relies on data stored in a "maildirdize" file in the user's area
# so it won't have much effect on a 2mx - we need to store quota/usage
# info in the DB for that.  At RCPT time it works with a SIZE=
# smtp command, trusting it.  Then at DATA time we check again having
# real info, and in the case there was no maildirsize file we generate
# a bounce immediately on delivery failure.
acl_chk_quota:
   # ok if within quota
   accept        condition =     ${if <= {$acl_c8}{$acl_c7} {yes}{no}}
   defer         message =       quota exceeded
                 set acl_m9 =    ${lookup pgsql{ select set_user_param_int( \
                                         $acl_m4, 'over_quota', 1 )}\
                                         {dummy}fail}


quota_db_flag:
   accept        condition =     ${if = \
     {${lookup pgsql{ SELECT user_param_int($acl_m4, 'over_quota')}{$value}}} \
                                 {1} {no}{yes}}
   defer         message =       quota exceeded


rcpt_chk_quota:
   # OK if no local deliveries
   require       set acl_c7 =    1000M
                 set acl_c5 =    0
   accept        condition =     ${if match {$acl_c9}{/(,|\$)} {no}{yes}}


   # Get the deliver directory and append "maildirsize"
   require       set acl_c8 =    ${sg {$acl_c9} {([^/],)*(.*/(,|\$))} \
                                         {\$2maildirsize}}
   # If the size file is not present either it's never been created or
   # we're on a 2MX.  Check the DB for an over-quota flag.
   accept        condition =     ${if !exists{$acl_c8} {yes}{no}}
                 acl =           quota_db_flag
   # read the size file
   require       set acl_c8 =    ${readfile{$acl_c8}{!}}
   # empty first line means infinite quota
   accept        condition =     ${if match {$acl_c8}{^\\s*(!.*)?\$} {yes}{no}}
                 set acl_c7 =    0
   # quota: first line, first number
   require       set acl_c7 =    ${sg {$acl_c8} {^(\\s*)(\\d+).*\$} {\$2}}
   # zero means infinite quota
   accept        condition =     ${if = {$acl_c7}{0} {yes}{no}}
   # drop the first line
   warn          set acl_c8 =    ${sg {$acl_c8}{^[^!]*!(.*)\$} {\$1}}
   # convert 2nd number & linebreak into addition, on each line
                 set acl_c8 =    ${sg {$acl_c8}{\\s*(-?\\d+)[^!]*!?} {\$1 +}}
   # evaluate the sum (and leave around for the data-time check)
                 set acl_c5 =    ${eval:$acl_c8 +0}
   # add this message (10k default if no SIZE=)
                 set acl_c8 =    ${eval:$acl_c5 + ${if < {$message_size}{0} \
                                         {10K} {$message_size}}}
   # ok if within quota
   accept        acl =           acl_chk_quota


data_chk_quota:
   accept        condition =     ${if > {$rcpt_count}{1} {yes}{no} }
   accept        condition =     ${if match {$acl_c9}{/(,|\$)} {no}{yes}}
   accept        condition =     ${if = {$acl_c7}{0} {yes}{no}}
   require       set acl_c8 =    ${eval:$acl_c5+$message_size}
   accept        acl =           acl_chk_quota




- Jeremy