# Function list:
#
# asceds-source-etcconf "etcdir" "conflist"
# executes the config files in <conflist> in the right order
#
# asceds-extract "variable" "file"
# parses file and extracts the value of the first appearance of variable;
# stores unfiltered value in VARVAL
# 
# asceds-parse-certconf "cert.conf"
# parses cert.conf, filters the values, loads variables
#
# asceds-parse-newcertreq "req.cert"
# parses new certificate request file req.cert, filters the values, loads variables
# 
# asceds-parse-revokereq "req.rev"
# parses revoke request file req.rev, filters the values, loads variables
#  
# asceds-parse-propagatereq "req.asceds"
# parses ASCEDS propagate request file req.asceds, filters the values, loads variables
# 
# asceds-set-certbot-key
# sets the certbot --key-type option according to CLIENT_ENC in <hostname>_cert.conf
#
# asceds-build-certconf "noreturn"
# builds fresh ${ASCEDSHOMEDIR}/cert.conf used by the cert manager
# to generate certificates for managed clients
# 
# asceds-build-hostconf
# builds ${ASCEDSETCDIR}/asceds-hostconf.conf
# 
# asceds-fix-client-certconf "client_name" 
# creates/fixes a missing/incomplete ~asceds/${client_name}_cert.conf
# 
# asceds-build-siteconf
# builds fresh or adjusts existing site file ${ASCEDSETCDIR}/asceds-site.conf
# which is distributed to the clients
#
# asceds-parse-siteconf
# parses and filters certificate manager provided asceds-site.conf,
# create local ${ASCEDSETCDIR}/asceds-site.conf
#
# asceds-echo "message"
# issue info message; don't exit
#  
# asceds-warning "message"
# issue warning message; don't exit
# 
# asceds-error "message"
# issue error message; exit 1
# 
# asceds-greetings
# greeting messages issued by scripts
# 
# asceds-mailto-alarm "message" "logfile"
# send mail to $ALARM containing last 20 lines from <logfile> with subject <message>
# 
# asceds-run-asroot
# check if running as root; exit otherwise
# 
# asceds-run-asasceds
# check if running as asceds; exit otherwise
# 
# asceds-logdir-check
# check if log dir and log files exist, create otherwise
# 
# asceds-exec-noninteractive
# execute scripts in non-interactive mode -- tee stdout and stderr
# to ${ASCEDSLOGF} and ${ASCEDSERRF}
# 
# asceds-refresh-var "conffile" "varname" "newvalue"
# refresh/set variable "varname" with new value "newvalue" in file "conffile";
# if variable "varname" is not in file "conffile", a new assignment is added;
# if file "conffile" doesn't exist, it is created;
# the old value is stored to VARVAL_OLD;
# if the new value is the same with the old one, nothing is changed;
# 
# asceds-expiration-test "certfile" 
# tests if a certificate will expire in less than 
# 5 days(stale) or 2 days (almost expired); 
#    returns "2"=almost expired/mail to ALARM  "1"=stale, "0"=is not expiring
#
# asceds-cert-freshtest "certfile" 
# tests if a certificate was just created; 
#    returns "1" if old or if it doesn't exist, "0" if just created
# 
# asceds-check-domains "domainname" "sanslist" 
# checks if cert manager is authorized to generate requested certificate
#  
# asceds-ip-resolve "hostname"
# finds the IP address associated with <hostname> as provided by DNS 
# or by user input, loaded in the variable ASCEDS_IP and returns 0;
# if <hostname> cannot be resolved or IP invalid,
# returns 1 and sets ASCEDS_IP=10.100.100.100 
#  
# asceds-test-ping "hostname"
# tests if hostname is online and accessible using ping;
# returns "0"=host exists and online, "1"=host exists but offline
#         "2"=hostname unknown 
#  
# asceds-test-remoterw "hostname"
# tests remote asceds user read/write access through scp; 
# returns "0"=read/write successful, "1"=read-only, "2"=no ssh access at all
#
# asceds-client-showconf  
# show status of the client as reflected by ~asceds/cert.conf and 
# and ${ASCEDSETCDIR}/asceds-site.conf
# 
# asceds-configphp-get "file" "var"
# get value of variable <var> from php config file ${ASCEDSWEBDIR}/etc/<file>
# into ASCEDS_PHPVAL
#  
# asceds-configphp-set "file" "var" "value"
# change values of variables in the php file ${ASCEDSWEBDIR}/etc/<file>
#
# asceds-config-configphp
# reconfigures the config.php file used by the website
#  
# asceds-find-bin "bin1" "bin2" ...
# FULLBIN = the path to the first name found the list; empty if none found
#
# asceds-comma-uniq "list"
# order a comma separated list and removes duplicates
#
# asceds-refresh-webdomains
# propagate the AUTHDOMAIN values to /usr/share/asceds/etc/users.php
#
# asceds-check-netconf
# checks the network configuration of /etc/hosts, postfix and openssl.cnf 
# of the host; attempts to fix it
#
# asceds-expand-hostname "hostname"
# if short hostname is given, try to expand it using configured domains: 
# FQDN stored in ASCEDS_FQDN
# if replacement was performed, return 0, otherwise return 1
#
# asceds-validate-ip "ip_address"
# validates a string as an IP address: 0/true=valid 1/false=invalid
#
# asceds-validate-fqdn "FQDN"
# validates a string as a FQDN: 0/true=valid 1/false=invalid
#
# asceds-find-mail
# check for mailer
# 
# asceds-find-certbot
# check for certbot; if not found, request certbot install
#
# asceds-config-certbot
# config certbot to work with the ACME server
#
# asceds-change-userdom "user" "del"
# change/remove authority domains for "user"; returns 0 if successful
#
# asceds-change-email "user" "del"
# change/remove email address for "user"; returns 0 if successful
#
# asceds-change-passwd "user" "del"
# change/remove simple auth password for "user"; returns 0 if successful
#
# asceds-disable-host "client"
# remove "client" from asceds and certbot
#
####################################################################################
# asceds-source-etcconf "etcdir" "conflist"
# executes the config files in <conflist> in the right order

# Usage: asceds-source-etcconf "etcdir" "conflist"
asceds-source-etcconf()
{
   local ASCEDS_ETC="$1"
   local ASCEDS_CONF="$2"
   if [ -z "${ASCEDS_CONF}" ] ; then
      asceds-error "Config file list ASCEDS_CONF undefined."
   fi
   if [ -z "${ASCEDS_ETC}" ] ; then
      asceds-error "Config dir ASCEDS_ETC undefined."
   fi

   for CONFFILE in ${ASCEDS_CONF} ; do
      if [ -r "${ASCEDS_ETC}/asceds-${CONFFILE}.conf" ] ; then
         . ${ASCEDS_ETC}/asceds-${CONFFILE}.conf
      fi
   done
}

####################################################################################
# parses file and extracts the value of the first appearance of variable;
# stores unfiltered value in VARVAL
# 

# Usage asceds-extract "variable" "file"
asceds-extract()
{
   VARVAL=''
   VARNAME=$1
   VARFILE=$2

   if [ -z "${VARNAME}" ] ; then
      asceds-error "Empty variable name passed to asceds-extract."
   fi
   if [ ! -r "${VARFILE}" ] ; then
      asceds-error "File ${VARFILE} cannot be read."
   fi

   VARVAL=$( ${GREP} "^\s*${VARNAME}\s*=" ${VARFILE} | head -n 1 | \
             sed -e "s/^\s*${VARNAME}\s*=\s*// ; s/\s*$//" | \
             sed -e 's/^"// ; s/"$// ; s/^\s*// ; s/\s*$//' | \
             sed -e "s/^'// ; s/'$// ; s/^\s*// ; s/\s*$//"  )

}

####################################################################################
# parses cert.conf, filters the values, loads variables

