#!/bin/bash
VERSION="8.47.3 [16 Mar 2026]"
set -o pipefail
# Script to setup initial configuration for TimeDicer Server
#
# For code to download and run this file see end of the file
#
docmd() {
# call this as e.g. docmd "description of command" "the command to run" "/etc/crontab" "y"
# 3rd parameter if present is the file to append output to (or use /dev/null)
# if 4th parameter is present this acts as force
if [[ -n $4 ]]; then
	echo "$1"; $CURS_UP
	YESNO=$4
elif [[ -n $FORCE ]]; then
	echo "$1"; $CURS_UP
	YESNO=y;
else
	read -rp "${1} (y/-): " YESNO
	$CURS_UP
fi
erred=$?
if [[ "$YESNO" == "y" ]]; then
	echo "$2" >"$TMPF"
	if [[ -n $VERBOSE ]]; then
		echo
		if [[ -n $3 ]]; then
			. "$TMPF" >>"$3"; erred=$?
		else
			. "$TMPF"; erred=$?
		fi
	else
		if [[ -n $3 ]]; then
			. "$TMPF" >>"$3"; erred=$?
		else
			. "$TMPF"; erred=$?
		fi
	fi
	echo -n "$1"
	$SET_COL
	if [[ "$erred" -eq 0 ]]; then
		echo "[ OK ]"
		rm -- "$TMPF"
	else
		echo "[FAIL]"
	fi
else
	$SET_COL
	echo "[SKIP]"
	#SKIPPED=Y
fi
ERREDTOTAL=$((ERREDTOTAL+erred))
return $erred
}

EditOrAdd() {
# edit or add aka modify or add aka replace or append
# 3 parameters: regex(egrep)_to_check_for full_line_to_replace/add file
# - case-sensitive test of file $3 seeking the 1st line containing regex $1; then change or add it to read $2
# example     : EditOrAdd "^favicon ?=" "favicon = /etc/rdiffweb/favicon.ico" /etc/rdiffweb/rdw.conf
local SEARCH_FOR
SEARCH_FOR="$1"
local REPLACE_WITH
REPLACE_WITH="$2"
local FILE
FILE="$3"
local LINENUM
LINENUM=$(grep -Enm1 "$SEARCH_FOR" "$FILE"|cut -d: -f1)
if [[ $LINENUM =~ ^[1-9][0-9]*$ ]]; then
	sed -i "${LINENUM}s~.*~${REPLACE_WITH}~" "$FILE"
else
	echo "$REPLACE_WITH" >>"$FILE"
fi
}

THIS="$(basename "$0")"
echo -e "\n$THIS v$VERSION by Dominic (try -h for help)\n${THIS//?/=}\n"
COLUMNS=$(stty size 2>/dev/null||echo 80); COLUMNS=${COLUMNS##* }
COL=$((COLUMNS - 5))
SET_COL="echo -en \\033[${COL}G"
CURS_UP="echo -en \\033[A"
ERREDTOTAL=0
PRIMARYUSER=$(grep :1000: /etc/passwd|awk -F : '{print $1}')
IP4ADDRESSES=$(ip addr show|grep " inet "|grep -v "127\.0\.[01]\.1"|awk -F"[ /]*" '{print $3}')

while getopts ":cdflhvw" optname; do
	case "$optname" in
	"c")
		[[ "$IP4ADDRESSES" == "192.168.100.193" ]] || { echo "Unknown option -c"; exit 1; }
		# undocumented -c option
		[[ $(id -u) -eq 0 ]] || { echo "$THIS must be run with sudo or as root"; exit 1; }
		set -e # stop on any error
		cp -au "/opt/$THIS" "/tmp/$THIS"
		THISC="$(basename "$0" .sh).tar.bz2"
		echo "Recreating archive file $THISC"
		tar -cjPf "/tmp/$THISC" /opt/timedicer-rename-user.sh /opt/lvm-usage.sh /opt/newuser-request.sh /opt/timedicer-mirror.sh /var/www/html/processing.php /var/www/html/index.php /var/www/html/timedicer-username.bat /var/www/html/timedice.css /var/www/html/timedicer_die.png /var/www/html/timedicer_full.png /var/www/html/favicon.ico /var/www/html/robots.txt /opt/make-home-lv.sh /opt/timedicer-verify.sh /opt/lvm-delete-snapshot.sh /opt/dutree.sh /opt/timedicer-bloatwatch.sh "/tmp/$THIS"
		[[ -f /opt/$THISC ]] || touch "/opt/$THISC"
		if diff -q "/opt/$THISC" "/tmp/$THISC"; then
			echo "$THISC has not changed, server does not need to be updated"
		else
			cp "/tmp/$THISC" "/opt/" && echo "Updated $THISC locally, now run timedicer-update.sh on server"
		fi
		rm -- "/tmp/$THISC" "/tmp/$THIS"
		exit 0;;
 	"d") DEBUG="-d ";;
	"f") FORCE="-f ";;
	"h") HELP=y;;
	"l") CHANGELOG=y;;
	"v") VERBOSE="-v ";;
	"w") COLUMNS=30000;;
	"?") echo "Unknown option $OPTARG"; exit 1;;
	":") echo "No argument value for option $OPTARG"; exit 1;;
	*)
		# Should not occur
		echo "Unknown error while processing options"; exit 1;;
    esac
