#!/usr/bin/env bash
# manages acme accounts configured into certbot

SPATH="$(dirname "$(readlink -f ${BASH_SOURCE[0]})")"    # dir of the script
SNAME="$(basename ${BASH_SOURCE[0]})"                    # name of the script

ASCEDSCONF='local site certbot'
ASCEDSLIBS='utils'

if [ ! -r "${SPATH}/asceds-certbot-head" ] ; then
   echo "No header ${SPATH}/asceds-certbot-head. Exiting"
   exit 9
fi

. ${SPATH}/asceds-certbot-head

asceds-acme-accounts-usage()
{
  cat << ENDUSAGE

Usage: asceds-acme-accounts [-h]

Interfaces or disconnects ACME accounts configured into certbot.

-h --> display usage and exit

Checks the certbot ACME registered accounts:
* at least one account exists
* no account corresponds do multiple asceds configuration files asceds-acme-<NAME>.conf
  (deletes duplicates) - detected based on ACMECBACC value
* corresponding ACMESERVER from the asceds configuration file is correct,
  otherwise delete configuration file
Prompts to add configuration files for ACME accounts that don't have one,
  or to delete configuration files for ACME accounts that have one.
  When adding, set the default account for certbot.
Checks the consistency of configuration files:
* for every ASCEDS ACME configuration file, a correstonding certbot account must exist:
  checks the certbot account PATH and ID;
  otherwise, the configuration file is removed
* checks the default account consistency:
  only one account is marked as default
* makes default account settings consistent with asceds-certbot.conf
 
ENDUSAGE
exit 0
}

# general validation and parse of the command line
for ARG in ${ARGS} ; do
   #echo "ARG=${ARG}"
   if [ "${ARG}" != "${ARG//[^-a-zA-Z0-9.,]/}" ] ; then
      asceds-error "Malformed argument ${ARG}"
   elif [ "${ARG}" == "-h" ] ; then
      asceds-acme-accounts-usage
   else
      shift
   fi
done

# run as root only
asceds-run-asroot

# manage the current accounts configured into certbot
CERTBOTACCDIR="${CERTBOTCONFDIR}/accounts"
if [[ ! -d "${CERTBOTACCDIR}" ]]; then
    asceds-error "Certbot accounts directory not found: ${CERTBOTACCDIR}"
fi

