#!/bin/sh

set -ue
PATH="/usr/sbin:/sbin:/usr/bin:/bin"
export PATH

assert_stat() {
    local p="$1" st1="$2" st2
    if ! st2="$(stat -c "%U:%G %#a" -- "$p")" || [ "$st1" != "$st2" ]; then
        printf "%s has ownership/mode \"%s\", expected \"%s\"\\n" "$p" "$st2" "$st1" >&2
        exit 1
    fi
}

URL="http://127.0.0.1/"

# staple a random string to the login page so we can check we get the right page later
SEED="$(head -c18 /dev/urandom | base64)"
cat >>"/etc/roundcube/config.inc.php" <<-EOF
	\$config['support_url'] = 'mailto:noreply@example.net?subject=$SEED';
EOF


# use a dedicated username/group, see debian/roundcube-core.README.Debian

# 0. Choose and create suitable user/group names.
username="_roundcube"
groupname="_roundcube"
groupadd --system -- "$groupname"
useradd -p\* -Ng"$groupname" -Md/nonexistent -s/usr/sbin/nologin \
  --system -- "$username"

# 1. Update cronjob and logrotate configuration files.

sed -ri "s/^(([^#[:blank:]]\\s+){5})www-data\\s/\\1$username /" \
    /etc/cron.d/roundcube-core
sed -ri "/^(\\s*create)\\s.*/ s//\\1create 0640 $username adm/" \
    /etc/logrotate.d/roundcube-core

# 2. Transfer ownership (and add a stat override to make it stick on upgrades).
find /etc/roundcube -user  www-data -exec chown -c -- "$username" {} +
find /etc/roundcube -group www-data -exec chgrp -c -- "$groupname" {} +
install -o"$username" -g"$groupname" -m0700 -d /var/lib/roundcube/temp
install -o"$username" -g"adm"        -m0750 -d /var/log/roundcube
dpkg-statoverride --add "$username" "$groupname" 0700 /var/lib/roundcube/temp
dpkg-statoverride --add "$username" "adm"        0750 /var/log/roundcube

# authenticate to MariaDB via SO_PASSCRED/SO_PEERCRED
mysql --defaults-extra-file="/etc/mysql/debian.cnf" <<-EOF
	RENAME USER 'roundcube'@'localhost' TO '$username'@'localhost';
	ALTER USER '$username'@'localhost' IDENTIFIED VIA unix_socket;
EOF

sed -ri "s/^(dbc_dbuser)\\s*=.*/\1='$username'/;
         s/^(dbc_dbpass)\\s*=.*/\1='nopassword'/;
         s/^(dbc_dbport)\\s*=.*/\1=''/;
        " /etc/dbconfig-common/roundcube.conf
dpkg-reconfigure --frontend="noninteractive" -pcritical roundcube-core 2>&1

# 3. Configure the PHP stack so roundcube code is executed by the new user/group.
PHP_VERSION="$(php -r 'echo join(".",[PHP_MAJOR_VERSION, PHP_MINOR_VERSION]);')"
cat >"/etc/php/$PHP_VERSION/fpm/pool.d/roundcube.conf" <<-EOF
	[roundcube]
	user = $username
	group = $groupname
	listen = /run/php/php$PHP_VERSION-fpm@roundcube.sock
	listen.owner = www-data
	listen.group = www-data
	listen.mode = 0600
	pm = ondemand
	pm.max_children = 2

	php_admin_value[upload_tmp_dir] = /var/lib/roundcube/temp
	php_admin_value[open_basedir] = /var/lib/roundcube:/usr/share/roundcube:/etc/roundcube:/var/log/roundcube:/usr/share/php:/usr/share/javascript:/usr/share/nodejs:/usr/share/misc/magic:/dev
EOF
systemctl restart "php$PHP_VERSION-fpm.service"


