#!/bin/sh
# NiceOS CA trust store refresh/update helper.
# Container-safe, systemd-free.

set -u

PATH=/usr/sbin:/usr/bin:/sbin:/bin
export PATH

MODE="offline"
MAX_AGE_DAYS="${NICEOS_CA_REFRESH_MAX_AGE_DAYS:-7}"

SSL_CERTS_DIR="/etc/ssl/certs"
SSL_LOCAL_DIR="/etc/ssl/local"
CRT_DIR="/etc/pki/tls/certs"
CA_BUNDLE="${CRT_DIR}/ca-bundle.crt"
COMPAT_BUNDLE="${SSL_CERTS_DIR}/ca-certificates.crt"

STATE_DIR="/var/lib/ca-certificates"
STAMP_FILE="${STATE_DIR}/last-online-refresh"

LOCK_PARENT="/run/lock"
LOCK_DIR=""

usage()
{
    cat <<'USAGE'
Usage:
  niceos-update-ca-certificates [MODE]

Modes:
  --offline      Rebuild trust store from existing local CA material.
  --online       Download/update Mozilla certdata via make-ca -g and rebuild.
  --stale        Run --online only if the last online refresh is too old.
  --status       Show current CA refresh metadata.
  --help         Show this help.

Environment:
  NICEOS_CA_REFRESH_MAX_AGE_DAYS=N
      Max age for --stale mode. Default: 7.
USAGE
}

while [ "$#" -gt 0 ]; do
    case "$1" in
        --offline)
            MODE="offline"
            ;;
        --online|--force-online)
            MODE="online"
            ;;
        --stale|--if-stale)
            MODE="stale"
            ;;
        --status)
            MODE="status"
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        *)
            echo "niceos-update-ca-certificates: unknown option: $1" >&2
            usage >&2
            exit 2
            ;;
    esac
    shift
done

log()
{
    printf '%s\n' "$*" >&2
}

now_epoch()
{
    date -u +%s
}

file_mtime_epoch()
{
    # GNU coreutils/stat path.
    stat -c %Y "$1" 2>/dev/null || echo 0
}

status()
{
    echo "Mode default: offline"

    if command -v rpm >/dev/null 2>&1; then
        rpm -q ca-certificates 2>/dev/null | sed 's/^/Package: /' || :
    fi

    if [ -r "${STAMP_FILE}" ]; then
        echo "Last online refresh: $(cat "${STAMP_FILE}")"
        stamp_epoch="$(file_mtime_epoch "${STAMP_FILE}")"
        now="$(now_epoch)"
        age_days="$(( (now - stamp_epoch) / 86400 ))"
        echo "Last online refresh age days: ${age_days}"
    else
        echo "Last online refresh: never"
    fi

    if [ -e "${CA_BUNDLE}" ]; then
        echo "CA bundle: ${CA_BUNDLE}"
    else
        echo "CA bundle: missing"
    fi

    if [ -r /usr/share/ca-certificates/niceos/ca-certificates-build-info ]; then
        echo "Build info: /usr/share/ca-certificates/niceos/ca-certificates-build-info"
    fi

    if [ -r /etc/ssl/certdata.txt ]; then
        echo "Mozilla certdata: /etc/ssl/certdata.txt"
    fi
}

if [ "${MODE}" = "status" ]; then
    status
    exit 0
fi

if ! mkdir -p "${LOCK_PARENT}" 2>/dev/null; then
    LOCK_PARENT="/tmp"
fi

LOCK_DIR="${LOCK_PARENT}/niceos-update-ca-certificates.lock"

if ! mkdir "${LOCK_DIR}" 2>/dev/null; then
    # Another process is already refreshing the trust store.
    exit 0
fi

trap 'rmdir "${LOCK_DIR}" 2>/dev/null || :' EXIT INT TERM HUP

mkdir -p "${SSL_CERTS_DIR}" 2>/dev/null || :
mkdir -p "${SSL_LOCAL_DIR}" 2>/dev/null || :
mkdir -p "${CRT_DIR}" 2>/dev/null || :
mkdir -p /etc/pki/ca-trust/source/anchors 2>/dev/null || :
mkdir -p /etc/pki/ca-trust/source/blacklist 2>/dev/null || :