# Usage: asceds-parse-certconf "cert.conf"
asceds-parse-certconf()
{
   local REQFILE="$1"
   if [ ! -r "${REQFILE}" ] ; then
      asceds-error "No certificate config file ${REQFILE}"
   fi 

   # hostname
   asceds-extract "CLIENT_DOMAIN_NAME" "${REQFILE}"
   VARVAL="$( cat <<< ${VARVAL} | tr '[:upper:]' '[:lower:]' )"
   CLIENT_DOMAIN_NAME="${VARVAL//[^-a-z0-9.]/}"
   asceds-validate-fqdn "${CLIENT_DOMAIN_NAME}" || \
      asceds-error "Invalid client FQDN ${CLIENT_DOMAIN_NAME}"
      
   # SANS list
   asceds-extract "CLIENT_SANS_LIST" "${REQFILE}"
   VARVAL="$( cat <<< ${VARVAL} | tr '[:upper:]' '[:lower:]' )"
   CLIENT_SANS_LIST=${VARVAL//[^-a-z0-9.,]/}

   # certificate id
   asceds-extract "ASCEDS_CERT_ID" "${REQFILE}"
   ASCEDS_CERT_ID=${VARVAL//[^a-zA-Z0-9]/}

   # read-only status
   asceds-extract "NO_RETURN" "${REQFILE}"
   VARVAL="$( cat <<< ${VARVAL} | tr '[:upper:]' '[:lower:]' )"
   NO_RETURN="${VARVAL//[^a-z]/}"

   # requested by      
   asceds-extract "NO_ASCEDS" "${REQFILE}"
   VARVAL="$( cat <<< ${VARVAL} | tr '[:upper:]' '[:lower:]' )"
   NO_ASCEDS=${VARVAL//[^-_a-z0-9.@]/}

   # platform
   asceds-extract "CLIENT_PLATFORM" "${REQFILE}"
   VARVAL="$( cat <<< ${VARVAL} | tr '[:upper:]' '[:lower:]' )"
   CLIENT_PLATFORM=${VARVAL//[^-_a-z0-9.]/}
   if [ -z "${CLIENT_PLATFORM}" ] ; then
      if [ -n "${ASCEDS_CERT_ID}" ] ; then 
         CLIENT_PLATFORM='unknown'
      else
         CLIENT_PLATFORM='linux'
      fi
   fi
 
   # certificate encoding
   asceds-extract "CLIENT_ENC" "${REQFILE}"
   VARVAL="$( cat <<< ${VARVAL} | tr '[:upper:]' '[:lower:]' )"
   CLIENT_ENC=${VARVAL//[^-_a-z0-9.]/}
   if [ -z "${CLIENT_ENC}" ] ; then 
      CLIENT_ENC='rsa'
   fi
}

##########################################################################################
# parses new certificate request file req.cert, filters the values, loads variables

# Usage: asceds-parse-newcertreq "req.cert"
asceds-parse-newcertreq()
{
   local REQFILE="$1"
   if [ ! -r "${REQFILE}" ] ; then
      asceds-error "No new certificate request file ${REQFILE}"
   fi 

   # hostname
   asceds-extract "CLIENT_DOMAIN_NAME" "${REQFILE}"
   VARVAL="$( cat <<< ${VARVAL} | tr '[:upper:]' '[:lower:]' )"
   CLIENT_DOMAIN_NAME="${VARVAL//[^-a-z0-9.]/}"
   asceds-validate-fqdn "${CLIENT_DOMAIN_NAME}" || \
      asceds-error "Invalid client FQDN ${CLIENT_DOMAIN_NAME}"
      
   # request time
   asceds-extract "REQUEST_DATE" "${REQFILE}"
   REQUEST_DATE=${VARVAL//[^-_a-zA-Z0-9.:\/ ]/}      
      
   # requested by      
   asceds-extract "REQUESTED_BY" "${REQFILE}"
   VARVAL="$( cat <<< ${VARVAL} | tr '[:upper:]' '[:lower:]' )"
   REQUESTED_BY=${VARVAL//[^-_a-zA-Z0-9.@]/}
      
   # SANS list
   asceds-extract "CLIENT_SANS_LIST" "${REQFILE}"
   VARVAL="$( cat <<< ${VARVAL} | tr '[:upper:]' '[:lower:]' )"
   CLIENT_SANS_LIST=${VARVAL//[^-a-z0-9.,]/}

   # change type
   asceds-extract "ACK_CHANGE_TYPE" "${REQFILE}"
   VARVAL="$( cat <<< ${VARVAL} | tr '[:upper:]' '[:lower:]' )"
   ACK_CHANGE_TYPE=${VARVAL//[^a-z]/}

}

##########################################################################################
# parses revoke request file req.rev, filters the values, loads variables
# loads API variables: CLIENT_DOMAIN_NAME, REQUEST_DATE, REQUESTED_BY

# Usage: asceds-parse-revokereq "req.rev"
asceds-parse-revokereq()
{
   local REQFILE="$1"
   if [ ! -r "${REQFILE}" ] ; then
      asceds-error "No revoke request file ${REQFILE}"
   fi 

   # hostname
   asceds-extract "CLIENT_DOMAIN_NAME" "${REQFILE}"
   VARVAL="$( cat <<< ${VARVAL} | tr '[:upper:]' '[:lower:]' )"
   CLIENT_DOMAIN_NAME="${VARVAL//[^-a-z0-9.]/}"
   asceds-validate-fqdn "${CLIENT_DOMAIN_NAME}" || \
      asceds-error "Invalid client FQDN ${CLIENT_DOMAIN_NAME}"

   # request time
   asceds-extract "REQUEST_DATE" "${REQFILE}"
   REQUEST_DATE=${VARVAL//[^-_a-zA-Z0-9.:\/ ]/}      
      
   # requested by
   asceds-extract "REQUESTED_BY" "${REQFILE}"
   VARVAL="$( cat <<< ${VARVAL} | tr '[:upper:]' '[:lower:]' )"
   REQUESTED_BY=${VARVAL//[^-_a-zA-Z0-9.@]/}

}

##########################################################################################
# parses ASCEDS propagate request file req.asceds, filters the values, loads variables

# Usage: asceds-parse-propagatereq "req.asceds"
asceds-parse-propagatereq()
{
   local REQFILE="$1"
   if [ ! -r "${REQFILE}" ] ; then
      asceds-error "No propagate request file ${REQFILE}"
   fi 

   # hostname
   asceds-extract "CLIENT_DOMAIN_NAME" "${REQFILE}"
   VARVAL="$( cat <<< ${VARVAL} | tr '[:upper:]' '[:lower:]' )"
   CLIENT_DOMAIN_NAME="${VARVAL//[^-a-z0-9.]/}"
   asceds-validate-fqdn "${CLIENT_DOMAIN_NAME}" || \
      asceds-error "Invalid client FQDN ${CLIENT_DOMAIN_NAME}"

   # request time
   asceds-extract "REQUEST_DATE" "${REQFILE}"
   REQUEST_DATE=${VARVAL//[^-_a-zA-Z0-9.:\/ ]/}      
      
   # requested by
   asceds-extract "REQUESTED_BY" "${REQFILE}"
   VARVAL="$( cat <<< ${VARVAL} | tr '[:upper:]' '[:lower:]' )"
   REQUESTED_BY=${VARVAL//[^-_a-zA-Z0-9.@]/}

   # change type
   asceds-extract "ACK_CHANGE_TYPE" "${REQFILE}"
   VARVAL="$( cat <<< ${VARVAL} | tr '[:upper:]' '[:lower:]' )"
   ACK_CHANGE_TYPE=${VARVAL//[^a-z]/}

}

##########################################################################################
# asceds-set-certbot-key
# sets the certbot --key-type option according to CLIENT_ENC in <hostname>_cert.conf
# adds option to CERTBOTOPTS

# usage: asceds-set-certbot-key
asceds-set-certbot-key()
{
   # detox CERTBOTOPTS
   CERTBOTOPTS=$( cat <<< "${CERTBOTOPTS}" | xargs)
   KEY_TYPE=$( cat <<< "${CERTBOTOPTS}" | xargs | \
               sed -E "s/.*--key-type\b +([a-z]+).*/\1/" )
   while [ "${CERTBOTOPTS}" != "${KEY_TYPE}" ] ; do
      CERTBOTOPTS=$( cat <<< "${CERTBOTOPTS}" | xargs | \
                    sed -e "s/--key-type ${KEY_TYPE}\b//" | xargs )
      CERTBOTOPTS=$( cat <<< "${CERTBOTOPTS}" | xargs)
      KEY_TYPE=$( cat <<< "${CERTBOTOPTS}" | xargs | \
                  sed -E "s/.*--key-type\b +([a-z]+).*/\1/" )
   done

   # if CLIENT_ENC is ecc or ecdsa, set key type to ecdsa
   # otherwise, default to rsa
   if [ "${CLIENT_ENC}" == 'ecc' ] || [ "${CLIENT_ENC}" == 'ecdsa' ] ; then
      CERTBOTOPTS="${CERTBOTOPTS} --key-type ecdsa"   
   else
      CERTBOTOPTS="${CERTBOTOPTS} --key-type rsa"
   fi
}

##########################################################################################
# builds fresh ${ASCEDSHOMEDIR}/cert.conf used by the cert manager
# to generate certificates for managed clients

# usage: asceds-build-certconf "noreturn"
asceds-build-certconf()
{
   if [ -r "${ASCEDSHOMEDIR}/cert.conf" ] ; then
      asceds-parse-certconf "${ASCEDSHOMEDIR}/cert.conf"
   fi

   # check with the user if NO_RETURN status changes
   if [ -n "${NO_RETURN}" ] && [ -z "$1" ] ; then
      echo
      echo '!!!!!!!!!!!!!!'
      echo "Changing NO_RETURN status from ro to automatic updates"
      echo -n "Is this correct? [Y/n] "
      read YE
      if [ "${YE}" = "n" ] || [ "${YE}" = "N" ] ; then
         NO_RETURN='yes'
      else
         NO_RETURN=''
      fi
   else 
      NO_RETURN="$1"
   fi

   # initialize cert.conf
   : > ${ASCEDSHOMEDIR}/cert.conf || \
       asceds-error "Cannot create cert.conf file ${ASCEDSHOMEDIR}/cert.conf"

   cat <<< "# don't return certificate to client?" >> ${ASCEDSHOMEDIR}/cert.conf
   if [ -z "${NO_RETURN}" ] ; then
      cat <<< "NO_RETURN=''" >> ${ASCEDSHOMEDIR}/cert.conf
   else
      cat <<< "NO_RETURN='yes'" >> ${ASCEDSHOMEDIR}/cert.conf   
   fi

   # generates DOMAIN_NAME 
   DOMAIN_NAME=$( hostname -f  | tr '[:upper:]' '[:lower:]' )
   cat <<< "" >> ${ASCEDSHOMEDIR}/cert.conf
   cat <<< "# fqdn name on the certificate" >> ${ASCEDSHOMEDIR}/cert.conf
   cat <<< "CLIENT_DOMAIN_NAME=${DOMAIN_NAME}" >> ${ASCEDSHOMEDIR}/cert.conf

   # parses SANS_LIST out of ASCEDS_OPENSSL (Ubuntu -> /etc/ssl/openssl.cnf)
   SANS_LIST=$( ${GREP} "^\s*DNS.[0-9]*\s*=" ${ASCEDS_OPENSSL} | sed -e "s/.*=\s*//" \
       | sed -e "s/${DOMAIN_NAME}//" | xargs | sed -e "s/ /,/g"  \
       | tr '[:upper:]' '[:lower:]' )
   cat <<< "" >> ${ASCEDSHOMEDIR}/cert.conf
   cat <<< "# SANS list of the certificate" >> ${ASCEDSHOMEDIR}/cert.conf
   cat <<< "CLIENT_SANS_LIST=${SANS_LIST}" >> ${ASCEDSHOMEDIR}/cert.conf

   # initialize other variables used by unmanaged clients 
   cat <<< "" >> ${ASCEDSHOMEDIR}/cert.conf
   cat <<< "# certificate id --> for unmanaged clients" >> ${ASCEDSHOMEDIR}/cert.conf
   cat <<< "ASCEDS_CERT_ID=''" >> ${ASCEDSHOMEDIR}/cert.conf

   cat <<< "" >> ${ASCEDSHOMEDIR}/cert.conf
   cat <<< "# should contain the email address of the requestor" >> ${ASCEDSHOMEDIR}/cert.conf
   cat <<< "# if not handled by asceds --> for notifications" >> ${ASCEDSHOMEDIR}/cert.conf
   cat <<< "NO_ASCEDS=''" >> ${ASCEDSHOMEDIR}/cert.conf

   cat <<< "" >> ${ASCEDSHOMEDIR}/cert.conf
   cat <<< "# client platform" >> ${ASCEDSHOMEDIR}/cert.conf
   if [ -z "${ASCEDS_PLATFORM}" ] ; then 
      ASCEDS_PLATFORM='linux'
   fi
   cat <<< "CLIENT_PLATFORM='${ASCEDS_PLATFORM}'" >> ${ASCEDSHOMEDIR}/cert.conf

   cat <<< "" >> ${ASCEDSHOMEDIR}/cert.conf
   cat <<< "# client certificate encoding" >> ${ASCEDSHOMEDIR}/cert.conf
   if [ -z "${ASCEDS_ENC}" ] ; then 
      ASCEDS_ENC='rsa'
   fi
   cat <<< "CLIENT_ENC='${ASCEDS_ENC}'" >> ${ASCEDSHOMEDIR}/cert.conf

   # fix ownership
   chown asceds:asceds ${ASCEDSHOMEDIR}/cert.conf

}

##########################################################################################
# parses and filters certificate manager provided asceds-site.conf,
# creates local ${ASCEDSETCDIR}/asceds-site.conf

# Usage: asceds-parse-siteconf "${ASCEDSHOMEDIR}/asceds-site.conf"
asceds-parse-siteconf()
{

   local REQFILE="$1"
   if [ ! -r "${REQFILE}" ] ; then
      asceds-error "Site config file ${REQFILE} unreadable."
   fi 

   cat <<< "# This is automately generated by asceds-asceds-update-siteconfig -c" > \
           ${ASCEDSETCDIR}/asceds-site.conf
   cat <<< "#  based on the asceds-site file from ${ASCEDSCRTMGR}" >> \
           ${ASCEDSETCDIR}/asceds-site.conf

   # ALARM
   asceds-extract "ALARM" "${REQFILE}"
   VARVAL="$( cat <<< ${VARVAL} | tr '[:upper:]' '[:lower:]' )"
   ALARM="${VARVAL//[^-_a-z0-9.@]/}"
   cat <<< "ALARM='${ALARM}'" > ${ASCEDSETCDIR}/asceds-site.conf   

   # certificate manager hostname
   asceds-extract "ASCEDSCRTMGR" "${REQFILE}"
   VARVAL="$( cat <<< ${VARVAL} | tr '[:upper:]' '[:lower:]' )"
   ASCEDSCRTMGR="${VARVAL//[^-a-z0-9.]/}"
   if [ -n "${ASCEDSCRTMGR}" ] ; then
      cat <<< "ASCEDSCRTMGR='${ASCEDSCRTMGR}'" >> ${ASCEDSETCDIR}/asceds-site.conf
   else
      asceds-error "Empty ASCEDSCRTMGR in retrieved site config file"
   fi
   asceds-validate-fqdn "${ASCEDSCRTMGR}" || \
      asceds-error "Invalid certificate manager FQDN ${ASCEDSCRTMGR}"

   # wget
   asceds-extract "WGET" "${REQFILE}"
   WGET="${VARVAL//[^-a-zA-Z0-9.\/ ]/}"
   if [ -n "${WGET}" ] ; then
      cat <<< "WGET='${WGET}'" >> ${ASCEDSETCDIR}/asceds-site.conf
   else
      asceds-error "Empty WGET in retrieved site config file"
   fi

   # asceds ssh public key
   asceds-extract "ASCEDSSSHPUBKEY" "${REQFILE}"
   ASCEDSSSHPUBKEY="${VARVAL//[^-+@a-zA-Z0-9=.\/ ]/}"
   if [ -n "${ASCEDSSSHPUBKEY}" ] ; then
      cat <<< "ASCEDSSSHPUBKEY='${ASCEDSSSHPUBKEY}'" >> ${ASCEDSETCDIR}/asceds-site.conf
   else
      asceds-error "Empty ASCEDSSSHPUBKEY in retrieved site config file"
   fi

   # list of encryption algorithms (rsa,ecc)
   asceds-extract "ASCEDSENCLIST" "${REQFILE}"
   VARVAL=$( cat <<< ${VARVAL} | tr '[:upper:]' '[:lower:]' | \
            sed -e "s/,/ /g" | xargs | sed -e "s/\s/,/g" )
   ASCEDSENCLIST="${VARVAL//[^-_a-z0-9.,]/}"
   if [ -n "${ASCEDSENCLIST}" ] ; then
      cat <<< "ASCEDSENCLIST='${ASCEDSENCLIST}'" >> ${ASCEDSETCDIR}/asceds-site.conf
   else
      cat <<< "ASCEDSENCLIST='rsa'" >> ${ASCEDSETCDIR}/asceds-site.conf
   fi
}

##########################################################################################
# creates/fixes a missing/incomplete ~asceds/${client_name}_cert.conf

# Usage: asceds-fix-client-certconf "${CLIENT}"
asceds-fix-client-certconf()
{

   local CLIENT_NAME="$1"
   CERT_FILE="/etc/letsencrypt/live/${CLIENT_NAME}/cert.pem"   

   if [ -z "${CLIENT_NAME}" ] ; then
      asceds-error "No client_name passed to asceds-fix-client-certconf"
   elif [ ! -d "/etc/letsencrypt/live/${CLIENT_NAME}" ] ; then
      asceds-error "${CLIENT_NAME} is not a client in /etc/letsencrypt/live/"
   elif [ ! -r "${CERT_FILE}" ] ; then
      asceds-error "No cert.pem for this client for extracting info"
   fi

   # check for missing fields
   NORETTEST=''
   DOMNAMETEST=''
   SANSTEST=''
   CERTIDTEST=''
   NOASCEDSTEST=''
   ENCTEST=''
   PLATFORMTEST=''
   if [ -r "${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf" ] ; then
      # check if NO_RETURN is defined
      NORETTEST=$( ${GREP} "\bNO_RETURN=" ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf )
      CERTIDTEST=$( ${GREP} "\bASCEDS_CERT_ID=" ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf )
      NOASCEDSTEST=$( ${GREP} "\bNO_ASCEDS=" ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf )
      DOMNAMETEST=$( ${GREP} "\bCLIENT_DOMAIN_NAME=" ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf )
      SANSTEST=$( ${GREP} "\bCLIENT_SANS_LIST=" ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf )
      ENCTEST=$( ${GREP} "\bCLIENT_ENC=" ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf )
      PLATFORMTEST=$( ${GREP} "\bCLIENT_PLATFORM=" ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf )
   fi

   # if CLIENT_ENC not defined, add it and assume rsa
   if [ -z "${ENCTEST}" ] ; then
      cat <<< "" >> ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
      cat <<< "# client certificate encoding" >> ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
      cat <<< "CLIENT_ENC='rsa'" >> ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
   fi

   # if CLIENT_PLATFORM not defined, add it and assume unknown, fix later
   if [ -z "${PLATFORMTEST}" ] ; then
      cat <<< "" >> ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
      cat <<< "# client platform" >> ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
      cat <<< "CLIENT_PLATFORM='unknown'" >> ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
   fi

   # if NO_RETURN not defined, add it and assume no return
   # for clients with read only filesystems or no ASCEDS installed
   if [ -z "${NORETTEST}" ] ; then
      cat <<< "" >> ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
      cat <<< "# don't return certificate to client?" >> \
                        ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
      cat <<< "NO_RETURN='yes'" >> ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
   fi

   # if NO_ASCEDS not defined, add it and assume managed client
   # for unmanaged clients, keys only
   if [ -z "${NOASCEDSTEST}" ] ; then
      cat <<< "" >> ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
      cat <<< "# unmanaged client: should contain the email address " >> \
              ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
      cat <<< "# of the requestor for notifications" >> \
              ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
      cat <<< "NO_ASCEDS=''" >> ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
   fi

   # if CLIENT_DOMAIN_NAME is missing, add it
   if [ -z "${DOMNAMETEST}" ] ; then
      cat <<< "" >> ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
      cat <<< "# client hostname" >> ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
      cat <<< "CLIENT_DOMAIN_NAME='${CLIENT_NAME}'" >> \
               ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
   fi

   # extract SANS list from the certificate
   CERT_SANS_LIST=$( eval ${OPENSSLEXT} < ${CERT_FILE} | \
      ${GREP} -v "Subject Alternative Name" | sed -e "s/DNS://g" | \
      sed -e "s/\b${CLIENT_NAME}\b//g" | sed -e "s/\s*//g" | \
      tr , "\n" | sort | tr "\n" , | sed -e "s/,$//" |  \
      sed -e "s/^,//" | tr '[:upper:]' '[:lower:]' )

   # if CLIENT_SANS_LIST is missing, add it
   if [ -z "${SANSTEST}" ] ; then
      cat <<< "" >> ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
      cat <<< "# client SANS list" >> ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
      cat <<< "CLIENT_SANS_LIST='${CERT_SANS_LIST}'" >> \
              ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
   fi

   # if ASCEDS_CERT_ID not defined, add it and assume managed client
   if [ -z "${CERTIDTEST}" ] ; then
      cat <<< "" >> ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
      cat <<< "# certificate id --> for unmanaged clients " >> \
              ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
      cat <<< "ASCEDS_CERT_ID=''" >> ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf
   fi

   # fix ownership
   chown asceds:asceds ${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf

   # checks for ${CLIENT_NAME}_cert.conf
   asceds-parse-certconf "${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf"

   # compare DOMAIN_NAME
   if [ "${CLIENT_NAME}" != "${CLIENT_DOMAIN_NAME}" ] ; then
      asceds-warning "Domain name mismatch for ${CLIENT_NAME} in the cert.conf file"
   fi
   # compare SANS
   SANS_LIST0=$( echo ${CLIENT_SANS_LIST} | tr , "\n" | \
      sort | tr "\n" , | sed -e "s/,$//" | tr '[:upper:]' '[:lower:]' )
   if [ "${CERT_SANS_LIST}" != "${SANS_LIST0}" ] ; then 
      asceds-warning "SANS mismatch for ${CLIENT_NAME} in the cert.conf file"
   fi

   # fix client platform
   if [ "${CLIENT_PLATFORM}" == "unknown" ] && [ -z "${ASCEDS_CERT_ID}" ] ; then
      asceds-refresh-var "${ASCEDSHOMEDIR}/${CLIENT_NAME}_cert.conf" \
          'CLIENT_PLATFORM' 'linux'
   fi
}

##########################################################################################
# builds fresh or adjusts existing site file ${ASCEDSETCDIR}/asceds-site.conf
# which is distributed to the clients;
# checks the ssh site key;
# to be run by asceds-certmanager-setup when the certificate manager is initialized

# usage asceds-build-siteconf
asceds-build-siteconf()
{
   # initialize site file with the default values
   if [ ! -r "${ASCEDSETCDIR}/asceds-site.conf" ] ; then
      ${CP} ${ASCEDSCONFDIR}/asceds-site.conf.proto ${ASCEDSETCDIR}/asceds-site.conf
   fi
   asceds-source-etcconf "${ASCEDSETCDIR}" "${ASCEDSCONF}"
   #  # alarm email address
   #  ALARM=""
   #  # certificate manager hostname 
   #  ASCEDSCRTMGR=""
   #  # wget -> for self signed certificates on the certificate manager,
   #  # add `--no-check-certificate' to wget 
   #  WGET='/usr/bin/wget'
   #  # asceds ssh public key
   #  ASCEDSSSHPUBKEY=''
   #  # list of encryption algorithms (rsa,ecc)
   #  ASCEDSENCLIST="rsa"

   echo "Configuring the site values for the certificate manager:"
   echo "${ASCEDSETCDIR}/asceds-site.conf"
   echo "Values can be modified later by using asceds-update-siteconfig -s"

   SITECHANGE=''
   ALARM_ORIG="${ALARM}"
   ASCEDSCRTMGR_ORIG="${ASCEDSCRTMGR}"
   WGET_ORIG="${WGET}"
   ASCEDSSSHPUBKEY_ORIG="${ASCEDSSSHPUBKEY}"
   ASCEDSENCLIST_ORIG="${ASCEDSENCLIST}"

   # set ALARM email address
   YE=''
   echo
   echo "Set ALARM email address for sending important messages like" 
   echo "software malfunction or certificate expiration (empty = no emails)."
   echo "The current value is '${ALARM}'"
   echo -n "Do you want to change it? [y/N] "
   read YE
   if [ "${YE}" == "y" ] || [ "${YE}" == "Y" ] ; then
      read -e -p "ALARM = " -i "${ALARM}" ALARM1
      echo
      YES=''
      echo -n "Is '${ALARM1}' the right email address? [Y/n] "
      read YES
      if [ "${YES}" != "n" ] && [ "${YES}" != "N" ] ; then
         ALARM=$( cat <<< ${ALARM1} | tr '[:upper:]' '[:lower:]' )
         # Check format
         if [ "${ALARM}" != "${ALARM//[^-_a-z0-9.@]/}" ] ; then
            asceds-error "Malformed email address ${ALARM}"
         fi
         if [ "${ALARM}" != "${ALARM_ORIG}" ] ; then
            asceds-refresh-var "${ASCEDSETCDIR}/asceds-site.conf" \
                               "ALARM" "${ALARM}"
            SITECHANGE='y'
         fi
      else
         echo "Value of ALARM was kept to: ${ALARM} "
      fi
   fi
   echo

   YE=''
   echo "The certificate manager hostname is currently '${ASCEDSCRTMGR}'"
   # can change it only if called from asceds-certmanager-setup
   if [ -n "${CALLFROMCRTMGRST}" ] ; then
      if [ -n "${ASCEDSALTCRTMGR}" ] ; then
         ASCEDSCRTMGR="${ASCEDSALTCRTMGR}"
      else
         echo -n "Do you want to change it? [y/N] "
         read YE
         while [ "${YE}" == "y" ] || [ "${YE}" == "Y" ] ; do
            echo "Full hostname/alias of the certificate manager: "
            read -e -p "ASCEDSCRTMGR = " ASCEDSCRTMGR1
            echo
            YES=''
            echo -n "Is '${ASCEDSCRTMGR1}' the right certificate manager name? [Y/n] "
            read YES
            if [ "${YES}" != "n" ] && [ "${YES}" != "N" ] ; then
               # check format
               if [ "${ASCEDSCRTMGR1}" != "${ASCEDSCRTMGR1//[^-a-zA-Z0-9.]/}" ] ; then
                  asceds-error "Malformed hostname ${ASCEDSCRTMGR1} for certificate manager"
               fi    
               ASCEDSCRTMGR=$( cat <<< ${ASCEDSCRTMGR1} | tr '[:upper:]' '[:lower:]' )
               YE='N'
            fi
         done
      fi
      echo "   Changing to '${ASCEDSCRTMGR}'"
      asceds-validate-fqdn "${ASCEDSCRTMGR}" || \
         asceds-error "Invalid certificate manager FQDN ${ASCEDSCRTMGR}"

      # if a SAN
      if [ "${ASCEDSCRTMGR}" != "$( hostname -f | tr '[:upper:]' '[:lower:]' )" ] ; then
         # check if it resolves into the same IP address as localhost
         asceds-ip-resolve "$( hostname -f | tr '[:upper:]' '[:lower:]' )"
         LOCALHOST_IP="${ASCEDS_IP}"
         asceds-ip-resolve "${ASCEDSCRTMGR}"
         if [ "${ASCEDS_IP}" != "${LOCALHOST_IP}" ] ; then
            asceds-error "${ASCEDSCRTMGR} and $( hostname -f ) resolve into different IP addresses"
         fi

         # check if in /etc/hosts, postfix, openssl.cnf
         asceds-hosts-check "${ASCEDSCRTMGR}" || \
            asceds-hosts-add "${ASCEDSCRTMGR}" "${ASCEDS_IP}"
         asceds-postfix-check "${ASCEDSCRTMGR}" || asceds-postfix-add "${ASCEDSCRTMGR}"
         asceds-openssl-check "${ASCEDSCRTMGR}" || asceds-openssl-add "${ASCEDSCRTMGR}"
      fi

   else
      echo "Change allowed only through asceds-certmanager-setup."
   fi

   # catch all test
   if [ -z "${ASCEDSCRTMGR}" ] ; then 
      ASCEDSCRTMGR="$( hostname -f | tr '[:upper:]' '[:lower:]' )"
      echo "Setting cert manager name to ${ASCEDSCRTMGR}"
   fi
   # check ping
   asceds-test-ping "${ASCEDSCRTMGR}" || \
      asceds-error "Certificate manager ${ASCEDSCRTMGR} unknown or down"
   # update ASCEDSCRTMGR in ${ASCEDSETCDIR}/asceds-site.conf
   if [ "${ASCEDSCRTMGR_ORIG}" != "${ASCEDSCRTMGR}" ] ; then
      asceds-refresh-var "${ASCEDSETCDIR}/asceds-site.conf" \
                         "ASCEDSCRTMGR" "${ASCEDSCRTMGR}"   
      SITECHANGE='y'
   fi
   echo

   # wget option for the self signed certificate sites
   WGET=$( which wget )
   if [ -z "${WGET}" ] ; then
      asceds-error "No wget installed on this system."
   fi
   if [ -n "${SELFSIGNED}" ] ; then
      WGET="${WGET} --no-check-certificate"
   fi
   # Update WGET in ${ASCEDSETCDIR}/asceds-site.conf
   if [ "${WGET_ORIG}" != "${WGET}" ] ; then
      asceds-refresh-var "${ASCEDSETCDIR}/asceds-site.conf" \
                         "WGET" "${WGET}"
      SITECHANGE='y'
   fi

   # asceds ssh public key
   if [ -r "${ASCEDSHOMEDIR}/.ssh/id_rsa.pub" ] ; then
      ASCEDSSSHPUBKEY="$( cat ${ASCEDSHOMEDIR}/.ssh/id_rsa.pub )"
      if [ -n "${ASCEDSSSHPUBKEY}" ] ; then
         if [ "${ASCEDSSSHPUBKEY_ORIG}" != "${ASCEDSSSHPUBKEY}" ] ; then
            asceds-refresh-var "${ASCEDSETCDIR}/asceds-site.conf" \
                               "ASCEDSSSHPUBKEY" "${ASCEDSSSHPUBKEY}"
            SITECHANGE='y'
            asceds-warning 'Site ssh key changed!!!!!'
            asceds-warning "The new site config file cannot be pushed to managed clients."
            asceds-warning "Each managed client should run asceds-update-siteconfig -c \
                            by hand to refresh asceds-site.conf."
         fi
      else
         asceds-error "Empty ssh public key from ${ASCEDSHOMEDIR}/.ssh/id_rsa.pub"
      fi
   else
      asceds-error "No asceds ssh public key ${ASCEDSHOMEDIR}/.ssh/id_rsa.pub"
   fi

   # set list of encryption algorithms (rsa,ecc)
   YE=''
   echo
   echo "Set the list of encryption algorithms provided by the CA."
   echo "The current value is '${ASCEDSENCLIST}'"
   echo -n "Do you want to change it? [y/N] "
   read YE
   if [ "${YE}" == "y" ] || [ "${YE}" == "Y" ] ; then
      echo "Please type the list (e.g. rsa,ecc):"
      read -e -p "ASCEDSENCLIST = " -i 'rsa' ASCEDSENCLIST1
      echo
      YES=''
      echo -n "Is '${ASCEDSENCLIST1}' the right list of encryption algorithms? [Y/n] "
      read YES
      if [ "${YES}" != "n" ] && [ "${YES}" != "N" ] ; then
         ASCEDSENCLIST=$( cat <<< ${ASCEDSENCLIST1} | tr '[:upper:]' '[:lower:]' | \
            sed -e "s/,/ /g" | xargs | sed -e "s/\s/,/g" )
         # Check format
         if [ -z "${ASCEDSENCLIST}" ] ; then
            ASCEDSENCLIST='rsa'
         fi
         if [ "${ASCEDSENCLIST}" != "${ASCEDSENCLIST//[^-_a-z0-9.,]/}" ] ; then
            asceds-error "Malformed list ${ASCEDSENCLIST}"
         fi
         if [ "${ASCEDSENCLIST}" != "${ASCEDSENCLIST_ORIG}" ] ; then
            asceds-refresh-var "${ASCEDSETCDIR}/asceds-site.conf" \
                               "ASCEDSENCLIST" "${ASCEDSENCLIST}"
            SITECHANGE='y'
         fi
      else
         echo "Value of ASCEDSENCLIST was kept to: ${ASCEDSENCLIST} "
      fi
   fi
   echo

   # refresh symlink to make site config file available by wget to clients
   ${RM} -f ${ASCEDSWEBDIR}/html/public/asceds-site.conf > /dev/null
   ln -s ${ASCEDSETCDIR}/asceds-site.conf ${ASCEDSWEBDIR}/html/public/

   # add the new version of the site config file to all existing managed clients
   # add the site config trigger to all managed clients except cert manager itself
   if [ -n "${SITECHANGE}" ] ; then
      # if any client configured
      if [ "$( echo ${ASCEDSHOMEDIR}/*_cert.conf )" != \
            "${ASCEDSHOMEDIR}/*_cert.conf" ] ; then
         # find hostname of client
         for HSTCONF in ${ASCEDSHOMEDIR}/*_cert.conf ; do
            if [ -r "${HSTCONF}" ] ; then
               HST=$( basename ${HSTCONF} )
               HST=${HST%%_*}
               if [ ! -d "${ASCEDSHOMEDIR}/${HST}" ] ; then
                  mkdir -p ${ASCEDSHOMEDIR}/${HST}
               fi
               # copy the asceds site file
               ${CP} -f ${ASCEDSETCDIR}/asceds-site.conf ${ASCEDSHOMEDIR}/${HST}/
               if [ "${HST}" != "${ASCEDSCRTMGR}" ] && \
                  [ "${HST}" != "$( hostname -f | tr '[:upper:]' '[:lower:]' )" ] ; then 
                  # if client is not the cert manager
                  . ${HSTCONF}
                  if [ -z "${NO_ASCEDS}" ] ; then
                     # new site config file trigger
                     touch ${ASCEDSHOMEDIR}/${HST}/.newsiteconf
                  fi
               else
                  # on the cert manager copy directly in ${ASCEDSHOMEDIR}
                  ${CP} -f ${ASCEDSETCDIR}/asceds-site.conf \
                              ${ASCEDSHOMEDIR}/
                  chown asceds:asceds ${ASCEDSHOMEDIR}/asceds-site.conf
               fi
               chown -R asceds:asceds ${ASCEDSHOMEDIR}/${HST}
            else
               asceds-error "Client configuration file ${HSTCONF} not readable." 
            fi
         done
      fi
   fi
}

#########################################################################################
# asceds-build-hostconf
# builds ${ASCEDSETCDIR}/asceds-hostconf.conf

# usage: asceds-build-hostconf
asceds-build-hostconf()
{
   # initialize
   if [ -r "${ASCEDSETCDIR}/asceds-hostconf.conf" ] ; then
      ${RM} -f ${ASCEDSETCDIR}/asceds-hostconf.conf
   fi

   # desired certificate encoding ASCEDS_ENC=rsa/ecc
   ASCEDSENCLIST0="$( cat <<< ${ASCEDSENCLIST//,/ } | xargs )"
   YE=''
   while [ "${YE}" != "y" ] && [ "${YE}" != "Y" ] ; do
      echo "Available certificate encodings: ${ASCEDSENCLIST0}"
      if [ -z "${ASCEDS_ENC}" ] ; then
         # if unspecified, should be the first element of the list
         ASCEDS_ENC=${ASCEDSENCLIST0/ */}
      fi
      # if ${ASCEDS_ENC} is in the list
      if [[ " ${ASCEDSENCLIST0} " =~ " ${ASCEDS_ENC} " ]] ; then
         echo -n "Is ${ASCEDS_ENC} the desired certificate encoding? [Y/n] "
         read YE
         if [ "${YE}" != "n" ] && [ "${YE}" != "N" ] ; then
            YE='y'
         else
            echo "Please pick one certificate encoding from the list above:"
            read -e -p "ASCEDS_ENC = " ASCEDS_ENC0
            echo
            if [ -n "${ASCEDS_ENC0}" ] ; then   
               ASCEDS_ENC0=${ASCEDS_ENC0//[^-a-zA-Z0-9]/}
               ASCEDS_ENC="${ASCEDS_ENC0}"
            fi
            YE=''
         fi
      else
         YE=''
      fi
   done
   asceds-refresh-var "${ASCEDSETCDIR}/asceds-hostconf.conf" \
                         "ASCEDS_ENC" "${ASCEDS_ENC}"

}

##########################################################################################
# issue info message; don't exit

# usage: asceds-echo "message"
asceds-echo()
{
   local WDATE="$( date +'%Y-%m-%d-%H-%M' ) "
   echo "${WDATE}Info ${SNAME}: $*"
}

##########################################################################################
# issue warning message; don't exit

# usage: asceds-warning "message"
asceds-warning()
{
   local WDATE="$( date +'%Y-%m-%d-%H-%M' ) "
   echo "${WDATE}Warning ${SNAME}:${FUNCNAME[@]::${#FUNCNAME[@]}-1}: $*"
}

##########################################################################################
# issue error message; exit 1

# usage: asceds-error "message"
asceds-error()
{
   local WDATE="$( date +'%Y-%m-%d-%H-%M' ) "
   echo "${WDATE}Error ${SNAME}:${FUNCNAME[@]::${#FUNCNAME[@]}-1}: $* ... Exiting!"

   # switch filesystem to rw state for pikvm systems
   if [ -x /usr/bin/ro ] ; then 
      sudo /usr/bin/ro 
   fi

   exit 1   
} 

##########################################################################################
# greeting messages issued by scripts

asceds-greetings()
{
   ASCEDSDATE=$( date +'%Y-%m-%d-%H-%M' )
   asceds-echo "Executing $@ version ${ASCEDSVER} on $( hostname -f )"
}

##########################################################################################
# send mail to $ALARM containing last 20 lines from logfile

# usage: asceds-mailto-alarm "message" "logfile"
asceds-mailto-alarm()
{
   local LOGFILE="$2"
   local SUBJECT="$1"

   if [ -z "${SUBJECT}" ] ; then
      asceds-warning "No subject defined. Using: ASCEDS error on $( hostname -f )"
      SUBJECT="ASCEDS error on $( hostname -f )"
   fi 
   
   if [ ! -r "${LOGFILE}" ] ; then
      asceds-warning "Logfile ${LOGFILE} does not exist or is not a regular file. Using /dev/null"
      LOGFILE="/dev/null"
   fi

   if [ -z "${ALARM}" ] || [ -z "${ALARMSEND}" ] ; then
      asceds-warning "Empty ALARM address or no mailer installed: no email will be sent"
   else
      # trim file and send email
      cat ${LOGFILE} | tail -n 20 | ${ALARMSEND} -s "$1" ${ALARM}
   fi
}

##########################################################################################
# check if running as root; exit otherwise

asceds-run-asroot()
{
   if [ $( id -u ) -ne 0 ] ; then
      asceds-mailto-alarm "Asceds warning: script ${SNAME} is not running as root on $( hostname -f )"  
      asceds-error 'This script needs to be run by root!!'
    fi
}

##########################################################################################
# check if running as asceds; exit otherwise

asceds-run-asasceds()
{  
   if [ "$( id -un )" != "asceds" ] ; then
      asceds-mailto-alarm "Asceds warning: script ${SNAME} is not running as asceds on $( hostname -f )"
      asceds-error 'This script needs to be run by asceds!!'
    fi
} 

##########################################################################################
# check if log dir and log files exist, create otherwise

asceds-logdir-check()
{
   # check log dir
   if [ ! -d "${ASCEDSLOGDIR}" ] ; then 
      if ! mkdir -p ${ASCEDSLOGDIR} ; then
         asceds-mailto-alarm "ASCEDS warning: cannot create log dir on $( hostname -f )"
         asceds-warning "Cannot create log dir ${ASCEDSLOGDIR}"
      else
         chown -R asceds:asceds ${ASCEDSLOGDIR}
      fi
   fi
   
   # if the log dir was created succesfully
   if [ -d "${ASCEDSLOGDIR}" ] ; then
      # check log file
      if [ -z "${ASCEDSLOGF}" ] ; then
         # may run in non-interactive mode; prepare log file
         ASCEDSLOGF=${ASCEDSLOGDIR}/${SNAME}.log
         if [ ! -e "${ASCEDSLOGF}" ] ; then
            :>${ASCEDSLOGF}
            chown asceds:asceds ${ASCEDSLOGF}
         fi
      fi

      # check error file
      if [ -z "${ASCEDSERRF}" ] ; then
         # prepare error file
         ASCEDSERRF=${ASCEDSLOGDIR}/${SNAME}.error
         if [ ! -e "${ASCEDSERRF}" ] ; then
            :>${ASCEDSERRF}
            chown asceds:asceds ${ASCEDSERRF}
         fi
      fi
   fi
}

##########################################################################################
# execute scripts in non-interactive mode -- tee stdout and stderr
# to ${ASCEDSLOGF} and ${ASCEDSERRF}
# solution using named pipes

asceds-exec-noninteractive()
{
   ASCEDSDATE=$( date +'%Y-%m-%d-%H-%M' )
   cat <<< "Executing ${SPATH}/${SNAME} $@ on $( hostname -f ) at ${ASCEDSDATE}" >> ${ASCEDSERRF}
   NPIPEOUT=/tmp/${SNAME}$$.out
   NPIPEERR=/tmp/${SNAME}$$.err
   trap "${RM} -f ${NPIPEOUT}" EXIT
   trap "${RM} -f ${NPIPEERR}" EXIT
   mknod ${NPIPEOUT} p
   mknod ${NPIPEERR} p
   tee -ia <${NPIPEOUT} ${ASCEDSLOGF} &
   # stderr not colorized - interleaving with stdout still not atomic
   ## tee -ia <${NPIPEERR} ${ASCEDSERRF} &
   # stderr is colorized/delayed
   tee -ia <${NPIPEERR} ${ASCEDSERRF} \
      |  while read line ; do echo -e "\e[01;31m$line\e[0m" >&2 ; done &
   exec 2>&- 1>&-
   exec 2>${NPIPEERR} 1>${NPIPEOUT}
}

##########################################################################################
# asceds-refresh-var "conffile" "varname" "newvalue"
# refresh/set variable "varname" with new value "newvalue" in file "conffile";
# if variable "varname" is not in file "conffile", a new assignment is added;
# if file "conffile" doesn't exist, it is created;
# the old value is stored to VARVAL_ORIG;
# if the new value is the same with the old one, nothing is changed;

# usage ex: asceds-refresh-var "${TOBVOLDIR}/${NEWVOL}.conf" 'PARENTVOL' "${PARENTVOL}"
asceds-refresh-var()
{

   local CONFFILE=$1
   local VARNAME=$2
   local NEWVALUE=$3

   # if file doesn't exist, create it
   if [ -e "${CONFFILE}" ] ; then
      if [ ! -f "${CONFFILE}" ] ; then
         asceds-error "File ${CONFFILE} is not a regular file"
      fi
   else
      cat <<< '' > ${CONFFILE} || \
           asceds-error "Cannot create file ${CONFFILE}"
   fi

   if [ -z "${VARNAME}" ] ; then
      asceds-error "Empty variable name ${VARNAME}"
   fi

   if [ -z "$( ${GREP} ^${VARNAME}= ${CONFFILE} )" ] ; then
     asceds-echo "No variable name ${VARNAME} in ${CONFFILE}; adding variable and setting to ${NEWVALUE}"
     cat <<< '' >> ${CONFFILE} || \
           asceds-error "Cannot add content to file ${CONFFILE}"
     cat <<< "# variable added by asceds-refresh-var on ${ASCEDSDATE}" >> ${CONFFILE}
     cat <<< "${VARNAME}=\"${NEWVALUE}\"" >> ${CONFFILE}
   else
      # find the current value of variable
      asceds-extract "${VARNAME}" "${CONFFILE}"
      VARVAL_OLD="${VARVAL}"
      if [ "${NEWVALUE}" != "${VARVAL_OLD}" ] ; then
         asceds-echo "Changing value of variable ${VARNAME} from ${VARVAL_OLD} \
                      to ${NEWVALUE} in file ${CONFFILE}"
         sed -i -e '\%\b'"${VARNAME}"'=%c\\'"${VARNAME}"'=\"'"${NEWVALUE}"'\"' ${CONFFILE} || \
              asceds-error "Cannot modify file ${CONFFILE}"
      else
         asceds-echo "New value for ${VARNAME} is the same. Not changing."
      fi 
   fi
}

##########################################################################################
# asceds-expiration-test "certfile"
# tests if a certificate will expire in less than 
# 5 days(stale) or 2 days (almost expired);  
#    returns "2"=almost expired/mail to ALARM  "1"=stale, "0"=is not expiring

# usage ex: asceds-expiration-test "${CERTFILE}" || bad-cert-action
asceds-expiration-test()
{
   local CERTFILE=$1

   if [ ! -f "${CERTFILE}" ] ; then
      asceds-warning "No certificate ${CERTFILE}"
      return 1
   fi

   local TIMENOW=$( date +"%s" )
   # move forward 5 days
   local TIMENOW5=$( expr ${TIMENOW} + 432000 )
   # move forward 2 days
   local TIMENOW2=$( expr ${TIMENOW} + 172800 )
   local CERTEXPLINE=$( openssl x509 -dates -noout < ${CERTFILE} | ${GREP} "notAfter=" )
   CERTEXPLINE=${CERTEXPLINE//notAfter=/} 
   local TIMEEXP=$( date --date="${CERTEXPLINE}" +"%s" )

   if [ "${TIMENOW2}" -gt "${TIMEEXP}" ] ; then
      asceds-mailto-alarm "Certificate in ${CERTFILE} on $( hostname -f ) is (almost) expired" \
                                      "${ASCEDSLOGF}"
      return 2
   elif [ "${TIMENOW5}" -gt "${TIMEEXP}" ] ; then
      asceds-warning "Certificate ${CERTFILE} is stale"
      return 1
   else
      return 0
   fi
}

##########################################################################################
# asceds-cert-freshtest "certfile" 
# tests if a certificate was just created; 
#    returns "1" if old or if it doesn't exist, "0" if just created

# usage ex: asceds-cert-freshtest "${CERTFILE}" || old-cert-action
asceds-cert-freshtest()
{
   local CERTFILE=$1

   if [ ! -f "${CERTFILE}" ] ; then
         asceds-warning "No certificate ${CERTFILE}"
         return 1
   fi

   local TIMENOW=$( date +'%Y-%m-%d' )
   local TIMEYEST=$( date --date="yesterday" +'%Y-%m-%d' )
   local CERTVALLINE=$( openssl x509 -dates -noout < ${CERTFILE} | ${GREP} "notBefore=" )
   CERTVALLINE=${CERTVALLINE//notBefore=/} 
   local TIMEVAL=$( date --date="${CERTVALLINE}" +'%Y-%m-%d' )

   if [ "${TIMENOW}" != "${TIMEVAL}" ] && [ "${TIMEYEST}" != "${TIMEVAL}" ] ; then
      asceds-warning "Certificate ${CERTFILE} is not fresh"
      return 1
   else
      return 0
   fi
}

##########################################################################################
# asceds-check-domains "domainname" "sanslist" 
# checks if cert manager is authorized to generate requested certificates

# usage ex: asceds-check-domains "${DOMAIN_NAME}" "${SANS_LIST}"
asceds-check-domains()
{
   local DOM="$1"
   local SANS="$2"

   # basic checks on character set
   if [ -z "${DOM}" ] || [[ "${DOM}" = "-"* ]] || \
      [ "${DOM}" != "${DOM//[^-a-zA-Z0-9.]/}" ] ; then 
      asceds-error "Hostname ${DOM} missing or contains special characters.
         Only -. a-z A-Z 0-9 are allowed ... Exiting" 
   fi

   if [ -z "${SANS}" ] ; then
      asceds-echo 'No alternate names will be used !!!'
   elif [ "${SANS}" != "${SANS//[^-a-zA-Z0-9.,]/}" ] ; then 
      asceds-error "SANS list ${SANS} contains special characters.
         Only -., a-z A-Z 0-9 are allowed. Exiting" 
   fi

   # check if domain name and hosts in sans list match authorized domains
   # in AUTHDOMAIN defined in asceds-certbot.conf 
   if [ -n "${AUTHDOMAIN}" ] ; then
      # if AUTHDOMAIN is empty, no checks
      
      # transform commas into spaces
      AUTHL=${AUTHDOMAIN//,/ }
      SANSL="${DOM} ${SANS//,/ }"
      for HST in ${SANSL} ; do
         asceds-validate-fqdn "${HST}" || \
            asceds-error "Invalid FQDN ${HST} in SANs list."
         VAL=''
         for AU in ${AUTHL} ; do
            # if ${HST} is ${AU} and doesn't start with a dot, set VAL to yes
            if [ "${AU}" = "${HST}" ] && [[ "${HST}" != .* ]] ; then
               VAL="yes"
            fi
            # if ${HST} ends in .${AU}, set VAL to yes
            if [[ "${HST}" = *."${AU}" ]] ; then
               VAL="yes"
            fi
            # if ${AU} starts with a dot, and  if ${HST} ends in ${AU}, set VAL to yes
             if [[ "${HST}" = *"${AU}" ]] && [[ "${AU}" = ."${AU}" ]] ; then
               VAL="yes"
            fi
         done
         if [ -z "${VAL}" ] ; then
            asceds-error "Domain name of ${HST} is not authorized to manage certificates"
         fi
      done
      asceds-echo "List ${SANSL} authorized" 
   fi 

}

##########################################################################################
# asceds-ip-resolve "hostname"
# finds the IP address associated with <hostname> as provided by DNS 
# or by user input, loaded in the variable ASCEDS_IP and returns 0;
# if <hostname> cannot be resolved or IP invalid,
# returns 1 and sets ASCEDS_IP=10.100.100.100 

# usage: asceds-ip-resolve "<hostname>"
asceds-ip-resolve()
{
   local HSTNAME="$1"

   if [ -z "${HSTNAME}" ] ; then
      asceds-error "Empty hostname"
   fi

   TESTHOSTNAME='y'
   ${PING} -n -q -c 1 ${HSTNAME} || TESTHOSTNAME='' > /dev/null 

   if [ -n "${TESTHOSTNAME}" ] ; then
      ASCEDS_IP=$( ${PING} -n -q -c 1 ${HSTNAME} | head -n 1 | \
                   sed -e "s/).*$//" | sed -e "s/^.*(//" )
      # test format
      BAD_IP=''
      asceds-validate-ip "${ASCEDS_IP}" || BAD_IP='y'
      if [ -z "${BAD_IP}" ] ; then
         return 0
      else
         asceds-warning "Resolved IP address ${ASCEDS_IP} for host ${HSTNAME} is illegal."
         asceds-warning "Using 10.100.100.100"
         ASCEDS_IP="10.100.100.100"
         return 1 
      fi
   else
      asceds-warning "IP address of host ${HSTNAME} could not be found."
      YE='n'
      while [ "${YE}" == 'n' ] || [ "${YE}" == 'N' ] ; do
         ASCEDS_IP=''
         read -e -p "Please type the IP address of ${HSTNAME}: " ASCEDS_IP
         echo
         # filter input
         ASCEDS_IP=${ASCEDS_IP//[^0-9.]/} 
         echo -n "Is ${ASCEDS_IP} the IP address to be used for ${HSTNAME}? [Y/n] "
         read YE
      done

      # test format
      BAD_IP=''
      asceds-validate-ip "${ASCEDS_IP}" || BAD_IP='y'  

      if [ -z "${BAD_IP}" ] ; then
         return 0
      else
         asceds-warning "Introduced IP address ${ASCEDS_IP} for host ${HSTNAME} is illegal."
         asceds-warning "Using 10.100.100.100"
         ASCEDS_IP="10.100.100.100"
         return 1 
      fi
   fi
}

##########################################################################################
# asceds-test-ping "hostname"
# tests if hostname is online and accessible using ping;
# returns "0"=host exists and online, "1"=host exists but offline
#         "2"=hostname unknown 

# Usage: asceds-test-ping "<hostname>"   
asceds-test-ping()
{
   local TESTHOST="$1"
   if [ -z "${TESTHOST}" ] ; then
      asceds-error "No hostname to test with ping"
   fi

   # test access
   asceds-echo "Testing access to ${TESTHOST} with ping:
      This should take less than 5 seconds ..."
   TESTPING=$( ${PING} -w 4 ${TESTHOST} 2>&1 | tail -n 3 )

   # interpret results
   if [ -n "$( cat <<< ${TESTPING} | ${GREP} '100% packet loss' )" ] ; then
      asceds-warning "Host ${TESTHOST} exists but is not online"
      return 1
   elif  [ -n "$( cat <<< ${TESTPING} | ${GREP} 'not known' )" ] || \
         [ -n "$( echo ${TESTPING} | ${GREP} 'unknown host' )" ] ; then
      asceds-warning "Host ${TESTHOST} does not exist"
      return 2
   else
      asceds-echo "Host ${TESTHOST} is alive"
      return 0 
   fi
}

##########################################################################################
# asceds-test-remoterw "hostname"
# should work only on cert manager
# tests remote asceds user read/write access through scp; 
# returns "0"=read/write successful, "1"=read-only, "2"=no ssh access at all

# Usage: asceds-test-remoterw "<hostname>" 
asceds-test-remoterw()
{
   local TESTHOST="$1"
   if [ -z "${TESTHOST}" ] ; then
      asceds-error "No hostname to test with ssh/scp"
   fi

   # test access
   TESTSSH=''
   TESTSCP=''
   asceds-echo "Testing access to ${TESTHOST} with ssh/scp:
      This should take less than 5 seconds ..."
   # replace here with tests sudo 
   sudo -u asceds -i ${SSH} ${TESTHOST} echo || TESTSSH='fail'
   sudo -u asceds -i ${SCP} /etc/hostname ${TESTHOST}:certmanager \
        || TESTSCP='fail'

   # interpret results
   if [ -n "${TESTSSH}" ] ; then
      asceds-warning "Host ${TESTHOST} does not allow asceds ssh access"
      return 2
   elif  [ -n "${TESTSCP}" ] ; then
      asceds-warning "Host ${TESTHOST} does not allow write access inside ~asceds"
      return 1
   else
      asceds-echo "Host ${TESTHOST} is accessible for asceds"
      return 0 
   fi
}

##########################################################################################
# asceds-client-showconf  
# show status of the client as reflected by ~asceds/cert.conf and 
# and ${ASCEDSETCDIR}/asceds-site.conf

# Usage: asceds-client-showconf
asceds-client-showconf()
{
   if [ -r "${ASCEDSHOMEDIR}/certs/cert.pem" ] ; then
      echo
      if [ -n "${OPENSSLEXT}" ] ; then
         echo "Current certificate obtained through ASCEDS:"
         eval ${OPENSSLEXT} < ${ASCEDSHOMEDIR}/certs/cert.pem \
            | sed -e "s/^/   /"
      fi
      echo "   Cert Validity:" 
      openssl x509 -dates -noout < ${ASCEDSHOMEDIR}/certs/cert.pem | \
         sed -e "s/^/      /"
   else
      echo 
   fi

   if [ -n "${ASCEDSCRTMGR}" ] ; then
      echo "   Certificate manager: ${ASCEDSCRTMGR}"
   else 
      echo "   Certificate manager: UNDEFINED"
   fi

   if [ -n "${ALARM}" ] && [ -n "${ALARMSEND}" ] ; then
      echo "   Email for errors: ${ALARM}"
   else 
      echo "   Errors are not sent by email: "
      if [ -z "${ALARMSEND}" ] ; then
         echo "      No emailer installed;"
      fi
      if [ -z "${ALARM}" ] ; then
         echo "      No email address defined;"
      fi
   fi

   echo
   echo "Configuration of a new certificate"
   if [ -r "${ASCEDSHOMEDIR}/cert.conf" ] ; then
      asceds-parse-certconf "${ASCEDSHOMEDIR}/cert.conf"
      echo "   Domain name: ${CLIENT_DOMAIN_NAME}"
      echo "   SANS values: ${CLIENT_SANS_LIST}"
      if [ -n "${NO_RETURN}" ] && [ -z "${NO_ASCEDS}" ] && \
           [ -z "${ASCEDS_CERT_ID}" ] ; then
         echo "   Privately managed client"
      elif [ -z "${NO_RETURN}" ] && [ -z "${NO_ASCEDS}" ] && \
           [ -z "${ASCEDS_CERT_ID}" ] ; then
         echo "   Fully managed client"
      elif [ -n "${NO_RETURN}" ] && [ -n "${NO_ASCEDS}" ] && \
           [ -n "${ASCEDS_CERT_ID}" ] ; then
         echo "   Unmanaged client"
      else
         echo "   Inconsistent NO_RETURN, NO_ASCEDS, and ASCEDS_CERT_ID values"
         echo "   NO_RETURN=${NO_RETURN}"
         echo "   NO_ASCEDS=${NO_ASCEDS}"
         echo "   ASCEDS_CERT_ID=${ASCEDS_CERT_ID}"
      fi
      echo "   Certificate encoding: ${CLIENT_ENC}"
      echo "   Platform: ${CLIENT_PLATFORM}"
   else
      echo "   No certificate configuration file was found"
   fi
}

#########################################################################################
# asceds-configphp-get "file" "var"
# get value of variable <var> from php config file ${ASCEDSWEBDIR}/etc/<file>
# into ASCEDS_PHPVAL

# Usage: asceds-configphp-get "file" "var"
asceds-configphp-get()
{
   local PHPFILE="$1"
   local VARNAME="$2"
   ASCEDS_PHPVAL=''

   CONFIGPHP="${ASCEDSWEBDIR}/etc/${PHPFILE}"
   if [ ! -r "${CONFIGPHP}" ] ; then
     asceds-error "File ${CONFIGPHP} is not available"
   fi

   if [ -z "${VARNAME}" ] ; then
     asceds-error "Empty variable name to set."
   fi
   TESTVAR=$( ${GREP} "^\$${VARNAME}\s*=" ${CONFIGPHP} | tail -1 )
   if [ -z "${TESTVAR}" ] ; then
      asceds-warning "Variable \$${VARNAME} does not exist in ${CONFIGPHP}"
      return 1
   else
      asceds-echo "Loading value of variable ${VARNAME} from file ${CONFIGPHP}"
      ASCEDS_PHPVAL=$( cat <<< ${TESTVAR} | \
                       sed -e "s/^\s*\$${VARNAME}\s*=\s*'// ; s/'\s*;\s*$//" )
      return 0
   fi
}

##########################################################################################
# asceds-configphp-set "file" "var" "value"
# change values of variables in the php file ${ASCEDSWEBDIR}/etc/<file>

# Usage: asceds-configphp-set "config.php" "organization" "${WEBORGVAL}"
asceds-configphp-set()
{
   local PHPFILE="$1"
   local VARNAME="$2"
   local VARVAL="$3"

   CONFIGPHP="${ASCEDSWEBDIR}/etc/${PHPFILE}"
   if [ ! -r "${CONFIGPHP}" ] ; then
     asceds-error "File ${CONFIGPHP} is not available"
   fi

   if [ -z "${VARNAME}" ] ; then
     asceds-error "Empty variable name to set."
   fi
   TESTVAR=$( ${GREP} "^\$${VARNAME}\s*=" ${CONFIGPHP} )
   if [ -z "${TESTVAR}" ] ; then
      asceds-warning "Variable \$${VARNAME} does not exist in ${CONFIGPHP}"
      return 1
   else
      asceds-echo "Changing value of variable ${VARNAME} to ${VARVAL} in file ${CONFIGPHP}"
      sed -i -e "\%\$${VARNAME}\s*=\s*%c\\\$${VARNAME} = \'${VARVAL}\' ;" ${CONFIGPHP} || \
            asceds-error "Cannot modify file ${CONFIGPHP}"
      return 0
   fi
}

##########################################################################################
# asceds-find-bin "bin1" "bin2" ...
# FULLBIN = the path to the first name found the list; empty if none found

# Usage: asceds-find-bin "mail" "gnu-mail" "mailx"
asceds-find-bin()
{
   BINNAMES=$*
   if [ -z "${BINNAMES}" ] ; then
      asceds-error "No binary names to search for"
   fi

   for i in ${BINNAMES} ; do
      FULLBIN=$( which $i )
      if [ -n "${FULLBIN}" ] ; then
         asceds-echo "Found ${FULLBIN}"
         break
      fi
   done
   unset i
}

##########################################################################################
# asceds-comma-uniq "list"
# order a comma separated list and removes duplicates

# Usage: asceds-comma-uniq "AUTHDOMAIN"
asceds-comma-uniq()
{
   local CLISTNAME="$1"
   eval ${CLISTNAME}=$(  cat <<< ${!CLISTNAME} | tr ',' "\n" | \
      sort | uniq |  tr "\n" ',' | sed -e "s/,$//" | sed -e "s/^,//" )
}

########################################################################################
# asceds-refresh-webdomains
# propagate the AUTHDOMAIN values to /usr/share/asceds/etc/users.php
#

# Usage: asceds-refresh-webdomains
asceds-refresh-webdomains()
{
   if [ -d "${ASCEDSWEBDIR}/etc" ] ; then
      if [ -f "${ASCEDSWEBDIR}/etc/users.php" ] ; then
         ALLDOMLINE="\$allauthdomains = array("
         for DOM in $( cat <<< ${AUTHDOMAIN} | sed -e "s/,/ /g" ) ; do
            ALLDOMLINE="${ALLDOMLINE}\"${DOM}\", "
         done
         ALLDOMLINE=$( cat <<< "${ALLDOMLINE}" | sed -e "s/,\s*$/);/" )
         # echo $ALLDOMLINE
         sed -i -e "/allauthdomains\s*=\s*array\s*(/c ${ALLDOMLINE}" \
            ${ASCEDSWEBDIR}/etc/users.php
      else
         asceds-warning "No users file ${ASCEDSWEBDIR}/etc/users.php
           on this website"
      fi
   else
      asceds-warning "No dir ${ASCEDSWEBDIR}/etc on this website"
   fi

   if [ -d "${ASCEDSWEBDIR}/html/public" ] ; then
      :> ${ASCEDSWEBDIR}/html/public/.htaccess
      for DOM in $( cat <<< ${AUTHDOMAIN} | sed -e "s/,/ /g" ) ; do
         cat <<< "Require host .${DOM}" >> ${ASCEDSWEBDIR}/html/public/.htaccess
      done
   else
      asceds-warning "No dir ${ASCEDSWEBDIR}/html/public on this website"
   fi
}

########################################################################################
# asceds-check-netconf
# checks the network configuration of /etc/hosts, postfix and openssl.cnf 
# of the host; attempts to fix it

# Usage: asceds-check-netconf
asceds-check-netconf()
{
   local HSTNM="$( hostname -s | tr '[:upper:]' '[:lower:]' )"
   local FULLHSTNM="$( hostname -f | tr '[:upper:]' '[:lower:]' )"

   if [ "${HSTNM}" == "${FULLHSTNM}" ] ; then
      echo "No full hostname configured on this computer (missing domain)."
      YE=''
      while [ "${YE}" != "y" ] && [ "${YE}" != "Y" ] ; do
         read -e -p "Type fully qualified hostname: " -i "${HSTNM}" FULLHSTNM
         echo
         FULLHSTNM="$( cat <<< ${FULLHSTNM} | tr '[:upper:]' '[:lower:]' )"
         if [ "${FULLHSTNM}"  == "${FULLHSTNM//[^-a-z0-9.]/}" ] ; then
            echo -n "Is ${FULLHSTNM} the correct fully qualified hostname? [y/N] "
            read YE
         fi
      done
      asceds-validate-fqdn "${FULLHSTNM}" || \
         asceds-error "Invalid hostname FQDN ${FULLHSTNM}"

      # consistency test
      if [ "${HSTNM}" != "${FULLHSTNM%%.*}" ] ; then
         asceds-warning "The hostname part of the FQDN is different from hostname"
      fi
      
      # find IP address
      asceds-ip-resolve "${FULLHSTNM}" || \
         asceds-error "Full hostname ${FULLHSTNM} cannot be resolved."

      # add as the first line to /etc/hosts
      sed -i "1s/^/${ASCEDS_IP}\t${FULLHSTNM}\t${HSTNM}\n/" /etc/hosts
   fi   

   # fix postfix
   if [ -n "${ASCEDS_POSTFIX}" ] ; then 
      if [ ! -r "${ASCEDS_POSTFIX}" ] ; then
         asceds-refresh-var "${ASCEDSETCDIR}/asceds-hostconf.conf" "ASCEDS_POSTFIX" ''
         ASCEDS_POSTFIX=''
         asceds-echo "Postfix config file ${ASCEDS_POSTFIX} was not found \
                      on this computer."
         asceds-warning "Please set the variable ASCEDS_POSTFIX in \
                ${ASCEDSETCDIR}/asceds-hostconf.conf when postfix is installed."
      else
         asceds-postfix-check "${FULLHSTNM}" || asceds-postfix-add "${FULLHSTNM}"
      fi 
   fi

   # fix openssl.cnf
   if [ ! -r "${ASCEDS_OPENSSL}" ] ; then
      asceds-echo "OpenSSL config file ${ASCEDS_OPENSSL} not readable"
      ASCEDS_OPENSSL=''
      asceds-refresh-var "${ASCEDSETCDIR}/asceds-hostconf.conf" "ASCEDS_OPENSSL" ''
      asceds-error "Please set the variable ASCEDS_OPENSSL in \
                ${ASCEDSETCDIR}/asceds-hostconf.conf when/if OpenSSL is installed."
   else
      asceds-openssl-check "${FULLHSTNM}" || asceds-openssl-add "${FULLHSTNM}" 
   fi
}

########################################################################################
# asceds-expand-hostname "hostname"
# if short hostname is given, try to expand it using configured domains: 
# FQDN stored in ASCEDS_FQDN
# if replacement was performed, return 0, otherwise return 1

# Usage: asceds-expand-hostname "example"
asceds-expand-hostname()
{
   local HST_NAME="$1"

   # initial checks
   if [ -z "${HST_NAME}" ] ; then
      asceds-error "asceds-check-netconf called with empty argument."
   fi

   if [ "${HST_NAME//[^-a-zA-Z0-9.]/}" != "${HST_NAME}" ] ; then
      asceds-error "Invalid hostname ${HST_NAME}"
   else
      HST_NAME=$( cat <<< ${HST_NAME} | tr '[:upper:]' '[:lower:]' )
   fi

   # we have already FQDN, no replacement needed
   if [[ ${HST_NAME} == *.* ]] ; then
      ASCEDS_FQDN="${HST_NAME}"
      return 1
   fi

   ASCEDS_FQDN=''
   # find any _cert.conf files matching ${HST_NAME}
   if [ "$( echo ${ASCEDSHOMEDIR}/${HST_NAME}.*_cert.conf )" != \
        "${ASCEDSHOMEDIR}/${HST_NAME}.*_cert.conf" ] ; then
      # see how many matches
      if [ "$( echo ${ASCEDSHOMEDIR}/${HST_NAME}.*_cert.conf | wc -w )" \
           == '1' ] ; then
         # if one match only 
         ASCEDS_FQDN=$( basename ${ASCEDSHOMEDIR}/${HST_NAME}.*_cert.conf )
         ASCEDS_FQDN=${ASCEDS_FQDN%%_*}
      else
         asceds-warning "Multiple matches found for hostname ${HST_NAME}. Ambigous"
         # when in doubt -> unsuccessful
         return 1
      fi
   else
      asceds-echo "No matches found for hostname ${HST_NAME} in ${ASCEDSHOMEDIR}"
   fi

   # try to find fqdn from the output of hosts if it exists
   TESTHOSTS=$( which host 2>/dev/null )
   if [ -n "${TESTHOSTS}" ] && [ -z "${ASCEDS_FQDN}" ] ; then
      FAILT=''
      host -TtA ${HST_NAME} > /dev/null 2>&1  || FAILT="yes"
      if [ -z "${FAILT}" ] ; then
         ASCEDS_FQDN=$( host -TtA ${HST_NAME} | cut -d ' ' -f1 | \
                        tr '[:upper:]' '[:lower:]' )
      fi
   fi

   # final format test
   if [ -z "${ASCEDS_FQDN}" ] ; then
      asceds-validate-fqdn "${ASCEDS_FQDN}" || \
         asceds-error "Invalid expanded FQDN ${ASCEDS_FQDN}"
      asceds-echo "Short hostname ${HST_NAME} was expanded to ${ASCEDS_FQDN}"
      return 0
   else
      return 1
   fi
}

########################################################################################
# asceds-validate-ip "ip_address"
# validates a string as an IP address: 0/true=valid 1/false=invalid

# Usage: asceds-validate-ip "${ASCEDS_IP}"
asceds-validate-ip()
{
   local  HST_IP=$1
   local  RET=1

   if [[ ${HST_IP} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] ; then
      OIFS=$IFS
      IFS='.'
      HST_IP=(${HST_IP})
      IFS=$OIFS
      [[ ${HST_IP[0]} -le 255 && ${HST_IP[1]} -le 255 \
          && ${HST_IP[2]} -le 255 && ${HST_IP[3]} -le 255 ]]
      RET=$?
   fi
   return ${RET}
}

####################################################################################
# asceds-validate-fqdn "FQDN"
# validates a string as a FQDN: 0/true=valid 1/false=invalid

# Usage: asceds-validate-fqdn "${ASCEDSCRTMGR}"
asceds-validate-fqdn()
{
   local  HST_NAME=$1
   local  RET

   [[ ${HST_NAME} == *.* && ${HST_NAME} == ${HST_NAME//[^-a-zA-Z0-9.]/} ]] 
   RET=$?
   return ${RET}
}

####################################################################################
# asceds-find-mail
# check for mailer

# Usage: asceds-find-mail 
asceds-find-mail()
{
   asceds-find-bin "mail" "gnu-mail" "mailx"
   ALARMSEND="${FULLBIN}"
   asceds-refresh-var "${ASCEDSETCDIR}/asceds-local.conf" "ALARMSEND" "${ALARMSEND}"
}

####################################################################################
# asceds-find-certbot
# check for certbot; if not found, request certbot install

# Usage: asceds-find-certbot
asceds-find-certbot()
{
   asceds-find-bin "certbot"
   CERTBOTBIN="${FULLBIN}"
   if [ -z "${CERTBOTBIN}" ] ; then
      echo
      echo 'No binary found for certbot !!!!!!!!!!!!!!!!!!!!!!!!!'
      if [ -d "/etc/letsencrypt" ] && [ ! -r "/etc/letsencrypt/.fake" ] ; then
         echo 'Traces of an existing certbot install were found in /etc/letsencrypt'
         echo "Please fix it or install certbot and run again asceds-certmanager-setup"
         echo
         echo "   snap install --classic certbot"
         echo "   ln -s /snap/bin/certbot /usr/bin/certbot"
         echo 
         echo "If this is a migration from an existing certificate manager,"
         echo "please don't forget to copy /etc/letsencrypt" 
         exit 1
      else
         YE=''
         echo "Do you want to install a fake version of certbot" 
         echo '(for testing/development sites only!!!!)'
         echo -n "providing self-signed certificates? [Y/n] " 
         read YE
         if [ "${YE}" == 'n' ] || [ "${YE}" == 'N' ] ; then
            echo "Please clear /etc/letsencrypt, install certbot,"
            echo "and run again asceds-certmanager-setup"
            echo
            echo "   snap install --classic certbot"
            echo "   ln -s /snap/bin/certbot /usr/bin/certbot"
            echo 
            echo "If this is a migration from an existing certificate manager,"
            echo "please don't forget to copy /etc/letsencrypt" 
            exit 1
         fi
         echo "Installing fake certbot"
         # build the letsencrypt dir tree if it is not there
         if [ ! -d "/etc/letsencrypt" ] ; then
            mkdir -p /etc/letsencrypt/live /etc/letsencrypt/archive
            mkdir -p /etc/letsencrypt/renewal-hooks/pre
            mkdir -p /etc/letsencrypt/renewal-hooks/deploy
            mkdir -p /etc/letsencrypt/renewal-hooks/post
            mkdir -p /etc/letsencrypt/renewal
         fi

         # configure asceds-certbot.conf
         SELFSIGNED='yes'
         asceds-refresh-var "${ASCEDSETCDIR}/asceds-certbot.conf" \
            "SELFSIGNED" "${SELFSIGNED}"
         CERTBOTBIN="${ASCEDSHOME}/ss/certbot"
         asceds-refresh-var "${ASCEDSETCDIR}/asceds-certbot.conf" \
            "CERTBOTBIN" "${CERTBOTBIN}"

         # mark certbot dir as fake
         touch /etc/letsencrypt/.fake

         if [ -r "${ASCEDSETCDIR}/certbot-ss.conf" ] ; then
            . ${ASCEDSETCDIR}/certbot-ss.conf
         fi
         if [ -z "${CERTTERM}" ] ; then
            CERTTERM='3650'
         fi

         # set the self signed cert validity 
         # suggested 3650 for production, 10 for testing
         echo "Desired certificate expiration term in days:"
         echo "default is 3650 days = 10 years (forever)"
         echo "recommended for testing and development sites = 6-10"
         echo "This can be changed later in ${ASCEDSETCDIR}/certbot-ss.conf"
         echo -n "Enter an integer between 6 and 3650: [${CERTTERM}] "
         read -e -p "Enter an integer between 6 and 3650: [${CERTTERM}] " \
              -i "${CERTTERM}" CERTTERM0
         echo
         if [ -z "${CERTTERM0}" ] || \
            [ "${CERTTERM0}" != "${CERTTERM0//[^0-9]/}" ] ; then
            asceds-echo "Keeping the existing value of ${CERTTERM} days"
         elif [ "${CERTTERM0}" -gt 3650 ] ; then
            CERTTERM='3650'
         elif [ "${CERTTERM0}" -lt 6 ] ; then
            CERTTERM='6'
         else
            CERTTERM="${CERTTERM0}"
         fi
         echo
         asceds-refresh-var "${ASCEDSETCDIR}/certbot-ss.conf" \
            "CERTTERM" "${CERTTERM}"

         # create the refresh crontab
         if [ -d "/etc/cron.d" ] ; then
            CRONF="/etc/cron.d/asceds-selfsigned-refresh"
            if [ ! -r "${CRONF}" ] ; then
               asceds-echo "Creating cron file asceds-selfsigned-refresh"
               cat <<< "# refreshes self-signed certificates close to expiration" \
                        > ${CRONF}
               if [ -x "${ASCEDSHOME}/ss/certbot-selfsigned-refresh" ] ; then 
                  # run it every day at 20:43
                  cat <<< "43 20 * * * root ${ASCEDSHOME}/ss/certbot-selfsigned-refresh > /dev/null 2>&1" \
                      >> ${CRONF}
               fi
            fi
         else
            echo "This computer doesn't have cron installed."
            echo "To refresh certificates close to expiration,"
            echo "please run as root ${ASCEDSHOME}/ss/certbot-selfsigned-refresh"
         fi

         # set custom message
         if [ ! -r "${ASCEDSHOMEDIR}/custom.php" ] || \
            [ -z "$( ${GREP} 'self-signed certificate test site' \
                  ${ASCEDSHOMEDIR}/custom.php )" ] ; then
            echo 'This is a self-signed certificate test site.<br>' \
               >> "${ASCEDSHOMEDIR}/custom.php"
         fi
      fi 
   else 
      asceds-refresh-var "${ASCEDSETCDIR}/asceds-certbot.conf" \
         "CERTBOTBIN" "${CERTBOTBIN}"
   fi
}

####################################################################################
# asceds-config-certbot
# config certbot to work with the ACME server

# Usage: asceds-config-certbot
asceds-config-certbot()
{
   # certbot credentials
   #    ACMESERVER=
   #    EABKID=
   #    EABHMACKEY=
   #    AUTHDOMAIN=
   echo
   echo "Configuring certbot credentials"
   YE='n'
   while [ "${YE}" = 'n' ] || [ "${YE}" = 'N' ] ; do
      if [ -z "${SELFSIGNED}" ] ; then
         echo "Please introduce ACMESERVER value [default \"${ACMESERVER}\"] "
         read -e -p "ACMESERVER = " -i "${ACMESERVER}" ACMESERVER1
         echo
         if [ -z "${ACMESERVER1}" ] ; then
            ACMESERVER1="${ACMESERVER}"
         # accept a hostname
         # elif [ "${ACMESERVER1}" != "${ACMESERVER1//[^-_a-zA-Z0-9.:\/]/}" ] ; then
         # accept an URL
         elif [ "${ACMESERVER1}" != "${ACMESERVER1//[^-_a-zA-Z0-9.:\/?=]/}" ] ; then
            asceds-error "ACMESERVER contains illegal characters."
         fi

         echo "Please introduce EABKID value [default \"${EABKID}\"]"
         read -e -p "EABKID = " -i "${EABKID}" EABKID1
         echo
         if [ -z "${EABKID1}" ] ; then
            EABKID1="${EABKID}"
         elif [ "${EABKID1}" != "${EABKID1//[^a-zA-Z0-9]/}" ] ; then
            asceds-error "EABKID contains illegal characters. "
         fi

         echo "Please introduce EABHMACKEY value [default \"${EABHMACKEY}\"] "
         read -e -p "EABHMACKEY = " -i "${EABHMACKEY}" EABHMACKEY1
         echo
         if [ -z "${EABHMACKEY1}" ] ; then
            EABHMACKEY1="${EABHMACKEY}"
         elif [ "${EABHMACKEY1}" != "${EABHMACKEY1//[^-a-zA-Z0-9]/}" ] ; then
            asceds-error "EABHMACKEY contains illegal characters."
         fi
      fi

      echo "Please introduce the list of authorized domains separated by commas:"
      echo "   [default \"${AUTHDOMAIN}\"] "
      read -e -p "AUTHDOMAIN = " -i "${AUTHDOMAIN}" AUTHDOMAIN1
      echo
      AUTHDOMAIN1=$( cat <<< ${AUTHDOMAIN1} | tr '[:upper:]' '[:lower:]' | \
                   sed -e "s/\s//g" )
      if [ -z "${AUTHDOMAIN1}" ] ; then
         AUTHDOMAIN1="${AUTHDOMAIN}"
      elif [ "${AUTHDOMAIN1}" != "${AUTHDOMAIN1//[^-a-z0-9.,]/}" ] ; then
         asceds-error "AUTHDOMAIN contains illegal characters."
      fi
      echo

      echo "Introduced values:"
      if [ -z "${SELFSIGNED}" ] ; then
         echo "   ACMESERVER=\"${ACMESERVER1}\""
         echo "   EABKID=\"${EABKID1}\""
         echo "   EABHMACKEY=\"${EABHMACKEY1}\""
      fi
      echo "   AUTHDOMAIN=\"${AUTHDOMAIN1}\""
      echo -n 'Are these values correct (Ctrl-C to exit) [Y/n]? '
      read YE
   done
   echo "*** All these variables can be reconfigured later by hand in" 
   echo "   ${ASCEDSETCDIR}/asceds-certbot.conf"

   if [ -z "${SELFSIGNED}" ] ; then
      asceds-refresh-var "${ASCEDSETCDIR}/asceds-certbot.conf" \
         "ACMESERVER" "${ACMESERVER1}"
      asceds-refresh-var "${ASCEDSETCDIR}/asceds-certbot.conf" \
         "EABKID" "${EABKID1}"
      asceds-refresh-var "${ASCEDSETCDIR}/asceds-certbot.conf" \
         "EABHMACKEY" "${EABHMACKEY1}"
   fi

   # parse ${AUTHDOMAIN1} and make sure values are unique
   asceds-comma-uniq "AUTHDOMAIN1"
   asceds-refresh-var "${ASCEDSETCDIR}/asceds-certbot.conf" \
      "AUTHDOMAIN" "${AUTHDOMAIN1}"
}
####################################################################################
# asceds-change-userdom "user" "del"
# change/remove authority domains for "user"; returns 0 if successful

# Usage: asceds-change-userdom "${MODUSER}" "del"
asceds-change-userdom()
{
   local WEBUSR=$1
   local DELWEBUSR=$2

   CURRENT_DOM=$( ${GREP} "\['${WEBUSR}'\]" ${ASCEDSWEBDIR}/etc/users.php | \
         sed -e "s/^.*(//" | sed -e "s/).*$//" | sed -e "s/[\",]//g" | xargs)
   if [ -n "${CURRENT_DOM}" ] ; then
      echo "Current authorized domains for ${WEBUSR}:"
      echo "${CURRENT_DOM}"
   fi

   if [ -n "${DELWEBUSR}" ] ; then
      asceds-echo " Removing ${WEBUSR} from users.php"
      sed -i -e "/\['${WEBUSR/./\.}'\]/d" ${ASCEDSWEBDIR}/etc/users.php \
          || return 1
      return 0
   fi

   # authorized domains management
   if [ -z "${AUTHDOMAIN}" ] ; then
      echo "No authorized domains defined yet"
      return 1
   else
      echo 'Available authorized domains (use ALL for all domains):'
      echo $( cat <<< ${AUTHDOMAIN} | sed -e 's/,/ /g' )
   fi

   YE=''
   while [ "${YE}" != 'y' ] && [ "${YE}" != 'Y' ] ; do
      echo "Please type authorized domains for user ${WEBUSR}:"
      read -e AUTHD4USER0
      echo
      # wildcard for ALL domains and sanitize case
      if [ "${AUTHD4USER0}" != "ALL" ] && [ "${AUTHD4USER0}" != "all" ] ; then
         AUTHD4USER=$( cat <<< ${AUTHD4USER0} | tr '[:upper:]' '[:lower:]' )
      else
         AUTHD4USER="ALL"
      fi
      # check the input
      if [ "${AUTHD4USER}" != "${AUTHD4USER=//[^-a-z0-9., ]/}" ] ; then
         asceds-error "Malformed domain list ${AUTHD4USER}."
      elif [ -n "${AUTHD4USER}" ] ; then
         AUTHD4USER=$( cat <<< ${AUTHD4USER} | tr ',' ' ' | xargs )
      fi

      TESTTRUEDOM=''
      if [ "${AUTHD4USER}" != "ALL" ] ; then
         for AUTHD in ${AUTHD4USER} ; do
            TESTAUTHD=$( cat <<< ${AUTHDOMAIN} | ${GREP} -s "\b${AUTHD}\b" )
            if [ -z "${TESTAUTHD}" ] ; then
               TESTTRUEDOM='no'
               asceds-warning "Domain ${AUTHD} not in the authorized domain list"
            fi
         done
      fi
      if [ -z "${TESTTRUEDOM}" ] ; then
         echo "Authorized domains for user ${WEBUSR}: ${AUTHD4USER}"
         echo -n "Is this the desired configuration? [y/N] "
         read YE
      fi
   done

   # write domains to file ${ASCEDSWEBDIR}/etc/users.php
   AUTHUSERLINE="\$authdomains['${WEBUSR}'] = array( "
   if [ -n "${AUTHD4USER}" ] ; then
      for AUTHD in ${AUTHD4USER} ; do
         AUTHUSERLINE="${AUTHUSERLINE}\"${AUTHD}\", "
      done
      AUTHUSERLINE=$( cat <<< "${AUTHUSERLINE}" | sed -e "s/,\s*$/);/" )
   else
      AUTHUSERLINE="${AUTHUSERLINE});"
   fi   
   if [ -n "$( ${GREP} "\['${WEBUSR}'\]" ${ASCEDSWEBDIR}/etc/users.php )" ] ; then
      sed -i -e "/\['${WEBUSR/./\.}'\]/d" ${ASCEDSWEBDIR}/etc/users.php \
          || return 1
   fi
   sed -i -e "/?>/i ${AUTHUSERLINE}" ${ASCEDSWEBDIR}/etc/users.php || return 1
   return 0
}
  
####################################################################################
# asceds-change-email "user" "del"
# change/remove email address for "user"; returns 0 if successful

# Usage: asceds-change-email "${MODUSER}" "del"
asceds-change-email()
{
   local WEBUSR=$1
   local DELWEBUSR=$2

   # if username doesn't look like an email address name@domain
   if [[ "${WEBUSR}" =~ ^[a-z][-_a-z0-9]{0,31}@[a-z0-9][-a-z0-9]{0,31}(\.[a-z0-9][-a-z0-9]{0,31})*$ ]] ; then
      asceds-echo "Username ${WEBUSR} is an email address. No alias set."
   else
      asceds-echo " Removing ${WEBUSR} from /etc/aliases if it exists"
      sed -i -e "/^${WEBUSR/./\.}:/d" /etc/aliases || return 1
      newaliases || return 1
  
      if [ -z "${DELWEBUSR}" ] ; then
         echo "User name ${WEBUSR} doesn't looke like an email address."
         echo "Please type an email address to be used for notifications."
         echo "Leave it empty if no notification is desired."
         YE=''
         NOTIFICATIONADD=''
         while [ "${YE}" != "y" ] && [ "${YE}" != "Y" ] ; do 
            read -e -p "Notification email address: " NOTIFICATIONADD
            echo
            NOTIFICATIONADD=$( cat <<< ${NOTIFICATIONADD} | \
                   tr '[:upper:]' '[:lower:]' | sed -e "s/\s*//g" )
            if [[ ! "${NOTIFICATIONADD}" =~ ^[a-z][-_a-z0-9]{0,31}@[a-z0-9][-a-z0-9]{0,31}(\.[a-z0-9][-a-z0-9]{0,31})*$ ]] ; then
               echo "Notification email address ${NOTIFICATIONADD} is not formatted properly."
            else
               echo -n "Is \"${NOTIFICATIONADD}\" the right notification address? [y/N]: "
               read YE
            fi
         done
         if [ -n "${NOTIFICATIONADD}" ] ; then
            asceds-echo " Adding new address for ${WEBUSR} to /etc/aliases"
            cat <<< "${WEBUSR}: ${NOTIFICATIONADD}" >> /etc/aliases || return 1
            newaliases || return 1
         fi
      fi
   fi
   return 0
}

####################################################################################
# asceds-change-passwd "user" "del"
# change/remove simple auth password for "user"; returns 0 if successful

# Usage: asceds-change-passwd "${MODUSER}" "del"
asceds-change-passwd()
{
   local WEBUSR=$1
   local DELWEBUSR=$2

   # checks if simple auth is desired are performed by the caller
   if [ -n "${DELWEBUSR}" ] ; then
      if [ -r "${ASCEDSWEBDIR}/html/cert/.htpasswd" ] ; then
         asceds-echo " Removing ${WEBUSR} from .htpasswd"
         sed -i -e "/^${WEBUSR/./\.}:/d" ${ASCEDSWEBDIR}/html/cert/.htpasswd \
            || return 1
      else
         asceds-warning "The password file ${ASCEDSWEBDIR}/html/cert/.htpasswd does not exist"
      fi
   else
      asceds-echo " Setting simple auth password for user ${WEBUSR}"
      if [ ! -r "${ASCEDSWEBDIR}/html/cert/.htpasswd" ] ; then
         HTPASSWDF='-c'
      else
         HTPASSWDF=''
      fi
      # change/set password
      htpasswd ${HTPASSWDF} ${ASCEDSWEBDIR}/html/cert/.htpasswd ${WEBUSR} || \
         return 1
   fi
   return 0
}

####################################################################################
# asceds-config-configphp
# reconfigures the config.php file used by the website

# Usage: asceds-config-configphp
asceds-config-configphp()
{
   # get website header info
   # organization
   asceds-configphp-get "config.php" "organization" || \
      asceds-error 'No variable $organization in config.php'
   WEBORGVALUE="${ASCEDS_PHPVAL}"
   # unit
   asceds-configphp-get "config.php" "unit" || \
      asceds-error 'No variable $unit in config.php'
   WEBUNITVALUE="${ASCEDS_PHPVAL}"

   echo
   YE='n'
   CHANGEVAL=''
   while [ "${YE}" == "n" ] || [ "${YE}" == "N" ] ; do
      echo "Please provide the following website configuration data:"
      echo "Organization name: (default '${WEBORGVALUE}')"
      read -e -p "WEBORGVALUE = " -i "${WEBORGVALUE}" WEBORGVALUE0
      echo
      WEBORGVALUE0="${WEBORGVALUE0//[^-_a-zA-Z0-9. ]/}"
      if [ -n "${WEBORGVALUE0}" ] ; then
         WEBORGVALUE="${WEBORGVALUE0}"
         CHANGEVAL='yes'
      fi

      echo "Organizational unit/dept: (default '${WEBUNITVALUE}')"
      read -e -p "WEBUNITVALUE = " -i "${WEBUNITVALUE}" WEBUNITVALUE0
      echo
      WEBUNITVALUE0="${WEBUNITVALUE0//[^-_a-zA-Z0-9. ]/}"
      if [ -n "${WEBUNITVALUE0}" ] ; then
         WEBUNITVALUE="${WEBUNITVALUE0}"
         CHANGEVAL='yes'
      fi

      if [ -n "${CHANGEVAL}" ] ; then
         echo "New values for organization and unit/dept:"
         echo "Organization name: ${WEBORGVALUE}"
         echo "Organizational unit/dept: ${WEBUNITVALUE}"
         echo -n "Is this correct? [n/Y] "
         read YE
      else
         echo "The values for organization and unit/dept are kept unchanged"
         YE=''
      fi 
   done
   if [ -n "${CHANGEVAL}" ] ; then
      asceds-configphp-set "config.php" "organization" "${WEBORGVALUE}" || \
         asceds-error 'No variable $organization in config.php'
      asceds-configphp-set "config.php" "unit" "${WEBUNITVALUE}" || \
         asceds-error 'No variable $unit in config.php'
   fi
   if [ -n "${SELFSIGNED}" ] ; then
      asceds-refresh-var "${ASCEDSETCDIR}/certbot-ss.conf" \
         "A_ORG" "${WEBORGVALUE}"
      asceds-refresh-var "${ASCEDSETCDIR}/certbot-ss.conf" \
         "A_DIV" "${WEBUNITVALUE}"
      . ${ASCEDSETCDIR}/certbot-ss.conf

      echo
      echo "Please provide the following certificate configuration data:"
      echo "Country code (two capital letters): (default '${A_COUNTRY}')"
      read -e -p "A_COUNTRY = " -i "${A_COUNTRY}" A_COUNTRY0
      echo
      A_COUNTRY0=$( cat <<< ${A_COUNTRY0} | tr '[:lower:]' '[:upper:]' )
      A_COUNTRY0="${A_COUNTRY0//[^A-Z]/}"
      A_COUNTRY0="${A_COUNTRY0:0:2}"
      if [ -n "${A_COUNTRY0}" ] && [ "${A_COUNTRY0}" != "${A_COUNTRY}" ] ; then
         A_COUNTRY="${A_COUNTRY0}"
         asceds-refresh-var "${ASCEDSETCDIR}/certbot-ss.conf" \
            "A_COUNTRY" "${A_COUNTRY}"
      fi

      echo "State/Province (letters and -, no space): (default '${A_STATE}')"
      read -e -p "A_STATE = " -i "${A_STATE}" A_STATE0
      echo
      A_STATE0="${A_STATE0//[^-A-Za-z]/}"
      if [ -n "${A_STATE0}" ] && [ "${A_STATE0}" != "${A_STATE}" ] ; then
         A_STATE="${A_STATE0}"
         asceds-refresh-var "${ASCEDSETCDIR}/certbot-ss.conf" \
            "A_STATE" "${A_STATE}"
      fi

      echo "City (letters and -, no space): (default '${A_CITY}')"
      read -e -p "A_CITY = " -i "${A_CITY}" A_CITY0
      echo
      A_CITY0="${A_CITY0//[^-A-Za-z]/}"
      if [ -n "${A_CITY0}" ] && [ "${A_CITY0}" != "${A_CITY}" ] ; then
         A_CITY="${A_CITY0}"
         asceds-refresh-var "${ASCEDSETCDIR}/certbot-ss.conf" \
            "A_CITY" "${A_CITY}"
      fi
   fi

   # get qeue policy info
   # newcert mode: cron/ssh
   asceds-configphp-get "config.php" "newcertmode" || \
      asceds-error 'No variable $newcertmode in config.php'
   if [ "${ASCEDS_PHPVAL}" != "ssh" ] ; then
      WEBNEWCERTMODE='cron'
   else
      WEBNEWCERTMODE='ssh'
   fi
   # revoke mode: cron/ssh
   asceds-configphp-get "config.php" "revokemode" || \
      asceds-error 'No variable $revokemode in config.php'
   if [ "${ASCEDS_PHPVAL}" != "ssh" ] ; then
      WEBREVOKEMODE='cron'
   else
      WEBREVOKEMODE='ssh'
   fi
   # propagate mode: cron/ssh
   asceds-configphp-get "config.php" "propagatemode" || \
      asceds-error 'No variable $propagatemode in config.php'
   if [ "${ASCEDS_PHPVAL}" != "ssh" ] ; then
      WEBPROPAGATEMODE='cron'
   else
      WEBPROPAGATEMODE='ssh'
   fi
   echo
      
   echo "The queue modes can be toggled between 'cron' and 'ssh' modes."
   echo "New certificate request queue mode is set to '${WEBNEWCERTMODE}';"
   echo -n "Keep it that way? [Y/n] "
   read YES
   if [ "${YES}" == "n" ] || [ "${YES}" == "N" ] ; then
      if [ "${WEBNEWCERTMODE}" == "ssh" ] ; then
         WEBNEWCERTMODE='cron'
      else
         WEBNEWCERTMODE='ssh'
      fi
   fi
   asceds-configphp-set "config.php" "newcertmode" "${WEBNEWCERTMODE}" || \
         asceds-error 'No variable $newcertmode in config.php'


   echo "Revoke request queue mode is set to '${WEBREVOKEMODE}';"
   echo -n "Keep it that way? [Y/n] "
   read YES
   if [ "${YES}" == "n" ] || [ "${YES}" == "N" ] ; then
      if [ "${WEBREVOKEMODE}" == "ssh" ] ; then
         WEBREVOKEMODE='cron'
      else
         WEBREVOKEMODE='ssh'
      fi
   fi
   asceds-configphp-set "config.php" "revokemode" "${WEBREVOKEMODE}" || \
         asceds-error 'No variable $revokemode in config.php'

   echo "Propagate request queue mode is set to '${WEBPROPAGATEMODE}';"
   echo -n "Keep it that way? [Y/n] "
   read YES
   if [ "${YES}" == "n" ] || [ "${YES}" == "N" ] ; then
      if [ "${WEBPROPAGATEMODE}" == "ssh" ] ; then
         WEBPROPAGATEMODE='cron'
      else
         WEBPROPAGATEMODE='ssh'
      fi
   fi
   asceds-configphp-set "config.php" "propagatemode" "${WEBPROPAGATEMODE}" || \
         asceds-error 'No variable $propagatemode in config.php'

   echo "New mode settings for the queues:"
   echo "New certificate = ${WEBNEWCERTMODE}"
   echo "Revoke certificate = ${WEBREVOKEMODE}"
   echo "Propagate certificate = ${WEBPROPAGATEMODE}"
   echo

   # sync info from asceds-local.conf to config.php:
   # admin email 
   asceds-configphp-set "config.php" "adminemail" "${ALARM}" || \
      asceds-error 'No variable $adminemail in config.php'
   # path to website executables
   asceds-configphp-set "asceds.php" "pathtoexec" "${ASCEDSHOME}/website" || \
      asceds-error 'No variable $pathtoexec in asceds.php'
   # asceds home dir
   asceds-configphp-set "asceds.php" "ascedshomedir" "${ASCEDSHOMEDIR}" || \
      asceds-error 'No variable $ascedshomedir in asceds.php'
   # asceds web dir
   asceds-configphp-set "asceds.php" "ascedswebdir" "${ASCEDSWEBDIR}" || \
      asceds-error 'No variable $ascedswebdir in asceds.php'
}

####################################################################################
# asceds-disable-host "client"
# remove "client" from asceds and certbot

# Usage: asceds-disable-host "${TOREM}"
asceds-disable-host()
{
   local HSTNM=$1
   if [ -z "${HSTNM}" ] ; then
      asceds-error "Empty argument passed to asceds-disable-host"
   fi

   asceds-echo "Renaming cert manager copies of asceds certificate files"
   if [ -r "${ASCEDSHOMEDIR}/${HSTNM}_cert.conf" ] ; then
      asceds-parse-certconf "${ASCEDSHOMEDIR}/${HSTNM}_cert.conf"
      if [ -z "${CLIENT_DOMAIN_NAME}" ] || \
         [ "${HSTNM}" != "${CLIENT_DOMAIN_NAME}" ] ; then
         asceds-error "Client '${HSTNM}' is misconfigured"
      fi
      if [ -e "${ASCEDSHOMEDIR}/${HSTNM}_cert.conf.del" ] ; then
         ${RM} -f ${ASCEDSHOMEDIR}/${HSTNM}_cert.conf.del
      fi
      ${MV} -f ${ASCEDSHOMEDIR}/${HSTNM}_cert.conf \
            ${ASCEDSHOMEDIR}/${HSTNM}_cert.conf.del
   fi
   if [ -d "${ASCEDSHOMEDIR}/${HSTNM}" ] ; then
      if [ -e "${ASCEDSHOMEDIR}/${HSTNM}.del" ] ; then
         ${RM} -Rf ${ASCEDSHOMEDIR}/${HSTNM}.del
      fi
      ${MV} -f ${ASCEDSHOMEDIR}/${HSTNM} ${ASCEDSHOMEDIR}/${HSTNM}.del
   fi     
   asceds-echo "Deleting certbot certificate files"
   if [ -d "/etc/letsencrypt/archive/${HSTNM}" ] ; then
      ${RM} -Rf /etc/letsencrypt/archive/${HSTNM}
   fi
   if [ -d "/etc/letsencrypt/live/${HSTNM}" ] ; then
      ${RM} -Rf /etc/letsencrypt/live/${HSTNM}
   fi
   if [ -r "/etc/letsencrypt/renewal/${HSTNM}.conf" ] ; then
      ${RM} -f /etc/letsencrypt/renewal/${HSTNM}.conf 
   fi
   asceds-echo "Cleaning up the rest of the client files"
   if [ -n "${ASCEDS_CERT_ID}" ] ; then
      ${RM} -Rf ${ASCEDSWEBDIR}/html/cert/keys/${ASCEDS_CERT_ID}
   fi
   if [ -r "${ASCEDSHOMEDIR}/down/${HSTNM}" ] ; then
      ${RM} -f ${ASCEDSHOMEDIR}/down/${HSTNM} 
   fi
}

####################################################################################