cat >"/etc/nginx/sites-enabled/default" <<-EOF
	server {
	    listen 127.0.0.1:80 default_server;

	    root /var/lib/roundcube/public_html;
	    server_name _;

	    location = / { index index.php; }
	    location = /index.php {
	        include snippets/fastcgi-php.conf;
	        fastcgi_pass unix:/run/php/php$PHP_VERSION-fpm@roundcube.sock;
	    }

	    location /plugins/           {}
	    location /program/js/        {}
	    location /program/resources/ {}
	    location /skins/             {}
	    location /     { internal; }
	    location ~ /\. { internal; }
	}
EOF
systemctl restart nginx.service # a reload here would race with the below request


assert_stat "/var/lib/roundcube/temp" "$username:$groupname 0700"
assert_stat "/var/log/roundcube" "$username:adm 0750"
assert_stat "/etc/roundcube/config.inc.php" "root:$groupname 0640"
assert_stat "/etc/roundcube/debian-db.php" "root:$groupname 0640"

# make sure we get a working login page (and that it contains the seed)
OUT="$(mktemp --tmpdir)"
if ! code="$(curl -fsS -o "$OUT" -w"%{http_code}" "$URL")" || [ $code -ne 200 ]; then
    echo "Got HTTP code $code (wanted 200)" >&2
    exit 1
fi

if ! grep -Fq -e "$SEED" <"$OUT" || ! grep -Fqw "rcmloginsubmit" <"$OUT"; then
    echo ">>>" >&2
    cat <"$OUT" >&2
    echo "<<<" >&2
    echo "Landing page is lacking seed or login button!" >&2
    exit 1
fi


# make sure the ownership changes stick on upgrades
# XXX would be better to dist-upgrade but failing that we remove + install
DEBIAN_FRONTEND="noninteractive" apt-get remove --no-purge -y roundcube-core 2>&1

# set new debconf options so ucf registers a new file
debconf-set-selections <<-EOF
	roundcube-core roundcube/hosts string foo bar baz
	roundcube-core roundcube/language select en_US
EOF

DEBIAN_FRONTEND="noninteractive" apt-get install --no-install-recommends -y roundcube-core 2>&1
assert_stat "/var/lib/roundcube/temp" "$username:$groupname 0700"
assert_stat "/var/log/roundcube" "$username:adm 0750"
assert_stat "/etc/roundcube/config.inc.php" "root:$groupname 0640"
assert_stat "/etc/roundcube/config.inc.php.ucf-dist" "root:$groupname 0640"
assert_stat "/etc/roundcube/debian-db.php" "root:$groupname 0640"

if ! code="$(curl -fsS -o "$OUT" -w"%{http_code}" "$URL")" || [ $code -ne 200 ]; then
    echo "Got HTTP code $code (wanted 200)" >&2
    exit 1
fi

if ! grep -Fq -e "$SEED" <"$OUT" || ! grep -Fqw "rcmloginsubmit" <"$OUT"; then
    echo ">>>" >&2
    cat <"$OUT" >&2
    echo "<<<" >&2
    echo "Landing page is lacking seed or login button!" >&2
    exit 1
fi


# make sure DES key is preserved on upgrade
if ! key1="$(grep -Fw "des_key" /etc/roundcube/config.inc.php)" || \
        ! key2="$(grep -Fw "des_key" /etc/roundcube/config.inc.php.ucf-dist)" || \
        [ "$key1" != "$key2" ] || [ ${#key1} -lt 48 ]; then
    echo "Key not preserved on upgrade! (${key1-[unset]} != ${key2-[unset]})" >&2
    exit 1
fi


# check isolation
sed -ri "s,(/php$PHP_VERSION-fpm)@roundcube.sock;,\1.sock;," /etc/nginx/sites-enabled/default
rm -f /var/log/nginx/error.log
systemctl restart nginx

if ! curl -fs -o "$OUT" "$URL" || grep -Fqw "rcmloginsubmit" <"$OUT" || \
        ! grep -Fq "Permission denied" </var/log/nginx/error.log || \
        ! grep -Fq "config.inc.php was not found." </var/log/nginx/error.log; then
    echo ">>>" >&2
    cat <"$OUT" >&2
    echo "<<< >>>" >&2
    cat </var/log/nginx/error.log >&2
    echo "<<<" >&2
    echo "Looks like isolation failed" >&2
    exit 1
fi

exit 0
