#!/usr/bin/env bash
set -Eeuo pipefail

CONFIG_FILE="${LEGO_CONFIG_FILE:-/etc/lego/.lego.yml}"
CERT_DIR="${LEGO_CERT_DIR:-/etc/lego/certificates}"
VISUALWARE_DATA_DIR="${VISUALWARE_DATA_DIR:-/opt/vw/data}"
JKS="${VISUALWARE_JKS:-${VISUALWARE_DATA_DIR}/certs.jks}"
STORE_PASS="${VISUALWARE_STORE_PASS:-12345678}"

log() { printf 'lego visualware hook: %s\n' "$*"; }
die() { printf 'lego visualware hook: ERROR: %s\n' "$*" >&2; exit 1; }

command -v openssl >/dev/null 2>&1 || die "openssl is not available"
command -v keytool >/dev/null 2>&1 || die "keytool is not available; install a Java JRE/JDK"
[[ -r "${CONFIG_FILE}" ]] || die "cannot read ${CONFIG_FILE}"
[[ -d "${VISUALWARE_DATA_DIR}" ]] || die "Visualware data directory not found: ${VISUALWARE_DATA_DIR}"
[[ "${STORE_PASS}" =~ ^[[:alnum:]]+$ ]] ||
    die "Visualware keystore password must contain only letters and numbers"

mapfile -t certificate_names < <(
    awk '
        /^[[:space:]]*certificates:[[:space:]]*$/ { in_certificates=1; next }
        in_certificates && /^[^[:space:]]/ { exit }
        in_certificates && /^  [^[:space:]][^:]*:[[:space:]]*$/ {
            line=$0
            sub(/^  /, "", line)
            sub(/:[[:space:]]*$/, "", line)
            if ((substr(line,1,1) == "\047" && substr(line,length(line),1) == "\047") ||
                (substr(line,1,1) == "\042" && substr(line,length(line),1) == "\042")) {
                line=substr(line,2,length(line)-2)
            }
            print line
        }
    ' "${CONFIG_FILE}"
)

if [[ -n "${VISUALWARE_CERT_NAME:-}" ]]; then
    cert_name="${VISUALWARE_CERT_NAME}"
elif ((${#certificate_names[@]} == 1)); then
    cert_name="${certificate_names[0]}"
elif ((${#certificate_names[@]} == 0)); then
    die "no certificate entries found in ${CONFIG_FILE}"
else
    die "multiple certificate entries found; set VISUALWARE_CERT_NAME"
fi

crt="${CERT_DIR}/${cert_name}.crt"
issuer="${CERT_DIR}/${cert_name}.issuer.crt"
key="${CERT_DIR}/${cert_name}.key"
pfx="${CERT_DIR}/${cert_name}.pfx"
fullchain="${CERT_DIR}/${cert_name}.fullchain.pem"

[[ -s "${crt}" ]] || die "missing certificate: ${crt}"
[[ -s "${issuer}" ]] || die "missing issuer certificate: ${issuer}"
[[ -s "${key}" ]] || die "missing private key: ${key}"
openssl x509 -in "${crt}" -noout -checkend 0 >/dev/null 2>&1 ||
    die "certificate is expired or not yet valid: ${crt}"

cert_fingerprint="$(openssl x509 -in "${crt}" -pubkey -noout |
    openssl pkey -pubin -outform DER 2>/dev/null | openssl sha256)"
key_fingerprint="$(openssl pkey -in "${key}" -pubout -outform DER 2>/dev/null |
    openssl sha256)"
[[ "${cert_fingerprint}" == "${key_fingerprint}" ]] ||
    die "certificate and private key do not match"

tmp_fullchain="$(mktemp "${CERT_DIR}/.${cert_name}.fullchain.XXXXXX")"
tmp_pfx="$(mktemp "${CERT_DIR}/.${cert_name}.pfx.XXXXXX")"
tmp_jks="$(mktemp "${VISUALWARE_DATA_DIR}/.certs.jks.XXXXXX")"
cleanup() { rm -f -- "${tmp_fullchain}" "${tmp_pfx}" "${tmp_jks}"; }
trap cleanup EXIT

# keytool creates its destination and treats an existing empty file as a
# malformed keystore. Keep the unique name from mktemp, then release the file.
rm -f -- "${tmp_jks}"

cat "${crt}" "${issuer}" >"${tmp_fullchain}"
openssl pkcs12 -export \
    -out "${tmp_pfx}" \
    -inkey "${key}" \
    -in "${crt}" \
    -certfile "${issuer}" \
    -passout "pass:${STORE_PASS}"

keytool -importkeystore -noprompt \
    -srckeystore "${tmp_pfx}" \
    -srcstoretype PKCS12 \
    -srcstorepass "${STORE_PASS}" \
    -destkeystore "${tmp_jks}" \
    -deststoretype JKS \
    -deststorepass "${STORE_PASS}" \
    -destkeypass "${STORE_PASS}"
keytool -list -keystore "${tmp_jks}" -storepass "${STORE_PASS}" >/dev/null

# Retain an existing keystore's ownership; on first install inherit the data
# directory owner so the Visualware service can read the new file.
if [[ -e "${JKS}" ]]; then
    owner="$(stat -c '%u:%g' "${JKS}")"
else
    owner="$(stat -c '%u:%g' "${VISUALWARE_DATA_DIR}")"
fi
chown "${owner}" "${tmp_jks}"
chmod 0600 "${tmp_jks}"
chmod 0640 "${tmp_pfx}" "${tmp_fullchain}"

mv -f -- "${tmp_fullchain}" "${fullchain}"
mv -f -- "${tmp_pfx}" "${pfx}"
mv -f -- "${tmp_jks}" "${JKS}"
trap - EXIT

log "created ${pfx} and installed ${JKS} for ${cert_name}"
