The mailbox quota | MDaemon Technologies Community Forum

The mailbox quota


  • The mailbox quota is limited to a maximum of 9,999 emails (files). Why can't this limit be increased by a few orders of magnitude?



  • Your request will be considered for future versions. Thank you for your feedback.


  • Thank you. Could you please provide an estimated timeframe for when this feature might be implemented? Is it being considered for the next minor update, or is it planned for a major future release?


  • At this time we do not have a plan for when this feature might be implemented.   


  • I thought of a workaround, so I used AI to create a script that may help you out.  When the script runs, if the number of messages in an account exceeds the threshold passed to it, it will enable quota restrictions and set the limit to 1.  Forcing the account into an over quota situation.  When the script runs and there are less messages in the account than the threshold, it will turn off quotas for the account.  The script has had very limited testing, so I would highly reccomend that you test it in your environment before deploying into production.  The current script does not offer any way to exempt an account, it applies to all users of all domains.  Please feel free to modify the script to work in your environment.

    <#
    .SYNOPSIS
        Enforces or releases per-account "over quota" state on MDaemon mailboxes
        based on the current on-disk message count.
    
    .DESCRIPTION
        For every MDaemon user account, this script reads the current stored-message
        count via the MDaemon XML Management API (GetDomainList + GetDomainInfo with
        <Get><Users><Quotas/></Users></Get>). When the count exceeds -Threshold the
        account is forced into over-quota state by calling UpdateUser with
        ApplyQuotas=Yes and MaxMessageCount=1. When the count is at or below the
        threshold, ApplyQuotas is set to No to release the account.
    
        The on-disk count is read via the API rather than Hiwater.mrk directly so
        that any user whose cache is missing or stale is silently refreshed on the
        server side as a side effect of the GetDomainInfo call.
    
    .PARAMETER Threshold
        Message-count threshold X. Any user with count > X is enforced; <= X
        releases.
    
    .PARAMETER ServerUrl
        Base URL of the MDaemon XML API. Defaults to http://localhost:5480.
    
    .PARAMETER Credential
        Admin credentials for the initial Logon call. If omitted, Get-Credential
        prompts.
    
    .PARAMETER LogPath
        Append-only log file. Defaults to .\Set-MdaemonOverQuota.log.
    
    .PARAMETER DryRun
        Report intended changes without calling UpdateUser.
    
    .PARAMETER NoSkipUnchanged
        By default, users whose current ApplyQuotas already matches the desired
        state are skipped. Pass this switch to issue UpdateUser unconditionally.
    
    .EXAMPLE
        .\Set-MdaemonOverQuota.ps1 -Threshold 5000 -DryRun
    
    .EXAMPLE
        .\Set-MdaemonOverQuota.ps1 -X 5000 -ServerUrl http://mail.example.com:5480
    #>
    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [Alias('X')]
        [int]$Threshold,
    
        [string]$ServerUrl = 'http://localhost:5480',
    
        [System.Management.Automation.PSCredential]$Credential,
    
        [string]$LogPath = (Join-Path -Path (Get-Location) -ChildPath 'Set-MdaemonOverQuota.log'),
    
        [switch]$DryRun,
    
        [switch]$NoSkipUnchanged
    )
    
    $ErrorActionPreference = 'Stop'
    $SkipUnchanged = -not $NoSkipUnchanged
    
    function Write-Log {
        param([string]$Message, [string]$Level = 'INFO')
        $line = '{0}  {1,-5}  {2}' -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Level, $Message
        Add-Content -Path $LogPath -Value $line
        Write-Host $line
    }
    
    function Invoke-MdApi {
        param(
            [Parameter(Mandatory)] [string]$BodyXml,
            [string]$Token,
            [System.Management.Automation.PSCredential]$BasicCredential
        )
    
        $headers = @{}
        if ($BasicCredential) {
            $pair  = '{0}:{1}' -f $BasicCredential.UserName, $BasicCredential.GetNetworkCredential().Password
            $b64   = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes($pair))
            $headers['Authorization'] = "Basic $b64"
        }
        if ($Token) {
            $headers['X-MDAPI-TOKEN'] = $Token
        }
    
        $uri = ($ServerUrl.TrimEnd('/')) + '/MdMgmtWS'
    
        try {
            $response = Invoke-WebRequest -Uri $uri -Method Post -Headers $headers `
                -ContentType 'text/xml' -Body $BodyXml -UseBasicParsing
        }
        catch {
            throw "HTTP error calling $uri : $($_.Exception.Message)"
        }
    
        try {
            [xml]$xml = $response.Content
        }
        catch {
            throw "Server returned non-XML response: $($response.Content)"
        }
    
        $status = $xml.MDaemon.API.Response.Status
        if (-not $status) {
            throw "Server response had no <Status> element. Body: $($response.Content)"
        }
        if ([int]$status.id -ne 0) {
            throw "API error: id=$($status.id) value=$($status.value) message=$($status.message)"
        }
    
        return $xml
    }
    
    function New-RequestEnvelope {
        param([string]$Operation, [string]$ParametersXml = '<Parameters/>')
        @"
    <?xml version="1.0" encoding="utf-8"?>
    <MDaemon>
      <API>
        <Request version="25.0">
          <Operation>$Operation</Operation>
          $ParametersXml
        </Request>
      </API>
    </MDaemon>
    "@
    }
    
    # ----- Begin -----
    
    if (-not $Credential) {
        $Credential = Get-Credential -Message 'Enter MDaemon administrator credentials'
    }
    
    Write-Log "Starting quota sweep. Threshold=$Threshold ServerUrl=$ServerUrl DryRun=$($DryRun.IsPresent)"
    
    # 1. Logon
    Write-Log 'Logging on…'
    $logonBody = New-RequestEnvelope -Operation 'Logon'
    $logonXml  = Invoke-MdApi -BodyXml $logonBody -BasicCredential $Credential
    $Token     = [string]$logonXml.MDaemon.API.Response.Result.Token
    if (-not $Token) { throw 'Logon succeeded but no token was returned.' }
    Write-Log "Logon successful. Session timeout: $($logonXml.MDaemon.API.Response.Result.Timeout)s"
    
    $enforced  = 0
    $released  = 0
    $unchanged = 0
    $errors    = 0
    
    try {
        # 2. Enumerate domains
        Write-Log 'Fetching domain list…'
        $domainListBody = New-RequestEnvelope -Operation 'GetDomainList' `
            -ParametersXml '<Parameters><Get><Users/></Get></Parameters>'
        $domainListXml  = Invoke-MdApi -BodyXml $domainListBody -Token $Token
        $domains        = @($domainListXml.MDaemon.API.Response.Result.Domains.Domain)
        Write-Log "Found $($domains.Count) domain(s)."
    
        # 3. Per-domain quota fetch + per-user evaluation
        foreach ($d in $domains) {
            $domainName = [string]$d.id
            Write-Log "Processing domain '$domainName'…"
    
            $infoBody = New-RequestEnvelope -Operation 'GetDomainInfo' -ParametersXml @"
    <Parameters>
      <Domain>$domainName</Domain>
      <Get><Users><Quotas/></Users></Get>
    </Parameters>
    "@
            try {
                $infoXml = Invoke-MdApi -BodyXml $infoBody -Token $Token
            }
            catch {
                Write-Log "GetDomainInfo failed for '$domainName': $_" 'ERROR'
                $errors++
                continue
            }
    
            $users = @($infoXml.MDaemon.API.Response.Result.Domain.Users.User)
            Write-Log "  $($users.Count) user(s) in '$domainName'."
    
            foreach ($u in $users) {
                $mailbox = [string]$u.id
                if (-not $mailbox) { continue }   # skip the <MailboxRoot/> sentinel
    
                $quotas = $u.Quotas
                if (-not $quotas) {
                    Write-Log "  ${mailbox}@${domainName}: no <Quotas> element returned, skipping." 'WARN'
                    continue
                }
    
                $currentApply = [string]$quotas.ApplyQuotas
                $itemsRaw     = [string]$quotas.Usage.Items
                if ([string]::IsNullOrWhiteSpace($itemsRaw)) {
                    Write-Log "  ${mailbox}@${domainName}: no <Usage><Items> value, skipping." 'WARN'
                    continue
                }
                $msgCount = [int]$itemsRaw
    
                $desired = if ($msgCount -gt $Threshold) { 'Yes' } else { 'No' }
    
                if ($SkipUnchanged -and ($currentApply -eq $desired)) {
                    Write-Log "  ${mailbox}@${domainName}: count=$msgCount ApplyQuotas=$currentApply (no change)"
                    $unchanged++
                    continue
                }
    
                $action = if ($desired -eq 'Yes') { 'ENFORCE' } else { 'RELEASE' }
                Write-Log "  ${mailbox}@${domainName}: count=$msgCount  $action  (was ApplyQuotas=$currentApply)"
    
                if ($DryRun) {
                    if ($desired -eq 'Yes') { $enforced++ } else { $released++ }
                    continue
                }
    
                $updateBody = New-RequestEnvelope -Operation 'UpdateUser' -ParametersXml @"
    <Parameters>
      <Domain>$domainName</Domain>
      <Mailbox>$mailbox</Mailbox>
      <Quotas>
        <ApplyQuotas>$desired</ApplyQuotas>
        <MaxMessageCount>1</MaxMessageCount>
      </Quotas>
    </Parameters>
    "@
                try {
                    Invoke-MdApi -BodyXml $updateBody -Token $Token | Out-Null
                    if ($desired -eq 'Yes') { $enforced++ } else { $released++ }
                }
                catch {
                    Write-Log "  ${mailbox}@${domainName}: UpdateUser failed: $_" 'ERROR'
                    $errors++
                }
            }
        }
    }
    finally {
        # 4. Logoff (best effort)
        try {
            $logoffBody = New-RequestEnvelope -Operation 'Logoff'
            Invoke-MdApi -BodyXml $logoffBody -Token $Token | Out-Null
            Write-Log 'Logged off.'
        }
        catch {
            Write-Log "Logoff failed: $_" 'WARN'
        }
    }
    
    $prefix = if ($DryRun) { '[DRY RUN] ' } else { '' }
    Write-Log "${prefix}Summary: enforced=$enforced  released=$released  unchanged=$unchanged  errors=$errors"
    

  • Thank you so much for your effort and for providing this script! It's a very clever workaround. However, disabling 'ApplyQuotas' completely turns off our disk space restrictions (MB/GB limits) for those accounts, which poses a risk to storage.For now, we will be using the AccountPrune utility as a temporary solution to move older mail into separate folders. That being said, we are still looking forward to a native, complete solution in future versions that will allow us to properly increase the message count quota for individual mailboxes. Thank you again for your great support!


  • I think the script can be adjusted to to only change the Maximum number of messages stored at once and not change the Enable Quota Restrictions options.  If you are interested in using the script, I'll make adjustments retest and share, assuming I get it working.  Just let me know.


  • Thank you for the offer! Since our current solution with AccountPrune has been tested and is performing adequately for now, though it’s not ideal, there is no urgent need to rewrite the script for us. Furthermore, in our real-world environment, we actually require different quotas for different users, so a single global threshold would not be ideal for us anyway. However, if you'd like to update it for future reference or for other users, we would still be interested in taking a look at the final code. Thanks again!


Please login to reply to this topic!