#!/bin/bash
################################################################################
# Script Name: version/2.1.24/UPDATE.sh
# Description: Turn on everything added since 2.1.23 for servers that exist.
# Company: hitechcloud.org
# Copyright (c) hitechcloud.org
################################################################################
#
# A release ships pages. It does not, on its own, make them reachable: a page
# is registered only if its module is in enabled_modules, and it is shown to an
# account only if its feature is in that account's feature set. A fresh install
# gets both from INSTALL.sh. A server that already exists gets neither, so
# without this the whole release is code nobody can get to - which is the same
# outcome as not shipping it, arrived at more expensively.
#
# What is turned on here:
#
#   php_xray          Where a site's PHP is spending its time.
#   hotlink           Stop other sites embedding this one's images.
#   email_forwarders  Forward a mailbox elsewhere.
#   email_blocklist   Refuse or always accept a sender.
#   email_spam_filter Move the spam threshold for a domain.
#   email_greylisting Turn greylisting off for a domain that needs it off.
#   email_disk_usage  Which mailbox is filling the disk.
#   dav_configure     The CalDAV/CardDAV addresses for a mailbox.
#   dav_manage        Create and delete calendars and address books.
#   dav_sharing       Lend one to another mailbox on the same account.
#
# Existing feature sets are added to and never taken from. An operator who has
# removed a feature from a plan meant it, and an update must not quietly hand
# it back.
################################################################################

set -uo pipefail

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

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

readonly CONFIG=/etc/hitechcloud/hitechcloud/conf/hitechcloud.config
readonly FEATURES_DIR=/etc/hitechcloud/hitechcloud/features

readonly MODULES=(
    php_xray hotlink
    email_forwarders email_blocklist email_spam_filter email_greylisting email_disk_usage
    dav_configure dav_manage dav_sharing
)

# Which existing feature decides whether a plan gets each new one. A plan that
# does not offer PHP at all has no use for the PHP profiler, and a plan with no
# mail has no use for a spam threshold. "" means every plan gets it.
#
# Hotlink protection is gated on `error_pages` rather than on `domains`. It
# writes a rule into a website's own front-end configuration, and a DNS-only
# plan has `domains` without ever having a website - it would have got a page
# whose only control is a list of websites that is always empty.
declare -A GATE=(
    [php_xray]=php
    [hotlink]=error_pages
    [email_forwarders]=emails
    [email_blocklist]=emails
    [email_spam_filter]=emails
    [email_greylisting]=emails
    [email_disk_usage]=emails
    [dav_configure]=emails
    [dav_manage]=emails
    [dav_sharing]=emails
)

# 1. The module list the panel reads to decide which routes to register.
if [[ -f "$CONFIG" ]]; then
    current=$(grep -E '^enabled_modules=' "$CONFIG" | head -1 | cut -d= -f2- | tr -d '"')
    added=""
    for module in "${MODULES[@]}"; do
        grep -qE "(^|,)${module}(,|$)" <<< "$current" && continue
        current="${current},${module}"
        added="${added} ${module}"
    done
    if [[ -n "$added" ]]; then
        # Written through a temporary file rather than with sed -i: this line
        # is over two kilobytes long and holds characters sed would read as
        # its own delimiter.
        awk -v line="enabled_modules=\"${current}\"" \
            '/^enabled_modules=/ { print line; next } { print }' "$CONFIG" > "${CONFIG}.new" &&
            cat "${CONFIG}.new" > "$CONFIG" && rm -f "${CONFIG}.new"
        ok "Modules enabled:${added}"
    fi
else
    warn "No ${CONFIG} on this server, so no modules were enabled."
fi

