#!/usr/bin/with-contenv bash
# shellcheck shell=bash

# Display variables for troubleshooting
echo -e "Variables set:\\n\
PUID=${PUID}\\n\
PGID=${PGID}\\n\
TZ=${TZ}\\n\
URL=${URL}\\n\
SUBDOMAINS=${SUBDOMAINS}\\n\
EXTRA_DOMAINS=${EXTRA_DOMAINS}\\n\
ONLY_SUBDOMAINS=${ONLY_SUBDOMAINS}\\n\
VALIDATION=${VALIDATION}\\n\
CERTPROVIDER=${CERTPROVIDER}\\n\
DNSPLUGIN=${DNSPLUGIN}\\n\
EMAIL=${EMAIL}\\n\
STAGING=${STAGING}\\n"

# Sanitize variables
SANED_VARS=(DNSPLUGIN EMAIL EXTRA_DOMAINS ONLY_SUBDOMAINS STAGING SUBDOMAINS URL VALIDATION CERTPROVIDER)
for i in "${SANED_VARS[@]}"; do
    export echo "${i}"="${!i//\"/}"
    export echo "${i}"="$(echo "${!i}" | tr '[:upper:]' '[:lower:]')"
done

# check to make sure DNSPLUGIN is selected if dns validation is used
if [[ "${VALIDATION}" = "dns" ]] && [[ ! "${DNSPLUGIN}" =~ ^(acmedns|aliyun|azure|cloudflare|cpanel|desec|digitalocean|directadmin|dnsimple|dnsmadeeasy|dnspod|do|domeneshop|duckdns|dynu|gandi|gehirn|godaddy|google|google-domains|he|hetzner|infomaniak|inwx|ionos|linode|loopia|luadns|netcup|njalla|nsone|ovh|porkbun|rfc2136|route53|sakuracloud|standalone|transip|vultr)$ ]]; then
    echo "Please set the DNSPLUGIN variable to a valid plugin name. See docker info for more details."
    sleep infinity
fi

