#!/bin/sh
# The first start of a machine that just got the package. Everything a person
# would otherwise type after installing, done once, and written down in
# /root/nuxpbx-welcome.
#
# On Alpine it runs from the nuxpbx-firstboot service on the first boot of an
# installed disk; on Debian it runs from the package's postinst. Runs as
# root. Every step is safe to run again after a failure: what exists is left
# alone, what is missing is made.
set -eu

marker=/etc/nuxpbx/installed
env_file=/etc/nuxpbx/nuxpbx.env
welcome=/root/nuxpbx-welcome

if [ -e "$marker" ]; then
	echo "already set up"
	exit 0
fi

if command -v rc-service >/dev/null 2>&1; then
	os=alpine
elif command -v systemctl >/dev/null 2>&1; then
	os=debian
else
	echo "neither OpenRC nor systemd: set this machine up by hand, see the README" >&2
	exit 1
fi

# Enable now and after every boot, whichever init this is.
service_up() {
	case "$os" in
		alpine)
			rc-update add "$1" default >/dev/null 2>&1 || true
			rc-service "$1" restart >/dev/null 2>&1 || rc-service "$1" start
			;;
		debian)
			if [ -d /run/systemd/system ]; then
				systemctl enable "$1" >/dev/null 2>&1 || true
				systemctl restart "$1"
			elif [ -x "/etc/init.d/$1" ]; then
				# No systemd running (a container, a chroot): the init
				# scripts still start things, and nuxpbx has none, so it
				# is started by hand below.
				"/etc/init.d/$1" restart >/dev/null 2>&1 || "/etc/init.d/$1" start
			else
				echo "no init to start $1 with; start it yourself: $1" >&2
			fi
			;;
	esac
}

random() {
	head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n'
}

# The name this phone system answers to. Phones register against it, so it is
# the machine's own name: what the person typed during the installation,
# read from the file rather than resolved, because Alpine's hosts file
# would answer with a made up domain.
sip_domain=$(cat /etc/hostname 2>/dev/null || hostname)
case "$sip_domain" in
	""|localhost|localhost.localdomain) sip_domain="pbx.local" ;;
esac

# 0. Where updates come from. The disk was installed from the ISO alone, so
#    the package list points at a medium that is no longer there; Alpine's
#    own mirror and the phone system's repository replace it.
if [ "$os" = alpine ]; then
	branch="v$(cut -d. -f1,2 /etc/alpine-release)"
	if grep -q '^#\?/media/' /etc/apk/repositories 2>/dev/null; then
		cat >/etc/apk/repositories <<REPOS
https://dl-cdn.alpinelinux.org/alpine/$branch/main
https://dl-cdn.alpinelinux.org/alpine/$branch/community
REPOS
	fi
	grep -q 'apt.team-nifty.com/alpine' /etc/apk/repositories 2>/dev/null \
		|| echo "https://apt.team-nifty.com/alpine/$branch/nuxpbx" >>/etc/apk/repositories
fi

if [ "$os" = debian ] && [ ! -s /etc/apt/sources.list.d/team-nifty.list ]; then
	if curl -fsSL https://apt.team-nifty.com/repository-key.gpg | gpg --dearmor -o /usr/share/keyrings/team-nifty.gpg 2>/dev/null; then
		echo "deb [signed-by=/usr/share/keyrings/team-nifty.gpg] https://apt.team-nifty.com/ stable main" >/etc/apt/sources.list.d/team-nifty.list
	else
		echo "could not fetch the repository key from apt.team-nifty.com; updates will need it, see nuxpbx-update" >&2
	fi
fi

# 1. PostgreSQL, on loopback, with a database of its own. Alpine hands over
#    an empty package and has to be asked for a cluster; Debian made one.
if [ "$os" = alpine ] && [ ! -s "$(ls -d /var/lib/postgresql/*/data 2>/dev/null | head -1)/PG_VERSION" ]; then
	rc-service postgresql setup
fi
service_up postgresql
for _ in $(seq 30); do
	su postgres -c "psql -q -c 'SELECT 1'" >/dev/null 2>&1 && break
	sleep 1
done

db_password=$(random)
if su postgres -c "psql -tAq -c \"SELECT 1 FROM pg_roles WHERE rolname = 'nuxpbx'\"" | grep -q 1; then
	su postgres -c "psql -q -c \"ALTER ROLE nuxpbx WITH LOGIN PASSWORD '$db_password'\""
else
	su postgres -c "psql -q -c \"CREATE ROLE nuxpbx WITH LOGIN PASSWORD '$db_password'\""
fi
if ! su postgres -c "psql -tAq -c \"SELECT 1 FROM pg_database WHERE datname = 'nuxpbx'\"" | grep -q 1; then
	su postgres -c "psql -q -c 'CREATE DATABASE nuxpbx OWNER nuxpbx'"
fi
database="host=127.0.0.1 port=5432 user=nuxpbx password=$db_password dbname=nuxpbx"

# 2. Asterisk's own files, before nuxpbx writes the ones it owns into the same
#    directory. Alpine ships Asterisk with no configuration at all and its
#    data under /usr/share/asterisk, which the sample asterisk.conf does not
#    say, so two files of ours go in; Debian's own are right as they are.
#    unixODBC reads the driver list and the data sources from /etc; the data
#    source is the one nuxpbx renders, so it is a link.
if [ "$os" = alpine ]; then
	install -Dm644 /usr/share/nuxpbx/asterisk/asterisk.conf /etc/asterisk/asterisk.conf
	install -Dm644 /usr/share/nuxpbx/asterisk/modules.conf /etc/asterisk/modules.conf
