#!/usr/bin/with-contenv 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"

# Echo init finish for test runs
if [ -n "${TEST_RUN}" ]; then
	echo '[services.d] done.'
fi

# 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 that the required variables are set
[[ -z "$URL" ]] && \
  echo "Please pass your URL as an environment variable in your docker run command. See docker info for more details." && \
  sleep infinity

# make our folders and links
mkdir -p \
	/config/{log/letsencrypt,log/fail2ban,etc/letsencrypt,fail2ban,crontabs,dns-conf,geoip2db} \
	/var/run/fail2ban
rm -rf /etc/letsencrypt
ln -s /config/etc/letsencrypt /etc/letsencrypt

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

# copy reverse proxy configs
cp -R /defaults/proxy-confs /config/nginx/

# copy/update the fail2ban config defaults to/in /config
cp -R /defaults/fail2ban/filter.d /config/fail2ban/
cp -R /defaults/fail2ban/action.d /config/fail2ban/
# if jail.local is missing in /config, copy default
[[ ! -f /config/fail2ban/jail.local ]] && \
	cp /defaults/jail.local /config/fail2ban/jail.local
# Replace fail2ban config with user config
[[ -d /etc/fail2ban/filter.d ]] && \
	rm -rf /etc/fail2ban/filter.d
[[ -d /etc/fail2ban/action.d ]] && \
	rm -rf /etc/fail2ban/action.d
cp -R /config/fail2ban/filter.d /etc/fail2ban/
cp -R /config/fail2ban/action.d /etc/fail2ban/
cp /defaults/fail2ban/fail2ban.local /etc/fail2ban/
cp /config/fail2ban/jail.local /etc/fail2ban/jail.local

# copy crontab and proxy defaults if needed
[[ ! -f /config/crontabs/root ]] && \
	cp /etc/crontabs/root /config/crontabs/
[[ ! -f /config/nginx/proxy.conf ]] && \
	cp /defaults/proxy.conf /config/nginx/proxy.conf
[[ ! -f /config/nginx/ssl.conf ]] && \
	cp /defaults/ssl.conf /config/nginx/ssl.conf
[[ ! -f /config/nginx/ldap.conf ]] && \
	cp /defaults/ldap.conf /config/nginx/ldap.conf
[[ ! -f /config/nginx/authelia-server.conf ]] && \
	cp /defaults/authelia-server.conf /config/nginx/authelia-server.conf
[[ ! -f /config/nginx/authelia-location.conf ]] && \
	cp /defaults/authelia-location.conf /config/nginx/authelia-location.conf
[[ ! -f /config/nginx/geoip2.conf ]] && \
	cp /defaults/geoip2.conf /config/nginx/geoip2.conf
[[ ! -f /config/www/502.html ]] &&
	cp /defaults/502.html /config/www/502.html

# remove lua bits from nginx.conf if not done before
if ! grep -q '#Removed lua' /config/nginx/nginx.conf; then 
  echo "Removing lua specific info from nginx.conf"
  sed -i 's|\tlua_load_resty_core off;|\t#Removed lua. Do not remove this comment|g' /config/nginx/nginx.conf
fi

# copy pre-generated dhparams or generate if needed
[[ ! -f /config/nginx/dhparams.pem ]] && \
  cp /defaults/dhparams.pem /config/nginx/dhparams.pem
if ! grep -q 'PARAMETERS' "/config/nginx/dhparams.pem"; then
  curl -o /config/nginx/dhparams.pem -L "https://lsio.ams3.digitaloceanspaces.com/dhparams.pem"
fi
if ! grep -q 'PARAMETERS' "/config/nginx/dhparams.pem"; then
  echo "Generating dhparams.pem. This will take a long time. Do not stop the container until this process is completed."
  openssl dhparam -out /config/nginx/dhparams.pem 4096
fi

# check to make sure DNSPLUGIN is selected if dns validation is used
[[ "$VALIDATION" = "dns" ]] && [[ ! "$DNSPLUGIN" =~ ^(aliyun|cloudflare|cloudxns|cpanel|digitalocean|dnsimple|dnsmadeeasy|domeneshop|gandi|gehirn|google|hetzner|inwx|linode|luadns|netcup|njalla|nsone|ovh|rfc2136|route53|sakuracloud|transip)$ ]] && \
  echo "Please set the DNSPLUGIN variable to a valid plugin name. See docker info for more details." && \
  sleep infinity