done
shift $((OPTIND-1))
if [[ "$HELP" == "y" ]]; then
	echo -e "$THIS is part of the TimeDicer suite https://www.timedicer.co.uk, \
and sets up a TimeDicer Server or updates its existing setup.

It is normally run with -f option after the first boot of a new TimeDicer \
Server machine. It can be run subsequently with or without -f option to \
update the machine.

Usage: $THIS [option] [you@your_email_address.org]

Options: -v verbose (show normal output from commands)
         -f run automatically assuming 'y' for all questions - email \
address on command line required
         -h show this help
         -l show changelog

Details: Depending on the user's choices and existing installed software, \
$THIS configures the server as follows -
- download and extract key scripts from www.timedicer.co.uk (including \
self-updating this script)
- check and update timezone information [requires user response]
- download and install postfix, including optional relay authentication \
[requires user response]
- download and install updates for Ubuntu
- restrict access so that each user's /home folder is readable only \
by that user (and the administrator)
- download and install wakeonlan, lvm2, ssh, apache2, php
- bespoke settings for nano, php, postfix, cron, and enable apache2.conf
- create ssh keys for primary user and for root
- if root filesystem is encrypted, setup boot-time ssh to allow remote \
entering of encryption passphrase
- download and install rdiff-backup, Rdiffweb and incron
- start Rdiffweb, apache2 and postfix
- send a test email