ACCFOUND=0
while IFS= read -r -d '' REGR_FILE <&3 ; do
   ACCFOUND=1

   ACCDIR=${REGR_FILE%/regr.json}
   ACCID=${ACCDIR##*/}
   SERVERDIR=${ACCDIR%/*}
   SERVERPATH=${SERVERDIR#"${CERTBOTACCDIR}"/}

   # Certbot turns the ACME directory URL into this directory structure.
   SERVERURL="https://${SERVERPATH}"

   if [[ -n $(find "${ASCEDSETCDIR}" -maxdepth 1 -type f \
         -name 'asceds-acme-*.conf' -print -quit) ]] ; then
      ACCNAMELIST=$( 
         grep -lF -- "${ACCID}" "${ASCEDSETCDIR}"/asceds-acme-*.conf 2>/dev/null |
         sed -e 's|.*/asceds-acme-||' -e 's|\.conf$||' |
         paste -sd ' ' - 
      )
   else
      ACCNAMELIST=''
   fi

   ACCCOUNT=$(wc -w <<< "${ACCNAMELIST}")
   # test if ACCID is not found in multiple config files
   if (( ACCCOUNT > 1 )) ; then
      echo "There are multiple ASCEDS configurations for the same ACME account: ${ACCNAMELIST}"
      ACMEACC2=''
      echo -n "Remaining ASCEDS configuration: "
      read ACMEACC2

      # check if in the list
      if printf '%s' "${ACCNAMELIST}" | tr ' ' '\n' | grep -Fxq -- "${ACMEACC2}"; then
         for ACMEACCNAME in  ${ACCNAMELIST} ; do
            if [[ "${ACMEACCNAME}" != "${ACMEACC2}" ]]; then
               ${RM} -f "${ASCEDSETCDIR}"/asceds-acme-"${ACMEACCNAME}".conf
            else
               HAS_SERVER=$( grep -lF -- "${SERVERURL}" \
                            "${ASCEDSETCDIR}"/asceds-acme-"${ACMEACCNAME}".conf 2>/dev/null )
               if [ -z "${HAS_SERVER}" ] ; then
                  echo "Configuration ${ACMEACCNAME} does not contain the right ACMESERVER" 
                  echo "${SERVERURL} for the account. Deleting configuration."
                  ${RM} -f "${ASCEDSETCDIR}"/asceds-acme-"${ACMEACCNAME}".conf
               fi
            fi
         done
      else
         echo "Specified configuration ${ACMEACC2} not in the configurations list ${ACCNAMELIST}"
         asceds-error "Decide what configuration to keep and run asceds-acme-accounts again."
      fi   
   elif (( ACCCOUNT == 1 )) ; then
      ACMEACCNAME=$( printf '%s' "${ACCNAMELIST}" | xargs )
      HAS_SERVER=$( grep -lF -- "${SERVERURL}" \
                   "${ASCEDSETCDIR}"/asceds-acme-"${ACMEACCNAME}".conf 2>/dev/null )
      if [ -z "${HAS_SERVER}" ] ; then
         echo "Configuration ${ACMEACCNAME} does not contain the right ACMESERVER" 
         echo "${SERVERURL} for the account. Deleting configuration."
         ${RM} -f "${ASCEDSETCDIR}"/asceds-acme-"${ACMEACCNAME}".conf
      fi
   fi
done 3< <(find "${CERTBOTACCDIR}" -type f -name regr.json -print0 | sort -z)

# if no certbot-registered ACME account is found
if (( ACCFOUND == 0 )); then
    echo "No Certbot ACME accounts registered under ${CERTBOTACCDIR}"
    asceds-error "Register ACME account with certbot and run asceds-acme-accounts again."
fi


# offer to add/remove ASCEDS config for the account
while IFS= read -r -d '' REGR_FILE <&3 ; do

   ACCDIR=${REGR_FILE%/regr.json}
   ACCID=${ACCDIR##*/}
   SERVERDIR=${ACCDIR%/*}
   SERVERPATH=${SERVERDIR#"${CERTBOTACCDIR}"/}

   # Certbot turns the ACME directory URL into this directory structure.
   SERVERURL="https://${SERVERPATH}"

   if [[ -n $(find "${ASCEDSETCDIR}" -maxdepth 1 -type f \
         -name 'asceds-acme-*.conf' -print -quit) ]] ; then
      HAS_SERVER=$( grep -lF -- "${ACCID}" \
                "${ASCEDSETCDIR}"/asceds-acme-*.conf 2>/dev/null )
   else
      HAS_SERVER=''
   fi

   if [ -n "${HAS_SERVER}" ] ; then
      echo "####################"
      cat ${HAS_SERVER}
      echo "####################"
      YES=''
      echo -n "Remove configuration ${HAS_SERVER}? [y/N] "
      read YES
      if [ "${YES}" == "y" ] || [ "${YES}" == "Y" ] ; then
         ${RM} -f ${HAS_SERVER}
      fi
   else
      echo "####################"
      echo "Certbot Account ID:      ${ACCID}"
      echo "ACME server:     ${SERVERURL}"
      echo "####################"
      YES=''
      echo -n "Add configuration for the account above? [y/N] "
      read YES
      if [ "${YES}" == "y" ] || [ "${YES}" == "Y" ] ; then
         ACMEACCNAME=''
         echo -n "Type the ASCEDS configuration name for the ACME account - max 16 characters [_a-zA-Z0-9]: "
         read ACMEACCNAME
         if [ "${ACMEACCNAME}" != "${ACMEACCNAME//[^_a-zA-Z0-9]/}" ] ; then
            echo "Configuration name ${ACMEACCNAME} contains wrong characters"
            asceds-error "Run asceds-acme-accounts again."
         elif  (( ${#ACMEACCNAME} > 16 )); then
            echo "Configuration name ${ACMEACCNAME} contains more than 16 characters"
            asceds-error "Run asceds-acme-accounts again."
         fi
         asceds-refresh-var ${ASCEDSETCDIR}/asceds-acme-${ACMEACCNAME}.conf "ACMESERVER" "${SERVERURL}"
         asceds-refresh-var ${ASCEDSETCDIR}/asceds-acme-${ACMEACCNAME}.conf "ACMECBACC" "${ACCID}"

         # set as default? 
         ACMEDEFACC=''
         YES=''
         echo -n "Set account ${ACMEACCNAME} as default for certbot? [y/N] "
         read YES
         if [ "${YES}" == "y" ] || [ "${YES}" == "Y" ] ; then
            ACMEDEFACC='yes'
         fi
         asceds-refresh-var ${ASCEDSETCDIR}/asceds-acme-${ACMEACCNAME}.conf "ACMEDEFACC" "${ACMEDEFACC}"
      fi
   fi
done 3< <(find "${CERTBOTACCDIR}" -type f -name regr.json -print0 | sort -z)

# check consistency of current asceds-acme-*.conf files
# each asceds-acme-*.conf should contain
#     ACMESERVER
#     ACMECBACC
#     ACMEDEFACC -- empty or 'yes'

ACCNAME=''
ACMEACCLIST1=''
ACMEDEFACC1=''
# for every ACME account file configured in ASCEDS,
# test if there is a registered certbot account
if [[ -z $(find "${ASCEDSETCDIR}" -maxdepth 1 -type f \
      -name 'asceds-acme-*.conf' -print -quit) ]] ; then
   asceds-error "No ACME accounts configured into ASCEDS"
fi
for ACMECONFFILE in ${ASCEDSETCDIR}/asceds-acme-*.conf; do
   # the ASCEDS client account name CLIENT_ACME_ACC
   ACCNAME=${ACMECONFFILE#"${ASCEDSETCDIR}"/asceds-acme-}
   ACCNAME=${ACCNAME%.conf}

   # check the content
   source ${ACMECONFFILE}
    
   # test if account exists
   if [ -r "${CERTBOTACCDIR}/${ACMESERVER#https://}/${ACMECBACC}/regr.json" ] ; then
      ACMEACCLIST1="${ACMEACCLIST1} ${ACCNAME}"
      if [ -n "${ACMEDEFACC}" ] ; then
         echo "Account ${ACCNAME} is set up as default"
         ACMEDEFACC1="${ACMEDEFACC1} ${ACCNAME}"
      fi
   else
      YES=''
      echo -n "Account ${ACCNAME} does not exist. Remove the configuration? [Y/n] "
      read YES
      if [ "${YES}" != "n" ] && [ "${YES}" != "N" ] ; then
         ${RM} -f ${ACMECONFFILE} 
      else
         asceds-error "Register ACME account with certbot and run asceds-acme-accounts again."
      fi 
   fi
done

# exit if no valid ACME account registered with certbot
ACCCOUNT=$(wc -w <<< "${ACMEACCLIST1}")
if (( ACCCOUNT < 1 )) ; then
   asceds-error "Register ACME account with certbot and run asceds-acme-accounts again."
fi

# check the default account consistency
DEFCOUNT=$(wc -w <<< "${ACMEDEFACC1}")
if (( DEFCOUNT > 1 )) ; then
   for ACMEACCNAME in ${ACMEDEFACC1} ; do
      asceds-refresh-var ${ASCEDSETCDIR}/asceds-acme-${ACMEACCNAME}.conf "ACMEDEFACC" ""
   done
fi
if (( DEFCOUNT > 1 )) || (( DEFCOUNT < 1 )) ; then
   echo "There are multiple or no default ACME accounts: ${ACMEDEFACC1}"
   echo "Please pick only one of the following accounts as default: ${ACMEACCLIST1}"
   ACMEDEFACC2=''
   echo -n "Default ACME account: "
   read ACMEDEFACC2
   
   # check if in the list
   if printf '%s' "${ACMEACCLIST1}" | tr ' ' '\n' | grep -Fxq -- "${ACMEDEFACC2}" ; then
      ACMEDEFACC1=${ACMEDEFACC2}
   else
      echo "Specified account ${ACMEDEFACC2} not in the account list ${ACMEACCLIST1}"
      asceds-error "Decide what ACME account is the default and run asceds-acme-accounts again."
   fi   
fi

ACMEDEFACC1=$( printf '%s' "${ACMEDEFACC1}" | xargs )
asceds-refresh-var ${ASCEDSETCDIR}/asceds-acme-${ACMEDEFACC1}.conf "ACMEDEFACC" "yes"

# make default account settings consistent with asceds-certbot.conf
source ${ASCEDSETCDIR}/asceds-acme-${ACMEDEFACC1}.conf
asceds-refresh-var ${ASCEDSETCDIR}/asceds-certbot.conf "ACMECBACC" "${ACMECBACC}"
asceds-refresh-var ${ASCEDSETCDIR}/asceds-certbot.conf "ACMESERVER" "${ACMESERVER}"