# 2. The feature sets, each new feature going only to the plans that already
#    have the thing it belongs to.
granted=0
if [[ -d "$FEATURES_DIR" ]]; then
    for set in "$FEATURES_DIR"/*.txt; do
        [[ -f "$set" ]] || continue
        for feature in "${MODULES[@]}"; do
            gate="${GATE[$feature]:-}"
            [[ -z "$gate" ]] || grep -qx "$gate" "$set" || continue
            grep -qx "$feature" "$set" && continue
            echo "$feature" >> "$set"
            granted=$((granted + 1))
        done
    done
fi
(( granted == 0 )) || ok "Added ${granted} permission(s) to the plans that already offer the matching feature"

# 3. The mail tables, which need rebuilding for two separate reasons.
#
# `native-mail sync` is not optional on this release. Until now it read
# postfix-accounts.cf - `email|hash|uid` - with two variables, so every hash it
# wrote into dovecot-users carried "|<uid>" on the end and matched nothing: no
# mailbox on this server could sign in with its own password over IMAP, POP3,
# the webmail's own form, a phone, or CalDAV. Rerunning it with the corrected
# script is what actually repairs that, and every existing server needs it.
#
# `native-mailpolicy sync` compiles the blocked-sender, spam-threshold and
# greylisting store into Postfix and rspamd, and the same run writes the
# domain-to-account map that calendar sharing checks against.
#
# Skipped where there is no native mail stack, which is not a failure.
if [[ -d /etc/hitechcloud/mail ]]; then
    hitechcloudcli native-mailpolicy sync >/dev/null 2>&1 ||
        warn "The mail policy store could not be compiled - run: hitechcloudcli native-mailpolicy sync"
    hitechcloudcli native-mail sync >/dev/null 2>&1 ||
        warn "The mail tables could not be rebuilt - run: hitechcloudcli native-mail sync"
    ok "Mail policy and domain-owner tables rebuilt"
fi

# 4. Two new columns on `plans`, and the Email Pro tier definitions.
#
# max_email_lists is how many group addresses an account may have, and
# max_message_size_mb is the largest single message. Both default to 0, which
# means "no limit of its own" - so every existing plan keeps behaving exactly
# as it did until somebody sets one.
#
# ADD COLUMN is guarded by information_schema rather than by IF NOT EXISTS:
# MariaDB supports that spelling and MySQL does not, and a panel that has been
# migrated between the two would fail here on the version that does not.
add_column() {
    local column="$1" definition="$2" present
    present=$(mysql -N -B -e "SELECT COUNT(*) FROM information_schema.columns
        WHERE table_schema='panel' AND table_name='plans' AND column_name='${column}';" 2>/dev/null)
    [[ "$present" == "0" ]] || return 0
    if mysql panel -e "ALTER TABLE plans ADD COLUMN \`${column}\` ${definition};" 2>/dev/null; then
        ok "plans.${column} added"
    else
        warn "plans.${column} could not be added - the Email Pro limits will not be enforced until it is"
    fi
}
if mysql -N -B -e "SELECT 1 FROM panel.plans LIMIT 1;" >/dev/null 2>&1; then
    add_column max_email_lists "int NOT NULL DEFAULT 0"
    add_column max_message_size_mb "int NOT NULL DEFAULT 0"
else
    warn "The panel database could not be read, so the two new plan columns were not added."
fi

# The tier definitions, staged where `hitechcloudcli plan-emailpro` looks for
# them. Staged, not loaded: eight plans belong on a server when the
# administrator asks for them, not because an update ran. Found relative to
# this script so it works from either install directory.
tiers_src="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." 2>/dev/null && pwd)/configuration/mysql/initialize/1.1/email_pro_plans.sql"
if [[ -f "$tiers_src" ]]; then
    mkdir -p /etc/hitechcloud/mysql
    install -m 0644 "$tiers_src" /etc/hitechcloud/mysql/email_pro_plans.sql &&
        ok "Email Pro tiers available (try: hitechcloudcli plan-emailpro show)"
fi

# The panel remembers each account's feature list for a day; without this the
# new pages are live and invisible until that expires.
hitechcloudcli cache-forget_features >/dev/null 2>&1 || warn "Could not clear the feature cache - run: hitechcloudcli cache-forget_features"

ok "2.1.24 update complete"
exit 0