License: Copyright © 2026 Dominic Raferd. Licensed under the Apache License, \
Version 2.0 (the \"License\"); you may not use this file except in compliance \
with the License. You may obtain a copy of the License at \
https://www.apache.org/licenses/LICENSE-2.0. Unless required by applicable \
law or agreed to in writing, software distributed under the License is \
distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY \
KIND, either express or implied. See the License for the specific language \
governing permissions and limitations under the License.
"|fold -sw"$COLUMNS"
fi
if [[ -n $CHANGELOG ]]; then
	[[ -n $HELP ]] && echo -n "Changelog: "
	echo "[for versions prior to 8.12, version number indicates when: 201y.mmdd]
8.47 [08 Oct 2025]: substantially shellcheck compliant, but changes not fully tested
8.46 [26 Feb 2024]: ensure cron-generated outgoing emails use the modern 'From:' form
8.45 [12 Feb 2024]: remove no-longer-required-or-supported nano setting, tweak Rdiffweb settings
8.44 [05 Feb 2024]: bugfix - install bzip2 if missing
8.43 [17 Jan 2024]: updates to undocumented option
8.42 [29 Jun 2023]: small updates
8.41 [13 Jan 2023]: update Rdiffweb installation process
8.40 [12 Jan 2023]: update Rdiffweb installation process
8.35 [07 Nov 2022]: update to use rdiff-backup from standard repo package for Ubuntu>=22.04
8.34 [19 Jul 2022]: update Rdiffweb installation process
8.33 [23 Mar 2021]: update Rdiffweb installation process
8.32 [21 Jan 2021]: bugfix for adding incrontab entry
8.31 [07 Jan 2021]: fixes for rdiff-backup installation
8.30 [26 Nov 2020]: check any current rdiff-backup version and install latest rdiff-backup (currently 2.0.5) from PPA, not the older (possibly broken) version in standard Debian repository
8.20 [07 Jul 2020]: updated for new Rdiffweb
8.15 [24 May 2019]: bugfix for encrypted setup where eth device is not eth0
8.14 [09 May 2019]: bugfixes to prevent hangs
8.13 [23 Dec 2018]: simplify apache2 and postfix localisations
8.12 [19 Dec 2018]: use incron to monitor new TimeDicer user requests, don't request relayhost info if already set up
8.1115:  set postfix to refuse any incoming emails
8.1114:  bugfixes for encryption setup and for adding TimeDicer user, exit if initial apt-get fails
8.1030:  bugfix for Remote Booting with Encrypted LVM (LUKS + dm-crypt) (key \
was placed in wrong file)
8.0919:  set postfix to run at latest compatibility level, allow correction \
of relayhost when setting up relaying
8.0703:  bugfix compatibility with Ubuntu 18.04LTS (for php 7.2)
8.0414:  a number of bugfixes (including self-updating code), add testing \
of relayhost authentication for satellite mail setup (using swaks), tested \
under Ubuntu 18.04 LTS
8.0413:  use pip to install rdiffweb
8.0117:  if using LUKS + dm-crypt encrypted root fs, set up initramfs to obtain ip by DHCP
7.0609:  new way of obtaining ip addresses
7.0425:  add ability to work with, and setup remote login/mounting for, LUKS \
+ dm-crypt encrypted root fs, add full setup of email relayhost authentication
7.0412:  very minor text fixes
6.0707:  bugfix
6.0706:  improve self-updating code, remove php5-common for Ubuntu >=16.04, don't install sshfs
6.0622:  make compatible with Ubuntu 16.04 (Xenial), force Rdiffweb 0.6.3 upgrade
6.0426:  delete any pre-existing .don files when updating key scripts
6.0422:  internal changes
6.0218:  no longer compulsory to supply email address on command line (except with -f)
5.1224:  install new Rdiffweb >=0.7
5.0806:  some bugfixes
5.0129:  add dutree.sh
4.1203:  bugfix so that local TimeDicer homepage appears correctly after installation
4.1116:  further small changes for Ubuntu 14.04
4.1111:  fix for Ubuntu 14.04 (copy web page files to /var/www/html)
2.1107:  timedicer-mirror.sh updated
2.0827:  timedicer-mirror.sh updated, help text updated
2.0701:  fix for Ubuntu 12.04 to prevent apache2 messages about undefined ServerName
2.0627:  fix for Ubuntu 12.04 to allow creation of primary user rsa key, \
don't create /home/tmp, add cryptsetup package to prevent inotify_add_watch \
messages/delay at boot time
2.0515:  renamed as $THIS, now self-updating
2.0425:  fix of a small (but fatal) bug
2.0312:  small bugfix, updated help text for rdiffweb-install.sh
2.0308:  rename timedicer-mirror as timedicer-mirror.sh
2.0224:  remove rdiff-backup-fs-install and vmware-tools-install from \
package of installed utilities, rename rdiffweb-install as rdiffweb-install.sh
2.0221:  rename lv-usage.sh as lvm-usage.sh
1.1231:  improved output from lv-usage.sh (cron job test for disk space)
1.1023:  create /home/tmp directory when rdiff-backup is installed
1.1011:  recoding for timedicer-mirror
1.1009:  bugfixes for rdiffweb-install, timedicer-mirror and newuser-request.sh
1.0916a: remove heirloom-mailx (and remove dependency upon it)
1.0916:  add timezone configuration
1.0915c: fix rdiffweb-install failure on 2nd run, add rdiffweb setup package \
to timedicer-server-setup.tar.bz2 so setup can still work if rdiffweb site is down
1.0915:  add recognition of any type of network port
1.0914:  add make-home-lv.sh, can ease LVM configuration \
fix for rdiffweb-install to work with python2.7 (Ubuntu 11.04)
1.0913:  add install of postfix, lvm2, ssh and heirloom-mailx to assist with \
VM-based or non-standard TimeDicer Server setup
1.0901:  alter Server web pages so no data (css, png) is loaded from \
www.timedicer.co.uk, all is held and accessed locally - removes \
an unintended 'phone home' and means page formats are okay even \
if Server has no internet access
1.0830:  alter various files so that when a new TimeDicer user is created, a new \
rdiffweb user is also created
1.0306:  remove installation of rdiff-backup-fv [patched rdiff-backup]
1.0301:  bugfix - prevent automatic silent abort on exit code>0 \
remove daily repo verification from cron
1.0227:	 added changelog option and updated help text
1.0222:  install wakeonlan, add script install-rdiff-backup-fs \
(but rdiff-backup-fs not installed)
1.0217:  install sshfs and auto-add new users to fuse group
1.0216:  bugfix - create root rsa key without passphrase
1.0207:  bugfixes
"
fi
[[ -n $HELP$CHANGELOG ]] && exit 0

[[ $(id -u) != 0 ]] && echo "$THIS must be run with sudo or as root" && exit 1

[[ "$1" =~ @ ]] && MYEMAIL="$1" || MYEMAIL="$(grep -Em1 "^\s?MAILTO=" /etc/crontab|cut -d= -f2)"
[[ -n $MYEMAIL ]] && echo "Default email-to address will be: $MYEMAIL"

TMPF=$(mktemp)

# see if we are using LUKS +dm-crypt
if command -v cryptsetup >/dev/null 2>&1; then
	LUKSROOT=$(blkid|grep "crypto_LUKS"|head -n1|cut -d: -f1)	# detect (1st) encrypted partition with LUKS + dm-crypt
	if cryptsetup isLuks "$LUKSROOT" 2>/dev/null; then
		lsblk -i "$LUKSROOT"|grep -q "root\s.*\s/$" || unset LUKSROOT	# confirm (or not) that root filesystem is encrypted
	else
		unset LUKSROOT	# we can't confirm that $LUKSROOT really is LUKS
	fi
fi
if [[ -n $VERBOSE ]]; then
	[[ -n $LUKSROOT ]] && echo "Found root filesystem on encrypted (LUKS + dm-crypt) partition $LUKSROOT" || echo "root filesystem is not on an encrypted partition"
fi
[[ -n $LUKSROOT ]] && ADDITIONAL_INSTALLS="dropbear-initramfs"

[[ -n $FORCE && -z $MYEMAIL ]] && { echo "Missing parameter: please rerun, specifying an email address to receive notices from TimeDicer Server"; exit 1; }

[[ -z $IP4ADDRESSES ]] && echo "Unable to find ip address from this machine, aborting..." && exit 1

# install bzip2 if missing
command -v bzip2 >/dev/null 2>&1 || docmd "Install bzip2" "apt-get -qqy install bzip2" || { echo "There was a problem installing critical software, $THIS is aborting - please try again later" >&2; exit 1; }

if [[ $(dirname "${BASH_SOURCE[0]}") != "/tmp" ]]; then
	docmd "Download and extract key scripts from www.timedicer.co.uk" "wget -qO /tmp/timedicer-server-setup.tar.bz2  https://www.timedicer.co.uk/server/timedicer-server-setup.tar.bz2"
	if [[ "$YESNO" == "y" ]]; then
		[[ ! -e /tmp/timedicer-server-setup.tar.bz2 ]] && { echo "Sorry, couldn't find downloaded key scripts, unable to continue"; exit 1; }
		[[ -f /tmp/$THIS ]] && rm -- "/tmp/$THIS" # to prevent a permission error if re-running
		if [[ "$IP4ADDRESSES" != 192.168.100.193 ]]; then
#			fails because you cant combine overwrite and no-overwrite-dir switches [14Apr18]
#			tar --overwrite --preserve-permissions --no-overwrite-dir -xPjf /tmp/timedicer-server-setup.tar.bz2 || { echo "Error extracting: unable to continue"; exit 1; }
			tar --overwrite --preserve-permissions -xPjf /tmp/timedicer-server-setup.tar.bz2 || { echo "Error extracting: unable to continue"; exit 1; }
		fi
	fi
	# code to run a newer version of this program if one was extracted (to /tmp)
	if [[ -x /tmp/$THIS ]]; then
		# shellcheck disable=SC2068
		[[ $(stat -c%Y "/tmp/$THIS") -gt $(stat -c%Y "${BASH_SOURCE[0]}") ]] && { echo "A newer version of $THIS has been retrieved. Switching to this version."; exec "/tmp/$THIS" $@; }
	fi
else
	# we are running from /tmp and presumably from a new copy of $THIS
	cp "/tmp/$THIS" /opt
fi

# delete any old add-user files, these are a security risk [added 26Apr16]
find /var/www/.adduser -name "*.don" -delete 2>/dev/null
# create (if missing) subfolder for newuser details
# shellcheck disable=SC2174
mkdir -p -m 777 /var/www/.adduser
# set permissions of batch file so it can be accessed
chmod 644 "/var/www/html/timedicer-username.bat" 2>/dev/null
TOWEBDIR="/var/www/html"
if [[ -d /etc/apache2/sites-enabled ]]; then
	TOWEBDIR=$(grep -R "DocumentRoot\s*/var/www" /etc/apache2/sites-enabled|awk '{print $NF; exit}')
	# move web pages from /var/www/html
	# normally has no effect, but if running on a machine which does not have its document root at
	# /var/www/html (e.g. Ubuntu 12.04) then should allow TimeDicer local webpage to work
	# shellcheck disable=SC2038
	[[ "$TOWEBDIR" != "/var/www/html" ]] && find /var/www/html -maxdepth 1 -type f|xargs -I {} mv {} "$TOWEBDIR"
fi

# a few things at the beginning are interactive, so VERBOSE must be on
VERBOSE_HOLDER=$VERBOSE; VERBOSE="-v "
# check timezone
docmd "Check/update timezone information" "dpkg-reconfigure tzdata"
# install postfix and swaks (swaks is used to test authentication if required)
[[ ! -f /etc/postfix/main.cf ]] && { docmd "Install & configure postfix" "apt-get -qqy install postfix postfix-pcre swaks" || { echo "There was a problem installing critical software, $THIS is aborting - please try again later" >&2; exit 1; } }
VERBOSE=$VERBOSE_HOLDER
[[ -n $DEBUG ]] && echo "passed postfix installation"
# set postfix compatibility to latest
POSTFIX_VER=$(postconf -h mail_version 2>/dev/null|cut -d"." -f1-2)
# shellcheck disable=SC2181
if [[ $? -eq 0 ]]; then
	if [[ $(postconf -h compatibility_level) != "$POSTFIX_VER" ]]; then
		postconf compatibility_level="$POSTFIX_VER"; postfix reload
		[[ -n $DEBUG ]] && echo "postfix compatibility_level set to $POSTFIX_VER"
	fi
fi
[[ -n $DEBUG ]] && echo "passed postfix compatibility reset"

# Ensure cron-generated emails use the modern 'From:' form by stripping the old-type 'From:' header
if [[ ! -f pcre:/etc/postfix/check_header.pcre ]]; then
	# note: this might cause problems if this postfix instance were processing external emails, but this is not
	#       anticipated for a TimeDicer server
	echo -e "if /^From:/\n/ \(.*\)$/ IGNORE\nendif" >>/etc/postfix/check_header.pcre
	postconf -e header_checks=pcre:/etc/postfix/check_header.pcre; postfix reload
fi

# setup postfix relayhost authentication - do it before attempting main system update
RELAYHOST=$(sed -n 's/^relayhost = //p' /etc/postfix/main.cf); NEWRELAYHOST=$RELAYHOST
if [[ -n $RELAYHOST ]] && ! grep -aq "${RELAYHOST%%:*}" /etc/postfix/sasl_passwd.db 2>/dev/null; then
	while true; do
		read -rp "Username for authentication at $NEWRELAYHOST (or blank): " RELAYUSER
		if [[ -n $RELAYUSER ]]; then
			read -srp "Password for authentication at $NEWRELAYHOST: " REPLY && echo
			if [[ -n $REPLY ]]; then
				if swaks --version >/dev/null 2>&1; then
					# test with swaks if available
					if [[ $(swaks -tls --auth PLAIN --auth-hide-password "[password_hidden]" --auth-plaintext --auth-user "$RELAYUSER" --auth-password "$REPLY" --quit-after MAIL --server "$NEWRELAYHOST" 2>&1|grep -cF "2.7.0 Auth") -lt 1 ]]; then
						echo "Username and password were not validated at $NEWRELAYHOST, try again"
					else
						# ok, connection succeeded
						echo "Username and password were validated at $NEWRELAYHOST"
						break
					fi
				else
					# no swaks, connection can't be tested
					echo "Note: username and password have not been tested at $NEWRELAYHOST!"
					break
				fi
			else
				echo "Error entering password, try again"
			fi
		fi
		echo "Trying again:"
		read -rp "Relayhost:port through which emails will be sent: " NEWRELAYHOST
	done
	[[ $NEWRELAYHOST =~ :25$ ]] && NEWRELAYHOST=${NEWRELAYHOST:: -3}
	[[ $RELAYHOST != "$NEWRELAYHOST" ]] && postconf "relayhost=$NEWRELAYHOST"
	if [[ -n $RELAYUSER ]]; then
		touch /etc/postfix/sasl_passwd
		echo "$RELAYHOST $RELAYUSER:$REPLY" >>/etc/postfix/sasl_passwd
		chown root:root /etc/postfix/sasl_passwd
		chmod 600 /etc/postfix/sasl_passwd
		postmap /etc/postfix/sasl_passwd
		grep -q "^smtp_sasl_auth_enable..*=.*yes" /etc/postfix/main.cf || echo -e "smtp_sasl_auth_enable = yes\nsmtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd\nsmtp_sasl_security_options =\nsmtp_tls_security_level = may" >>/etc/postfix/main.cf
	fi
	postfix reload
fi

# update distro to latest
DISTRO=$(lsb_release -ds)
docmd "Download and install latest updates for $DISTRO" "apt-get update -y"
[[ $? -eq 0 && "$YESNO" == "y" ]] && docmd "Installing $DISTRO updates - may take some time" "apt-get upgrade -y --allow-downgrades --allow-remove-essential --allow-change-held-packages" "" "y"

# so new users home folders are readable/writeable only by themselves
if ! grep -q "DIR_MODE=0700" /etc/adduser.conf; then
	docmd "Set home folders to be inaccessible to others" "sed -i s/DIR_MODE=0755/DIR_MODE=0700/ /etc/adduser.conf"
	if [[ "$YESNO" == "y" ]]; then chmod -R 0700 "/home/$PRIMARYUSER"; fi
fi

# set some friendly default options for nano for primary user and for root
if ! grep -q "^set softwrap" /root/.nanorc; then
	docmd "Set nice default options for nano text editor" "echo -e set autoindent\\\\nset constantshow\\\\nset functioncolor green,white\\\\nset noconvert\\\\nset nonewlines\\\\nset softwrap\\\\nset statuscolor green,black\\\\nset tabsize 2\\\\nset titlecolor green,black\\\\nset positionlog\\\\nset regexp" "/root/.nanorc"
	if [[ "$YESNO" == "y" ]]; then
		touch "/home/$PRIMARYUSER/.nano_history"
		cp "/root/.nanorc" "/home/$PRIMARYUSER/.nanorc"
		chown "$PRIMARYUSER:" "/home/$PRIMARYUSER/.nano_history" "/home/$PRIMARYUSER/.nanorc"
	fi
fi

# install wakeonlan
if [[ $(whereis wakeonlan 2>/dev/null|wc -w) -lt 2 ]]; then
	docmd "Install wakeonlan (can be used by timedicer-mirror.sh)" "apt-get -q install wakeonlan"
fi

# install lvm2, ssh, apache2, php, run-one [- also cryptsetup (to stop inotify_add_watch error messages at boot)]
MAJOR_RELEASE=$(lsb_release -r|cut -f2|cut -d. -f1)
# Ubuntu 16.04 works with php 7.0
ADDITIONAL_INSTALLS="${ADDITIONAL_INSTALLS}libapache2-mod-php php-xml run-one"
# if upgraded from earlier Ubuntu then php5 should be removed
dpkg -s php5-common >/dev/null 2>&1 && docmd "Remove php5-common" "apt-get -qq purge php5-common"

docmd "Install & configure lvm2 ssh apache2 moreutils $ADDITIONAL_INSTALLS" "apt-get -qq install lvm2 ssh apache2 moreutils $ADDITIONAL_INSTALLS"
if [ "$YESNO" = "y" ]; then
	echo -n "Adjusting apache2 settings"
	# move the default apache homepage so that index.php will be used instead
	[[ -f $TOWEBDIR/index.html ]] && mv -f "$TOWEBDIR/index.html" "$TOWEBDIR/index.old"
	echo -n "."
	# if ServerName directive missing from apache2.conf, add it
	if ! grep -q "^ServerName " /etc/apache2/apache2.conf; then
		sed -i 's/^#\{0,1\}\(ServerRoot .*\)/\1\n# ServerName added by timedicer-server-setup\nServerName localhost/' /etc/apache2/apache2.conf
	fi
	echo -n "."
	# identify the highest php version available (must have .so file) - answer e.g. php7.2 [03 Jul 2018]
	PHPVER=$(grep -rH "lib" /etc/apache2/mods-available/php*.load|awk -F"[: ]" '{if ( system("ls "$NF" >/dev/null 2>&1") == 0 ) print $1}'|sort|tail -n1|awk -F"[/]" '{print substr($NF,1,length($NF)-5)}')
	echo -n "."
	# copy the requisite apache php files to mods-enabled
	for EXT in .conf .load; do
		[[ ! -h "/etc/apache2/mods-enabled/$PHPVER$EXT" ]] && ln -s ../mods-available/"$PHPVER$EXT" /etc/apache2/mods-enabled/"$PHPVER$EXT"
	done
	echo -n "."
	# remove any extraneous php-enabling symlinks
	find /etc/apache2/mods-enabled -maxdepth 1 -type l -name "php*" -not -name "${PHPVER}.*" -delete
	echo -n "."; $SET_COL
	# make apache2 aware of the changes
	apache2ctl -k graceful >/dev/null && echo "[ OK ]" || echo "[FAIL]"
fi
[[ -n $DEBUG ]] && echo "passed php/apache etc installation"

# alter postfix settings so that emails to the same domain as this machine
# (as set at setup time) do not loop back to here
MAILDOMAIN="$(cat /etc/mailname 2>/dev/null)"
if [[ -n $MAILDOMAIN ]]; then
	docmd "Configure email settings (postfix)" "echo -n"
	if [[ "$YESNO" == "y" ]]; then
		[[ -z $MYEMAIL ]] && { echo "Unable to continue: please rerun, specifying an email address to receive notices from TimeDicer Server"; exit 1; }
		# alteration so that emails back to 'root' or 'postmaster' go out to the user
		echo -e "# see man 5 aliases for further info\n# modified by $THIS $(date)\npostmaster: $1\nroot: $1" >/etc/aliases
		newaliases
		# alteration so that emails from 'root' or 'postmaster' appear to be from '(machinename)'
		echo -e "# modified by $THIS $(date)\nroot $(uname -n)\npostmaster $(uname -n)" >/etc/postfix/generic
		postmap /etc/postfix/generic
		postconf "mydestination=$MAILDOMAIN"
		postconf smtp_generic_maps=hash:/etc/postfix/generic
		# prevent postfix from being open to receiving emails - it is for outgoing only
		postconf inet_interfaces=loopback-only
		# reload postfix with changes
		postfix reload 2>/dev/null
	fi
fi

# establish primary user key, there will be problems with ssh-keygen if primaryuser name has a space in it!
if [[ -z $(ls /home/"$PRIMARYUSER"/.ssh/id_rsa 2>&-) ]]; then
	# create and set permissions and ownership for .ssh folder (rqd for Ubuntu 12.04)
	# shellcheck disable=SC2174
	mkdir -p -m 700 /home/"$PRIMARYUSER"/.ssh && chown "$PRIMARYUSER:$(id -gn "$PRIMARYUSER")" /home/"$PRIMARYUSER"/.ssh
	# we have to set a real passphrase here, trying to set blank does not work
	docmd "Setup user '$PRIMARYUSER' private/public key" "ssh-keygen -q -f /home/$PRIMARYUSER/.ssh/id_rsa -C autocreated-by-timedicer-server-setup-for-$PRIMARYUSER@$(uname -n) -N passphrase"
	# now we can change passphrase to blank as direct command
	[[ "$YESNO" == "y" ]] && ssh-keygen -q -f /home/"$PRIMARYUSER"/.ssh/id_rsa -p -P passphrase -N ''>/dev/null 2>&1
	chown -R "$PRIMARYUSER":"$PRIMARYUSER" /home/"$PRIMARYUSER"/.ssh
fi
[[ -n $DEBUG ]] && echo "passed '$PRIMARYUSER' key install"

# establish root key
if [[ -z $(ls /root/.ssh/id_rsa 2>&-) ]]; then
	# we have to set a real passphrase here, trying to set blank does not work
	docmd "Setup root private/public key (for mirroring)" "ssh-keygen -q -f /root/.ssh/id_rsa -C autocreated-by-timedicer-server-setup-for-root@$(uname -n) -N passphrase"
	# now we can change passphrase to blank as direct command
	[[ "$YESNO" == "y" ]] && ssh-keygen -q -f /root/.ssh/id_rsa -p -P passphrase -N ''>/dev/null 2>&1
fi
[[ -n $DEBUG ]] && echo "passed root key install"

# setup dropbear
if [[ -n $LUKSROOT && -d /etc/dropbear-initramfs && -s /home/"$PRIMARYUSER"/.ssh/id_rsa.pub ]]; then
	# add primary user public key to dropbear authorized_keys (unless already there)
	# shellcheck disable=SC2016
	docmd "Setup boot-time ssh to allow remote entering of encryption passphrase" 'touch /etc/dropbear-initramfs/authorized_keys && chmod 644 /etc/dropbear-initramfs/authorized_keys && grep -qf /home/"$PRIMARYUSER"/.ssh/id_rsa.pub /etc/dropbear-initramfs/authorized_keys 2>/dev/null || cat /home/"$PRIMARYUSER"/.ssh/id_rsa.pub >>/etc/dropbear-initramfs/authorized_keys'
	# new code 17 Jan 2018
	if [[ $YESNO == y ]] && ! grep -q "^GRUB_CMDLINE_LINUX=.*ip=" /etc/default/grub; then
		# find ip devices - new code 28 May 2019
		mapfile -t IPDEVS < <(ip link show|awk -F"[: ]" '{print $3}'|sed '/lo/d;/^$/d')
		if [[ ${#IPDEVS[@]} -gt 1 ]]; then
			echo -n "Ethernet devices:"
			for (( i=0; i<${#IPDEVS[@]}; i++ )); do echo "  $i: ${IPDEVS[$i]}"; done
			unset REPLY
			while [[ ! "$REPLY" =~ /^[0-9]$/ && $REPLY -ge ${#IPDEVS[@]} ]]; do
				read -rp "Select ethernet device for boot ssh access (0-$((i-1))): "
			done
		else
			REPLY=0
		fi
		# we use the first non-lo device (in theory this could be wrong)
		# this is skipped if grub already has an ip entry in its GRUB_CMDLINE_LINUX line
		if grep -q "^GRUB_CMDLINE_LINUX=" /etc/default/grub; then
			# grub already has GRUB_CMDLINE_LINUX line but without ip= entry
			# shellcheck disable=SC2016
			docmd "Amend GRUB_CMDLINE_LINUX in initramfs to enable DHCP for ${IPDEVS[$REPLY]}" 'sed -i "s/^\(GRUB_CMDLINE_LINUX=\"\)/\1ip=:::::${IPDEVS[$REPLY]}:dhcp /;s/ \"$/\"/" /etc/default/grub'
		else
			# grub needs a new GRUB_CMDLINE_LINUX line
			# shellcheck disable=SC2016
			docmd "Add GRUB_CMDLINE_LINUX to initramfs to enable DHCP for ${IPDEVS[$REPLY]}" 'echo GRUB_CMDLINE_LINUX="ip=:::::${IPDEVS[$REPLY]}:dhcp" >>/etc/default/grub'
		fi
		update-grub
	fi
	[[ $YESNO == y ]] && docmd "Update initramfs" 'update-initramfs -u -k all 2>&1|grep -v "defines no arrays"' && [[ $YESNO == y ]] && LUKSMESSAGE="\n\nRemote boot-time ssh access for entry of the encryption passphrase has been enabled at port 22 using local user ${PRIMARYUSER}'s private key .ssh/id_rsa: login with ssh to root@$(hostname -I) and run cryptroot-unlock. For this to work with other private key(s) please add the appropriate public key(s) to /etc/dropbear-initramfs/authorized_keys (each on a separate line) and then do:\nsudo update-initramfs -u -k all"
fi

# check if rdiff-backup is already installed and what version, then install rdiff-backup
RB_VER=$(hash rdiff-backup 2>/dev/null && rdiff-backup --version|cut -d" " -f2)
if [[ -n $RB_VER ]]; then
	echo "rdiff-backup v$RB_VER already installed"
	# we want version 2.0.5 or later... [26 Nov 2020]
	[[ ${RB_VER//./} -lt 205 ]] && echo "  - but v$RB_VER is too old, problems may occur unless updated!"
	docmd "Reinstall rdiff-backup" "echo -n"
else
	docmd "Install rdiff-backup" "echo -n"
fi
if [[ "$YESNO" == "y" ]]; then
	[[ -n $RB_VER ]] && apt-get -qq remove rdiff-backup # remove the existing version
	# 26 Nov 2020: add the repository that keeps the latest builds of rdiff-backup
	if [[ $MAJOR_RELEASE -lt 22 ]]; then
		add-apt-repository -uy ppa:rdiff-backup/rdiff-backup-backports
	fi
	apt-get -qqy install rdiff-backup && [[ -n $DEBUG ]] && echo "passed rdiff-backup installation"
	RB_VER=$(hash rdiff-backup 2>/dev/null && rdiff-backup --version|cut -d" " -f2)
	if [[ -n $RB_VER ]]; then
		echo " - version $RB_VER now installed"
	else
		echo " - unable to confirm correct installation, please check later"
	fi
fi

# install rdiffweb
docmd "Install/re-install Rdiffweb" "echo -n" "/dev/null" #$RDIFFWEBFORCE
if [[ "$YESNO" == "y" ]]; then
	echo -n "Installing Rdiffweb, please wait: "
	# adapted [13 Jan 2023] from instructions at:
	#   https://nexus.ikus-soft.com/repository/archive/rdiffweb/2.5.6/doc/installation.html#option-1-debian-ubuntu-repository
	# shellcheck disable=SC2015
	apt-get -qqy install lsb-release && echo -n "." && \
	curl -sL https://www.ikus-soft.com/archive/rdiffweb/public.key | gpg --dearmor >/usr/share/keyrings/rdiffweb-keyring.gpg 2>/dev/null && echo -n "." && \
	echo "deb [arch=amd64 signed-by=/usr/share/keyrings/rdiffweb-keyring.gpg] https://nexus.ikus-soft.com/repository/apt-release-$(lsb_release -sc)/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/rdiffweb.list && echo -n "." && \
	apt-get -eany -qq update && echo -n "." && \
	apt-get -qqy install rdiffweb && echo -n "." && \
	$SET_COL && echo "[ OK ]" || { $SET_COL; echo "[FAIL]"; }
	# [ ?? install sqlite3 for use by newuser-request.sh [14 Nov 2018] ]

	if [[ -s /etc/rdiffweb/rdw.conf ]]; then
		# a little custom configuration of the rdiffweb webpage [12 Jan 2023]
		echo -n "Customizing Rdiffweb a little"
		EditOrAdd "^brand-favicon=" "favicon=/var/www/html/favicon.ico" "/etc/rdiffweb/rdw.conf"; echo -n "."
		EditOrAdd "^brand-header-logo=" "header-logo=/var/www/html/timedicer_die.png" "/etc/rdiffweb/rdw.conf"; echo -n "."
		EditOrAdd "^brand-header-name=" "header-name=Rdiffweb on $HOSTNAME" "/etc/rdiffweb/rdw.conf"; echo -n "."
		EditOrAdd "^server-host=" "server-host=0.0.0.0" "/etc/rdiffweb/rdw.conf"
		$SET_COL; echo "[ OK ]"
		systemctl reload-or-restart rdiffweb
		for ((WAIT=1; WAIT<=4; WAIT++)); do
		  echo -n "Checking Rdiffweb homepage (loop $WAIT): "
		  # test that rdiffweb homepage can be reached
			sleep 5s
		 	[[ $(wget -q -O - "http://$(echo "$IP4ADDRESSES"|cut -d" " -f1):8080"|grep -c rdiffweb) -ge 1 ]] && { $SET_COL; echo "[ OK ]"; break; }
			$SET_COL; [[ $WAIT -lt 4 ]] && echo "[....]" || echo "[FAIL]"
		done
		[[ $WAIT -gt 4 ]] && echo -e "Unable to access rdiffweb homepage" && RDWFAIL="y"
	fi
fi

command -v incrond >/dev/null 2>&1 || docmd "Install and setup incron (for responding to new user requests)" 'apt-get -qq install incron'
if [[ -d /var/spool/incron ]] && ! grep -Fq newuser-request /var/spool/incron/root 2>/dev/null; then
	docmd "Add trigger for new user requests to root incrontab" 'echo "/var/www/.adduser IN_MODIFY /opt/newuser-request.sh" >>/var/spool/incron/root' "/dev/null" y
	EditOrAdd "^root$" "root" "/etc/incron.allow"
	systemctl reload-or-restart incron
	# silently remove any old line in crontab which calls newuser-request.sh (every minute) [19 Dec 2018]
	sed -i 's/^\([^#].*newuser-request[.]sh\)$/#\1/' /etc/crontab
fi

# install options to crontab
docmd "Install/Update options to crontab" "echo -n"
if [[ $YESNO == "y" ]]; then
	apt-get -qqy install anacron
	EditOrAdd "^MAILTO=" "MAILTO=$MYEMAIL" /etc/crontab
	EditOrAdd "lvm-usage\.sh" "24 14 \* \* \* root /opt/lvm-usage.sh -fq 70" /etc/crontab
fi

[[ -z $MYEMAIL ]] && { echo -e "To check and (if required) update crontab or to send test email, rerun $THIS, specifying as 1st parameter an email address to receive notices from TimeDicer Server.\nAll other operations were completed."|fold -sw"$COLUMNS"; exit 0; }

docmd "Send test email to $MYEMAIL" "echo -n"
[[ "$YESNO" == "y" ]] && MAILMESSAGE=y
MAILTAIL="\n\nDo not reply to this email, you will not receive a response.\n\nThank you for using TimeDicer."

if [[ "$ERREDTOTAL" -eq 0 && -n $FORCE ]]; then
	MESSAGE="\nTimeDicer Server '$(uname -n)' has been configured. You can now access your TimeDicer Server from a browser on the same network at:\n"
	for i in $IP4ADDRESSES; do
		MESSAGE="${MESSAGE}http://$i\t\t(to add users)"
		if [[ -z $RDWFAIL ]]; then
			MESSAGE="${MESSAGE}\nhttp://$i:8080\t(rdiffweb - for recovering files)"
		else
			MESSAGE="${MESSAGE}\n- but rdiffweb installation may not have succeeded, sorry"
		fi
	done
	echo -e "$MESSAGE"|fold -sw"$COLUMNS"
	[[ -n $MAILMESSAGE ]] && echo -e "Subject: TimeDicer Server $(uname -n) at $IP4ADDRESSES: Successful Configuration\n\n$MESSAGE$LUKSMESSAGE$MAILTAIL"|sendmail "$MYEMAIL"
else
	[[ -n $MAILMESSAGE ]] && echo -e "Subject: TimeDicer Server at $IP4ADDRESSES: Test email\n\nThis email was generated by $THIS running on TimeDicer Server $(uname -n); if you received it, TimeDicer Server $(uname -n) at $IP4ADDRESSES can now send emails to you.$MAILTAIL"|sendmail "$MYEMAIL"
fi
echo -e "$LUKSMESSAGE"|fold -sw"$COLUMNS"
exit 0
# shellcheck disable=all
{
# example code to run this script with questions (alter my@emailaddress first):
cd /opt
sudo wget https://www.timedicer.co.uk/server/timedicer-server-setup.sh
sudo chmod 744 timedicer-server-setup.sh
sudo /opt/timedicer-server-setup.sh my@emailaddress

# example code to run this script without questions (alter my@emailaddress first):
cd /opt
sudo wget https://www.timedicer.co.uk/server/timedicer-server-setup.sh
sudo chmod 744 timedicer-server-setup.sh
sudo /opt/timedicer-server-setup.sh -f my@emailaddress
}