offline_refresh()
{
    old_bundle=""

    if [ -s "${CA_BUNDLE}" ]; then
        old_bundle="$(mktemp /tmp/ca-bundle.XXXXXX)" || old_bundle=""
        if [ -n "${old_bundle}" ]; then
            cp -f "${CA_BUNDLE}" "${old_bundle}" || old_bundle=""
        fi
    fi

    if [ -x /usr/bin/remove-expired-certs.sh ]; then
        /usr/bin/remove-expired-certs.sh || :
    fi

    # Prefer explicit local certdata if it exists.
    if [ -x /usr/sbin/make-ca ] && [ -s /etc/ssl/certdata.txt ]; then
        /usr/sbin/make-ca \
            --force \
            --certdata /etc/ssl/certdata.txt \
            --pkidir /etc/pki \
            --bundledir /etc/pki/tls/certs \
            --cadir /etc/ssl/certs \
            --localdir /etc/ssl/local >/dev/null 2>&1 || :
    elif [ -x /usr/sbin/make-ca ]; then
        /usr/sbin/make-ca -r >/dev/null 2>&1 || :
    fi

    if [ -x /usr/bin/update-ca-trust ]; then
        /usr/bin/update-ca-trust extract >/dev/null 2>&1 || \
        /usr/bin/update-ca-trust >/dev/null 2>&1 || :
    fi

    if [ ! -s "${CA_BUNDLE}" ] || ! grep -q "BEGIN CERTIFICATE" "${CA_BUNDLE}" 2>/dev/null; then
        if [ -n "${old_bundle}" ] && [ -s "${old_bundle}" ]; then
            cp -f "${old_bundle}" "${CA_BUNDLE}" || :
        fi
    fi

    rm -f "${old_bundle}" 2>/dev/null || :

    if [ ! -s "${CA_BUNDLE}" ] || ! grep -q "BEGIN CERTIFICATE" "${CA_BUNDLE}" 2>/dev/null; then
        log "niceos-update-ca-certificates: CA bundle is empty or invalid: ${CA_BUNDLE}"
        return 1
    fi

    ln -sfn "${CA_BUNDLE}" "${COMPAT_BUNDLE}" || :
}

online_refresh()
{
    if [ -x /usr/bin/remove-expired-certs.sh ]; then
        /usr/bin/remove-expired-certs.sh || :
    fi

    if [ ! -x /usr/sbin/make-ca ]; then
        log "niceos-update-ca-certificates: /usr/sbin/make-ca is not executable"
        return 1
    fi

    # Online update: download/process current Mozilla certdata.txt.
    /usr/sbin/make-ca -g --force || return 1

    if [ -x /usr/bin/update-ca-trust ]; then
        /usr/bin/update-ca-trust extract >/dev/null 2>&1 || \
        /usr/bin/update-ca-trust >/dev/null 2>&1 || :
    fi

    if [ -e "${CA_BUNDLE}" ] || [ -L "${CA_BUNDLE}" ]; then
        ln -sfn "${CA_BUNDLE}" "${COMPAT_BUNDLE}" || :
    fi

    mkdir -p "${STATE_DIR}" 2>/dev/null || :
    date -u +%Y-%m-%dT%H:%M:%SZ > "${STAMP_FILE}" 2>/dev/null || :
}

stale_refresh()
{
    now="$(now_epoch)"
    max_age_seconds="$((MAX_AGE_DAYS * 86400))"

    if [ -r "${STAMP_FILE}" ]; then
        stamp_epoch="$(file_mtime_epoch "${STAMP_FILE}")"
        age_seconds="$((now - stamp_epoch))"

        if [ "${age_seconds}" -lt "${max_age_seconds}" ]; then
            exit 0
        fi
    fi

    online_refresh
}

case "${MODE}" in
    offline)
        offline_refresh
        ;;
    online)
        online_refresh
        ;;
    stale)
        stale_refresh
        ;;
esac

exit 0