# import user crontabs
rm /etc/crontabs/*
cp /config/crontabs/* /etc/crontabs/

# 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\" ORIGDUCKDNSTOKEN=\"$DUCKDNSTOKEN\" ORIGCERTPROVIDER=\"$CERTPROVIDER\" ORIGEMAIL=\"$EMAIL\"" > /config/.donoteditthisfile.conf
  echo "Created .donoteditthisfile.conf"
fi

# load original config settings
# shellcheck disable=SC1091
. /config/.donoteditthisfile.conf

# set default validation to http
if [ -z "$VALIDATION" ]; then
  VALIDATION="http"
  echo "VALIDATION parameter not set; setting it to http"
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

# update plugin names in dns conf inis
sed -i 's|^certbot_dns_aliyun:||g' /config/dns-conf/aliyun.ini
sed -i 's|^certbot_dns_domeneshop:||g' /config/dns-conf/domeneshop.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

# setting the validation method to use
if [ "$VALIDATION" = "dns" ]; then
  if [ "$DNSPLUGIN" = "route53" ]; then
    if [ -n "$PROPAGATION" ];then PROPAGATIONPARAM="--dns-${DNSPLUGIN}-propagation-seconds ${PROPAGATION}"; fi
    PREFCHAL="--dns-${DNSPLUGIN} ${PROPAGATIONPARAM}"
  elif [[ "$DNSPLUGIN" =~ ^(cpanel)$ ]]; then
    if [ -n "$PROPAGATION" ];then PROPAGATIONPARAM="--certbot-dns-${DNSPLUGIN}:${DNSPLUGIN}-propagation-seconds ${PROPAGATION}"; fi
    PREFCHAL="-a certbot-dns-${DNSPLUGIN}:${DNSPLUGIN} --certbot-dns-${DNSPLUGIN}:${DNSPLUGIN}-credentials /config/dns-conf/${DNSPLUGIN}.ini ${PROPAGATIONPARAM}"
  elif [[ "$DNSPLUGIN" =~ ^(gandi)$ ]]; then
    if [ -n "$PROPAGATION" ];then echo "Gandi dns plugin does not support setting propagation time"; fi
    PREFCHAL="-a certbot-plugin-${DNSPLUGIN}:dns --certbot-plugin-${DNSPLUGIN}:dns-credentials /config/dns-conf/${DNSPLUGIN}.ini"
  elif [[ "$DNSPLUGIN" =~ ^(google)$ ]]; then
    if [ -n "$PROPAGATION" ];then PROPAGATIONPARAM="--dns-${DNSPLUGIN}-propagation-seconds ${PROPAGATION}"; fi
    PREFCHAL="--dns-${DNSPLUGIN} --dns-${DNSPLUGIN}-credentials /config/dns-conf/${DNSPLUGIN}.json ${PROPAGATIONPARAM}"
  elif [[ "$DNSPLUGIN" =~ ^(aliyun|domeneshop|hetzner|inwx|netcup|njalla|transip)$ ]]; then
    if [ -n "$PROPAGATION" ];then PROPAGATIONPARAM="--dns-${DNSPLUGIN}-propagation-seconds ${PROPAGATION}"; fi
    PREFCHAL="-a dns-${DNSPLUGIN} --dns-${DNSPLUGIN}-credentials /config/dns-conf/${DNSPLUGIN}.ini ${PROPAGATIONPARAM}"
  else
    if [ -n "$PROPAGATION" ];then PROPAGATIONPARAM="--dns-${DNSPLUGIN}-propagation-seconds ${PROPAGATION}"; fi
    PREFCHAL="--dns-${DNSPLUGIN} --dns-${DNSPLUGIN}-credentials /config/dns-conf/${DNSPLUGIN}.ini ${PROPAGATIONPARAM}"
  fi
  echo "${VALIDATION} validation via ${DNSPLUGIN} plugin is selected"
elif [ "$VALIDATION" = "tls-sni" ]; then
  PREFCHAL="--non-interactive --standalone --preferred-challenges http"
  echo "*****tls-sni validation has been deprecated, attempting http validation instead"
elif [ "$VALIDATION" = "duckdns" ]; then
  PREFCHAL="--non-interactive --manual --preferred-challenges dns --manual-auth-hook /app/duckdns-txt"
  chmod +x /app/duckdns-txt
  echo "duckdns validation is selected"
  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 URL_REAL="-d *.${URL}"
  else
    echo "the resulting certificate will only cover the main domain due to a limitation of duckdns, ie. subdomain.duckdns.org"
    export URL_REAL="-d ${URL}"
  fi
else
  PREFCHAL="--non-interactive --standalone --preferred-challenges http"
  echo "http validation is selected"
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

# 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" ] || [ ! "$DUCKDNSTOKEN" = "$ORIGDUCKDNSTOKEN" ] || [ ! "$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 [ "$ORIGONLY_SUBDOMAINS" = "true" ] && [ ! "$ORIGSUBDOMAINS" = "wildcard" ]; then
    ORIGDOMAIN="$(echo "$ORIGSUBDOMAINS" | tr ',' ' ' | awk '{print $1}').${ORIGURL}"
  else
    ORIGDOMAIN="$ORIGURL"
  fi
  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
  [[ -f /config/etc/letsencrypt/live/"$ORIGDOMAIN"/fullchain.pem ]] && certbot revoke --non-interactive --cert-path /config/etc/letsencrypt/live/"$ORIGDOMAIN"/fullchain.pem --server $REV_ACMESERVER
  rm -rf /config/etc/letsencrypt
  mkdir -p /config/etc/letsencrypt
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\" ORIGDUCKDNSTOKEN=\"$DUCKDNSTOKEN\" ORIGCERTPROVIDER=\"$CERTPROVIDER\" ORIGEMAIL=\"$EMAIL\"" > /config/.donoteditthisfile.conf

# alter extension for error message
if [ "$DNSPLUGIN" = "google" ]; then
  FILENAME="$DNSPLUGIN.json"
else
  FILENAME="$DNSPLUGIN.ini"
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 --renew-by-default --server $ACMESERVER $ZEROSSL_EAB $PREFCHAL --rsa-key-size 4096 $EMAILPARAM --agree-tos $URL_REAL
  if [ -d /config/keys/letsencrypt ]; then
    cd /config/keys/letsencrypt || exit
  else
    if [ "$VALIDATION" = "dns" ]; then
      echo "ERROR: Cert does not exist! Please see the validation error above. Make sure you entered correct credentials into the /config/dns-conf/${FILENAME} file."
    elif [ "$VALIDATION" = "duckdns" ]; then
      echo "ERROR: Cert does not exist! Please see the validation error above. Make sure your DUCKDNSTOKEN is correct."
    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
  openssl pkcs12 -export -out privkey.pfx -inkey privkey.pem -in cert.pem -certfile chain.pem -passout pass:
  sleep 1
  cat {privkey,fullchain}.pem > priv-fullchain-bundle.pem
  echo "New certificate generated; starting nginx"
else
  echo "Certificate exists; parameters unchanged; starting nginx"
fi

# create GeoIP2 folder symlink
[[ -d /var/lib/libmaxminddb ]] && [[ ! -L /var/lib/libmaxminddb ]] && \
  rm -rf /var/lib/libmaxminddb
[[ ! -d /var/lib/libmaxminddb ]] && \
  ln -s /config/geoip2db /var/lib/libmaxminddb
# check GeoIP2 database
if [ -n "$MAXMINDDB_LICENSE_KEY" ]; then
  sed -i "s|.*MAXMINDDB_LICENSE_KEY.*|MAXMINDDB_LICENSE_KEY=\"${MAXMINDDB_LICENSE_KEY}\"|g" /etc/conf.d/libmaxminddb
  if [ ! -f /var/lib/libmaxminddb/GeoLite2-City.mmdb ]; then
    echo "Downloading GeoIP2 City database."
    /etc/periodic/weekly/libmaxminddb
  fi
elif [ -f /var/lib/libmaxminddb/GeoLite2-City.mmdb ]; then
  echo -e "Currently using the user provided GeoLite2-City.mmdb.\nIf you want to enable weekly auto-updates of the database, retrieve a free license key from MaxMind,\nand add a new env variable \"MAXMINDDB_LICENSE_KEY\", set to your license key."
else
  echo -e "Starting 2019/12/30, GeoIP2 databases require personal license key to download. Please retrieve a free license key from MaxMind,\nand add a new env variable \"MAXMINDDB_LICENSE_KEY\", set to your license key."
fi

# logfiles needed by fail2ban
[[ ! -f /config/log/nginx/error.log ]] && \
	touch /config/log/nginx/error.log
[[ ! -f /config/log/nginx/access.log ]] && \
	touch /config/log/nginx/access.log

# permissions
chown -R abc:abc \
	/config
chmod -R 0644 /etc/logrotate.d
chmod -R +r /config/log
chmod +x /app/le-renew.sh
chmod 700 /defaults/dns-conf
chmod 600 /defaults/dns-conf/*
