#!/bin/bash
################################################################################
# Script Name: version/2.1.29/UPDATE.sh
# Description: Say whether this server lost mail to the domain-deletion bug.
# Company: hitechcloud.org
# Copyright (c) hitechcloud.org
################################################################################
#
# Nothing to migrate. This is scripts and binaries: no schema, no
# configuration, no service.
#
# What it does instead is look for the damage. Until this release, deleting one
# domain deleted every mailbox the account owned, on every domain - and the
# maildir was left behind, because the deletion aimed at the container's path
# rather than the one Dovecot uses. So a server that hit this has maildirs
# under /var/mail/vhosts with no account behind them, and their owners cannot
# sign in.
#
# That is a recoverable state and it is worth naming, because nothing else
# will: the panel showed the deletion as successful and the customer finds out
# when their mail stops arriving.
#
# Read-only, and deliberately so. Recreating a mailbox needs a password, which
# this script has no business inventing - a mailbox silently given a password
# nobody was told is worse than one that is missing.
################################################################################

set -uo pipefail

GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
ok()   { echo -e "[2.1.29] ${GREEN}✓${NC} $*"; }
warn() { echo -e "[2.1.29] ${YELLOW}!${NC} $*" >&2; }

[[ $EUID -eq 0 ]] || { echo "[2.1.29] This update must run as root." >&2; exit 1; }

VMAIL_HOME="/var/mail/vhosts"
ACCOUNTS_FILE="/etc/hitechcloud/mail/postfix-accounts.cf"

if [[ ! -d "$VMAIL_HOME" ]]; then
    ok "No mail is hosted here"
    ok "2.1.29 update complete"
    exit 0
fi

# A maildir with no account behind it. Every one of these is a mailbox
# somebody can no longer sign in to, with its mail intact.
orphans=0
while IFS= read -r maildir; do
    [[ -n "$maildir" ]] || continue
    domain=$(basename "$(dirname "$maildir")")
    user=$(basename "$maildir")
    address="${user}@${domain}"

    # Nothing to compare against means nothing to report: an accounts file
    # that is missing is a server with no mailboxes configured, not a server
    # that lost them.
    [[ -s "$ACCOUNTS_FILE" ]] || continue

    # Spaces, tabs and carriage returns come out; newlines stay. `tr -d
    # '[:space:]'` deletes newlines too, which folds the whole file into one
    # line and makes grep -x match nothing - so the first version of this
    # reported every mailbox on the server as orphaned, including the ones
    # sitting in the file it had just read.
    if ! cut -d'|' -f1 "$ACCOUNTS_FILE" | tr -d ' \t\r' | grep -qixF "$address"; then
        size=$(du -sh "$maildir" 2>/dev/null | cut -f1)
        warn "${address} has mail on disk (${size:-unknown}) but no account. Recreate the mailbox at that address and its mail is there."
        orphans=$((orphans + 1))
    fi
done < <(find "$VMAIL_HOME" -mindepth 2 -maxdepth 2 -type d 2>/dev/null)

if (( orphans == 0 )); then
    ok "Every maildir on this server has an account behind it"
else
    warn "${orphans} mailbox(es) above were deleted without their mail being deleted - most likely by a domain deletion before this release, which removed every mailbox on the account rather than the domain's own. That is fixed in 2.1.29; the mail is still here."
fi

ok "2.1.29 update complete"
exit 0