# copy dns default configs
cp -n /defaults/dns-conf/* /config/dns-conf/
lsiown -R abc:abc /config/dns-conf

# copy default renewal hooks
chmod -R +x /defaults/etc/letsencrypt/renewal-hooks
cp -nR /defaults/etc/letsencrypt/renewal-hooks/* /config/etc/letsencrypt/renewal-hooks/
lsiown -R abc:abc /config/etc/letsencrypt/renewal-hooks

# replace nginx service location in renewal hooks
find /config/etc/letsencrypt/renewal-hooks/ -type f -exec sed -i 's|/run/service/nginx|/run/service/svc-nginx|g' {} \;
find /config/etc/letsencrypt/renewal-hooks/ -type f -exec sed -i 's|/var/run/s6/services/nginx|/run/service/svc-nginx|g' {} \;

# create original config file if it doesn't exist, move non-hidden legacy file to hidden
if [[ -f "/config/donoteditthisfile.conf" ]]; then
    mv /config/donoteditthisfile.conf /config/.donoteditthisfile.conf
fi
if [[ ! -f "/config/.donoteditthisfile.conf" ]]; then
    echo -e "ORIGURL=\"${URL}\" ORIGSUBDOMAINS=\"${SUBDOMAINS}\" ORIGONLY_SUBDOMAINS=\"${ONLY_SUBDOMAINS}\" ORIGEXTRA_DOMAINS=\"${EXTRA_DOMAINS}\" ORIGVALIDATION=\"${VALIDATION}\" ORIGDNSPLUGIN=\"${DNSPLUGIN}\" ORIGPROPAGATION=\"${PROPAGATION}\" ORIGSTAGING=\"${STAGING}\" ORIGCERTPROVIDER=\"${CERTPROVIDER}\" ORIGEMAIL=\"${EMAIL}\"" >/config/.donoteditthisfile.conf
    echo "Created .donoteditthisfile.conf"
fi

# load original config settings
# shellcheck source=/dev/null
. /config/.donoteditthisfile.conf

# setting ORIGDOMAIN for use in revoke sections
if [[ "${ORIGONLY_SUBDOMAINS}" = "true" ]] && [[ ! "${ORIGSUBDOMAINS}" = "wildcard" ]]; then
    ORIGDOMAIN="$(echo "${ORIGSUBDOMAINS}" | tr ',' ' ' | awk '{print $1}').${ORIGURL}"
else
    ORIGDOMAIN="${ORIGURL}"
fi

# update plugin names in dns conf inis
sed -i 's|^certbot[-_]dns[-_]aliyun:||g' /config/dns-conf/aliyun.ini
sed -i 's|^certbot[-_]dns[-_]cpanel:||g' /config/dns-conf/cpanel.ini
sed -i 's|^dns[-_]cpanel[-_]|cpanel_|g' /config/dns-conf/cpanel.ini
sed -i 's|^directadmin[-_]|dns_directadmin_|g' /config/dns-conf/directadmin.ini
sed -i 's|^certbot[-_]dns[-_]domeneshop:||g' /config/dns-conf/domeneshop.ini
sed -i 's|^certbot[-_]plugin[-_]gandi:dns[-_]|dns_gandi_|g' /config/dns-conf/gandi.ini
sed -i 's|^certbot[-_]dns[-_]inwx:||g' /config/dns-conf/inwx.ini
sed -i 's|^certbot[-_]dns[-_]transip:||g' /config/dns-conf/transip.ini

# update plugin names in renewal conf
if [[ -f "/config/etc/letsencrypt/renewal/${ORIGDOMAIN}.conf" ]] && [[ "${ORIGVALIDATION}" = "dns" ]]; then
    if [[ "${ORIGDNSPLUGIN}" =~ ^(aliyun)$ ]]; then
        sed -i 's|^authenticator = certbot[-_]dns[-_]aliyun:||g' "/config/etc/letsencrypt/renewal/${ORIGDOMAIN}.conf"
        sed -i 's|^certbot[-_]dns[-_]aliyun:||g' "/config/etc/letsencrypt/renewal/${ORIGDOMAIN}.conf"
    fi
    if [[ "${ORIGDNSPLUGIN}" =~ ^(cpanel)$ ]]; then
        sed -i 's|^authenticator = certbot[-_]dns[-_]cpanel:||g' "/config/etc/letsencrypt/renewal/${ORIGDOMAIN}.conf"
        sed -i 's|^certbot[-_]dns[-_]cpanel:||g' "/config/etc/letsencrypt/renewal/${ORIGDOMAIN}.conf"
        sed -i 's|^authenticator = dns[-_]cpanel|authenticator = cpanel|g' "/config/etc/letsencrypt/renewal/${ORIGDOMAIN}.conf"
        sed -i 's|^dns[-_]cpanel[-_]|cpanel_|g' "/config/etc/letsencrypt/renewal/${ORIGDOMAIN}.conf"
    fi
    if [[ "${ORIGDNSPLUGIN}" =~ ^(directadmin)$ ]]; then
        sed -i 's|^authenticator = directadmin|authenticator = dns-directadmin|g' "/config/etc/letsencrypt/renewal/${ORIGDOMAIN}.conf"
        sed -i 's|^directadmin[-_]|dns_directadmin_|g' "/config/etc/letsencrypt/renewal/${ORIGDOMAIN}.conf"
    fi
    if [[ "${ORIGDNSPLUGIN}" =~ ^(domeneshop)$ ]]; then
        sed -i 's|^authenticator = certbot[-_]dns[-_]domeneshop:||g' "/config/etc/letsencrypt/renewal/${ORIGDOMAIN}.conf"
        sed -i 's|^certbot[-_]dns[-_]domeneshop:||g' "/config/etc/letsencrypt/renewal/${ORIGDOMAIN}.conf"
    fi
    if [[ "${ORIGDNSPLUGIN}" =~ ^(gandi)$ ]]; then
        sed -i 's|^authenticator = certbot[-_]plugin[-_]gandi:dns|authenticator = dns-gandi|g' "/config/etc/letsencrypt/renewal/${ORIGDOMAIN}.conf"
        sed -i 's|^certbot[-_]plugin[-_]gandi:dns[-_]|dns_gandi_|g' "/config/etc/letsencrypt/renewal/${ORIGDOMAIN}.conf"
    fi
    if [[ "${ORIGDNSPLUGIN}" =~ ^(inwx)$ ]]; then
        sed -i 's|^authenticator = certbot[-_]dns[-_]inwx:||g' "/config/etc/letsencrypt/renewal/${ORIGDOMAIN}.conf"
        sed -i 's|^certbot[-_]dns[-_]inwx:||g' "/config/etc/letsencrypt/renewal/${ORIGDOMAIN}.conf"
    fi
    if [[ "${ORIGDNSPLUGIN}" =~ ^(transip)$ ]]; then
        sed -i 's|^authenticator = certbot[-_]dns[-_]transip:||g' "/config/etc/letsencrypt/renewal/${ORIGDOMAIN}.conf"
        sed -i 's|^certbot[-_]dns[-_]transip:||g' "/config/etc/letsencrypt/renewal/${ORIGDOMAIN}.conf"
    fi
fi

# set default validation to http
if [[ -z "${VALIDATION}" ]]; then
    VALIDATION="http"
    echo "VALIDATION parameter not set; setting it to http"
fi

# set duckdns validation to dns
if [[ "${VALIDATION}" = "duckdns" ]]; then
    VALIDATION="dns"
    DNSPLUGIN="duckdns"
    if [[ -n "${DUCKDNSTOKEN}" ]] && ! grep -q "dns_duckdns_token=${DUCKDNSTOKEN}$" /config/dns-conf/duckdns.ini; then
        sed -i "s|^dns_duckdns_token=.*|dns_duckdns_token=${DUCKDNSTOKEN}|g" /config/dns-conf/duckdns.ini
    fi
fi
if [[ "${VALIDATION}" = "dns" ]] && [[ "${DNSPLUGIN}" = "duckdns" ]]; then
    if [[ "${SUBDOMAINS}" = "wildcard" ]]; then
        echo "the resulting certificate will only cover the subdomains due to a limitation of duckdns, so it is advised to set the root location to use www.subdomain.duckdns.org"
        export ONLY_SUBDOMAINS=true
    else
        echo "the resulting certificate will only cover the main domain due to a limitation of duckdns, ie. subdomain.duckdns.org"
        export SUBDOMAINS=""
    fi
    export EXTRA_DOMAINS=""
fi

# setting the symlink for key location
rm -rf /config/keys/letsencrypt
if [[ "${ONLY_SUBDOMAINS}" = "true" ]] && [[ ! "${SUBDOMAINS}" = "wildcard" ]]; then
    DOMAIN="$(echo "${SUBDOMAINS}" | tr ',' ' ' | awk '{print $1}').${URL}"
    ln -s ../etc/letsencrypt/live/"${DOMAIN}" /config/keys/letsencrypt
else
    ln -s ../etc/letsencrypt/live/"${URL}" /config/keys/letsencrypt
fi

# cleanup unused csr and keys folders
rm -rf /etc/letsencrypt/csr
rm -rf /etc/letsencrypt/keys

# checking for changes in cert variables, revoking certs if necessary
if [[ ! "${URL}" = "${ORIGURL}" ]] ||
    [[ ! "${SUBDOMAINS}" = "${ORIGSUBDOMAINS}" ]] ||
    [[ ! "${ONLY_SUBDOMAINS}" = "${ORIGONLY_SUBDOMAINS}" ]] ||
    [[ ! "${EXTRA_DOMAINS}" = "${ORIGEXTRA_DOMAINS}" ]] ||
    [[ ! "${VALIDATION}" = "${ORIGVALIDATION}" ]] ||
    [[ ! "${DNSPLUGIN}" = "${ORIGDNSPLUGIN}" ]] ||
    [[ ! "${PROPAGATION}" = "${ORIGPROPAGATION}" ]] ||
    [[ ! "${STAGING}" = "${ORIGSTAGING}" ]] ||
    [[ ! "${CERTPROVIDER}" = "${ORIGCERTPROVIDER}" ]]; then
    echo "Different validation parameters entered than what was used before. Revoking and deleting existing certificate, and an updated one will be created"
    if [[ "${ORIGCERTPROVIDER}" = "zerossl" ]] && [[ -n "${ORIGEMAIL}" ]]; then
        REV_EAB_CREDS=$(curl -s https://api.zerossl.com/acme/eab-credentials-email --data "email=${ORIGEMAIL}")
        REV_ZEROSSL_EAB_KID=$(echo "${REV_EAB_CREDS}" | python3 -c "import sys, json; print(json.load(sys.stdin)['eab_kid'])")
        REV_ZEROSSL_EAB_HMAC_KEY=$(echo "${REV_EAB_CREDS}" | python3 -c "import sys, json; print(json.load(sys.stdin)['eab_hmac_key'])")
        if [[ -z "${REV_ZEROSSL_EAB_KID}" ]] || [[ -z "${REV_ZEROSSL_EAB_HMAC_KEY}" ]]; then
            echo "Unable to retrieve EAB credentials from ZeroSSL. Check the outgoing connections to api.zerossl.com and dns. Sleeping."
            sleep infinity
        fi
        REV_ACMESERVER="https://acme.zerossl.com/v2/DV90 --eab-kid ${REV_ZEROSSL_EAB_KID} --eab-hmac-key ${REV_ZEROSSL_EAB_HMAC_KEY}"
    elif [[ "${ORIGSTAGING}" = "true" ]]; then
        REV_ACMESERVER="https://acme-staging-v02.api.letsencrypt.org/directory"
    else
        REV_ACMESERVER="https://acme-v02.api.letsencrypt.org/directory"
    fi
    if [[ -f /config/etc/letsencrypt/live/"${ORIGDOMAIN}"/fullchain.pem ]]; then
        certbot revoke --non-interactive --cert-path /config/etc/letsencrypt/live/"${ORIGDOMAIN}"/fullchain.pem --server ${REV_ACMESERVER} || true
    fi
    rm -rf /config/etc/letsencrypt/{accounts,archive,live,renewal}
fi

# saving new variables
echo -e "ORIGURL=\"${URL}\" ORIGSUBDOMAINS=\"${SUBDOMAINS}\" ORIGONLY_SUBDOMAINS=\"${ONLY_SUBDOMAINS}\" ORIGEXTRA_DOMAINS=\"${EXTRA_DOMAINS}\" ORIGVALIDATION=\"${VALIDATION}\" ORIGDNSPLUGIN=\"${DNSPLUGIN}\" ORIGPROPAGATION=\"${PROPAGATION}\" ORIGSTAGING=\"${STAGING}\" ORIGCERTPROVIDER=\"${CERTPROVIDER}\" ORIGEMAIL=\"${EMAIL}\"" >/config/.donoteditthisfile.conf

# Check if the cert is using the old LE root cert, revoke and regen if necessary
if [[ -f "/config/keys/letsencrypt/chain.pem" ]] && { [[ "${CERTPROVIDER}" == "letsencrypt" ]] || [[ "${CERTPROVIDER}" == "" ]]; } && [[ "${STAGING}" != "true" ]] && ! openssl x509 -in /config/keys/letsencrypt/chain.pem -noout -issuer | grep -q "ISRG Root X"; then
    echo "The cert seems to be using the old LE root cert, which is no longer valid. Deleting and revoking."
    REV_ACMESERVER="https://acme-v02.api.letsencrypt.org/directory"
    if [[ -f /config/etc/letsencrypt/live/"${ORIGDOMAIN}"/fullchain.pem ]]; then
        certbot revoke --non-interactive --cert-path /config/etc/letsencrypt/live/"${ORIGDOMAIN}"/fullchain.pem --server ${REV_ACMESERVER} || true
    fi
    rm -rf /config/etc/letsencrypt/{accounts,archive,live,renewal}
fi

# if zerossl is selected or staging is set to true, use the relevant server
if [[ "${CERTPROVIDER}" = "zerossl" ]] && [[ "${STAGING}" = "true" ]]; then
    echo "ZeroSSL does not support staging mode, ignoring STAGING variable"
fi
if [[ "${CERTPROVIDER}" = "zerossl" ]] && [[ -n "${EMAIL}" ]]; then
    echo "ZeroSSL is selected as the cert provider, registering cert with ${EMAIL}"
    ACMESERVER="https://acme.zerossl.com/v2/DV90"
elif [[ "${CERTPROVIDER}" = "zerossl" ]] && [[ -z "${EMAIL}" ]]; then
    echo "ZeroSSL is selected as the cert provider, but the e-mail address has not been entered. Please visit https://zerossl.com, register a new account and set the account e-mail address in the EMAIL environment variable"
    sleep infinity
elif [[ "${STAGING}" = "true" ]]; then
    echo "NOTICE: Staging is active"
    echo "Using Let's Encrypt as the cert provider"
    ACMESERVER="https://acme-staging-v02.api.letsencrypt.org/directory"
else
    echo "Using Let's Encrypt as the cert provider"
    ACMESERVER="https://acme-v02.api.letsencrypt.org/directory"
fi

# figuring out url only vs url & subdomains vs subdomains only
if [[ -n "${SUBDOMAINS}" ]]; then
    echo "SUBDOMAINS entered, processing"
    if [[ "${SUBDOMAINS}" = "wildcard" ]]; then
        if [[ "${ONLY_SUBDOMAINS}" = true ]]; then
            export URL_REAL="-d *.${URL}"
            echo "Wildcard cert for only the subdomains of ${URL} will be requested"
        else
            export URL_REAL="-d *.${URL} -d ${URL}"
            echo "Wildcard cert for ${URL} will be requested"
        fi
    else
        echo "SUBDOMAINS entered, processing"
        for job in $(echo "${SUBDOMAINS}" | tr "," " "); do
            export SUBDOMAINS_REAL="${SUBDOMAINS_REAL} -d ${job}.${URL}"
        done
        if [[ "${ONLY_SUBDOMAINS}" = true ]]; then
            URL_REAL="${SUBDOMAINS_REAL}"
            echo "Only subdomains, no URL in cert"
        else
            URL_REAL="-d ${URL}${SUBDOMAINS_REAL}"
        fi
        echo "Sub-domains processed are: ${SUBDOMAINS_REAL}"
    fi
else
    echo "No subdomains defined"
    URL_REAL="-d ${URL}"
fi

# add extra domains
if [[ -n "${EXTRA_DOMAINS}" ]]; then
    echo "EXTRA_DOMAINS entered, processing"
    for job in $(echo "${EXTRA_DOMAINS}" | tr "," " "); do
        export EXTRA_DOMAINS_REAL="${EXTRA_DOMAINS_REAL} -d ${job}"
    done
    echo "Extra domains processed are: ${EXTRA_DOMAINS_REAL}"
    URL_REAL="${URL_REAL} ${EXTRA_DOMAINS_REAL}"
fi

# figuring out whether to use e-mail and which
if [[ ${EMAIL} == *@* ]]; then
    echo "E-mail address entered: ${EMAIL}"
    EMAILPARAM="-m ${EMAIL} --no-eff-email"
else
    echo "No e-mail address entered or address invalid"
    EMAILPARAM="--register-unsafely-without-email"
fi

# alter extension for error message
if [[ "${DNSPLUGIN}" = "google" ]]; then
    DNSCREDENTIALFILE="/config/dns-conf/${DNSPLUGIN}.json"
else
    DNSCREDENTIALFILE="/config/dns-conf/${DNSPLUGIN}.ini"
fi

# setting the validation method to use
if [[ "${VALIDATION}" = "dns" ]]; then
    AUTHENTICATORPARAM="--authenticator dns-${DNSPLUGIN}"
    DNSCREDENTIALSPARAM="--dns-${DNSPLUGIN}-credentials ${DNSCREDENTIALFILE}"
    if [[ -n "${PROPAGATION}" ]]; then PROPAGATIONPARAM="--dns-${DNSPLUGIN}-propagation-seconds ${PROPAGATION}"; fi

    # plugins that don't support setting credentials file
    if [[ "${DNSPLUGIN}" =~ ^(route53|standalone)$ ]]; then
        DNSCREDENTIALSPARAM=""
    fi
    # plugins that don't support setting propagation
    if [[ "${DNSPLUGIN}" =~ ^(azure|gandi|standalone)$ ]]; then
        if [[ -n "${PROPAGATION}" ]]; then echo "${DNSPLUGIN} dns plugin does not support setting propagation time"; fi
        PROPAGATIONPARAM=""
    fi
    # plugins that use old parameter naming convention
    if [[ "${DNSPLUGIN}" =~ ^(cpanel)$ ]]; then
        AUTHENTICATORPARAM="--authenticator ${DNSPLUGIN}"
        DNSCREDENTIALSPARAM="--${DNSPLUGIN}-credentials ${DNSCREDENTIALFILE}"
        if [[ -n "${PROPAGATION}" ]]; then PROPAGATIONPARAM="--${DNSPLUGIN}-propagation-seconds ${PROPAGATION}"; fi
    fi
    # don't restore txt records when using DuckDNS plugin
    if [[ "${DNSPLUGIN}" =~ ^(duckdns)$ ]]; then
        AUTHENTICATORPARAM="${AUTHENTICATORPARAM} --dns-${DNSPLUGIN}-no-txt-restore"
    fi

    PREFCHAL="${AUTHENTICATORPARAM} ${DNSCREDENTIALSPARAM} ${PROPAGATIONPARAM}"
    echo "${VALIDATION} validation via ${DNSPLUGIN} plugin is selected"
elif [[ "${VALIDATION}" = "tls-sni" ]]; then
    PREFCHAL="--standalone --preferred-challenges http"
    echo "*****tls-sni validation has been deprecated, attempting http validation instead"
else
    PREFCHAL="--standalone --preferred-challenges http"
    echo "http validation is selected"
fi

# generating certs if necessary
if [[ ! -f "/config/keys/letsencrypt/fullchain.pem" ]]; then
    if [[ "${CERTPROVIDER}" = "zerossl" ]] && [[ -n "${EMAIL}" ]]; then
        echo "Retrieving EAB from ZeroSSL"
        EAB_CREDS=$(curl -s https://api.zerossl.com/acme/eab-credentials-email --data "email=${EMAIL}")
        ZEROSSL_EAB_KID=$(echo "${EAB_CREDS}" | python3 -c "import sys, json; print(json.load(sys.stdin)['eab_kid'])")
        ZEROSSL_EAB_HMAC_KEY=$(echo "${EAB_CREDS}" | python3 -c "import sys, json; print(json.load(sys.stdin)['eab_hmac_key'])")
        if [[ -z "${ZEROSSL_EAB_KID}" ]] || [[ -z "${ZEROSSL_EAB_HMAC_KEY}" ]]; then
            echo "Unable to retrieve EAB credentials from ZeroSSL. Check the outgoing connections to api.zerossl.com and dns. Sleeping."
            sleep infinity
        fi
        ZEROSSL_EAB="--eab-kid ${ZEROSSL_EAB_KID} --eab-hmac-key ${ZEROSSL_EAB_HMAC_KEY}"
    fi
    echo "Generating new certificate"
    # shellcheck disable=SC2086
    certbot certonly --non-interactive --renew-by-default --server ${ACMESERVER} ${ZEROSSL_EAB} ${PREFCHAL} --rsa-key-size 4096 ${EMAILPARAM} --agree-tos ${URL_REAL}
    if [[ ! -d /config/keys/letsencrypt ]]; then
        if [[ "${VALIDATION}" = "dns" ]]; then
            echo "ERROR: Cert does not exist! Please see the validation error above. Make sure you entered correct credentials into the ${DNSCREDENTIALFILE} file."
        else
            echo "ERROR: Cert does not exist! Please see the validation error above. The issue may be due to incorrect dns or port forwarding settings. Please fix your settings and recreate the container"
        fi
        sleep infinity
    fi
    run-parts /config/etc/letsencrypt/renewal-hooks/deploy/
    echo "New certificate generated; starting nginx"
else
    echo "Certificate exists; parameters unchanged; starting nginx"
fi

# if certbot generated key exists, remove self-signed cert and replace it with symlink to live cert
if [[ -d /config/keys/letsencrypt ]]; then
    rm -rf /config/keys/cert.crt
    ln -s ./letsencrypt/fullchain.pem /config/keys/cert.crt
    rm -rf /config/keys/cert.key
    ln -s ./letsencrypt/privkey.pem /config/keys/cert.key
fi