fi
driver=$(find /usr/lib -name 'psqlodbcw.so' 2>/dev/null | head -1)
[ -n "$driver" ] || { echo "the PostgreSQL ODBC driver (psqlodbcw.so) is not installed" >&2; exit 1; }
if ! grep -q '^\[PostgreSQL\]' /etc/odbcinst.ini 2>/dev/null; then
	printf '\n[PostgreSQL]\nDescription = PostgreSQL ODBC, for Asterisk\nDriver = %s\n' "$driver" >>/etc/odbcinst.ini
fi
ln -sf /etc/asterisk/odbc.ini /etc/odbc.ini
mkdir -p /var/lib/asterisk /var/log/asterisk /var/spool/asterisk/monitor /var/spool/asterisk/voicemail \
	/usr/share/asterisk/sounds/nuxpbx /var/lib/nuxpbx /etc/nuxpbx

# 3. The phone system itself: schema, first administrator, Asterisk's files,
#    and the environment file with the generated secrets.
admin_password=$(head -c 24 /dev/urandom | base64 | tr -dc 'A-Za-z0-9' | head -c 20)
printf '%s' "$admin_password" | NUXPBX_DB="$database" nuxpbx install \
	--sip-domain "$sip_domain" \
	--admin admin \
	--db-host 127.0.0.1 --db-port 5432 --db-name nuxpbx --db-user nuxpbx --db-password "$db_password" \
	--asterisk-config /etc/asterisk \
	--env-file "$env_file" \
	--service-file none

# What the installer could not know about this machine: the interface is the
# whole point of the box and listens on every address, plain http because
# there is no certificate yet, and the browser interface and the sounds live
# where the package put them. The https line is there, commented out, so the
# person who opens this file sees where https is switched on.
sed -i '/^NUXPBX_HTTP_ADDR=/d' "$env_file"
cat >>"$env_file" <<ENV
NUXPBX_HTTP_ADDR=0.0.0.0:8080
NUXPBX_COOKIE_SECURE=false
# For https from the binary itself: uncomment, and set NUXPBX_TLS_DOMAIN (Let's Encrypt) or NUXPBX_TLS_CERT and NUXPBX_TLS_KEY (PEM files).
#NUXPBX_HTTPS_ADDR=0.0.0.0:443
NUXPBX_UI_DIR=/usr/share/nuxpbx/ui
NUXPBX_SOUNDS=/usr/share/asterisk/sounds/nuxpbx
NUXPBX_RECORDINGS=/var/spool/asterisk/monitor
NUXPBX_VOICEMAIL=/var/spool/asterisk/voicemail
NUXPBX_LANGUAGE=${NUXPBX_LANGUAGE:-de}
ENV
chown nuxpbx:nuxpbx "$env_file"
chmod 600 "$env_file"

# 4. Who may write where. nuxpbx writes Asterisk's configuration and the
#    uploaded sounds, and sweeps old recordings; Asterisk writes recordings
#    and voicemail. Group ownership does that without either running as root.
chgrp -R asterisk /etc/asterisk
chmod 2775 /etc/asterisk
chmod g+w /etc/asterisk/*
chown -R nuxpbx:asterisk /usr/share/asterisk/sounds/nuxpbx
chmod 2775 /usr/share/asterisk/sounds/nuxpbx
chown -R asterisk:asterisk /var/lib/asterisk /var/log/asterisk /var/spool/asterisk
chmod 2775 /var/spool/asterisk/monitor /var/spool/asterisk/voicemail
chown nuxpbx:nuxpbx /var/lib/nuxpbx

# 5. Up, now and after every boot. On Alpine that includes busybox crond,
#    which is in the base system and off by default, and which is what runs
#    the daily backup in /etc/periodic/daily.
service_up asterisk
service_up nuxpbx
if [ "$os" = alpine ]; then
	service_up crond
fi

touch "$marker"

address=$(ip -4 -o addr show scope global 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -1)
cat >"$welcome" <<TEXT
This machine is a phone system.

  Open http://${address:-<this machine's address>}:8080 in a browser.
  Sign in as: admin
  Password:   $admin_password

Change that password first. It was generated here and written nowhere else
but this file, which only root can read. Phones register against
$sip_domain; give them an extension from the interface.

The interface speaks plain http until it has a certificate. For https,
uncomment NUXPBX_HTTPS_ADDR in /etc/nuxpbx/nuxpbx.env and set
NUXPBX_TLS_DOMAIN, or NUXPBX_TLS_CERT and NUXPBX_TLS_KEY; it says how there.

To update the phone system and the operating system:  nuxpbx-update
TEXT
chmod 600 "$welcome"
cat >/etc/motd <<TEXT

  This machine is a phone system. The first administrator and the
  password are in /root/nuxpbx-welcome.

TEXT
# The ISO's login screen said to run the installer; this machine is past that.
if [ "$os" = alpine ]; then
	cat >/etc/issue <<TEXT

  nuxpbx phone system, $sip_domain. Administration on http://${address:-<this machine>}:8080

TEXT
fi
echo "set up. $sip_domain answers on http://${address:-?}:8080, welcome in $welcome"
