#!/bin/bash
# Copyright (c) 2016 Xavier G. <xavier.prooties@kindwolf.org>
# This program is free software. It comes without any warranty, to the extent
# permitted by applicable law. You can redistribute it and/or modify it under
# the terms of the Do What The Fuck You Want To Public License, Version 2, as
# published by Sam Hocevar. See http://www.wtfpl.net/ for more details.

function exit_with_message {
	echo "${2}"
	exit "${1}"
}

function mk_dir {
	local path="${1}"
	local mode="${2}"

	mkdir -p "${path}" || \
		exit_with_message 126 \
			"Unable to create the following directory, aborting: ${path}"

	chmod "${mode}" "${path}" || \
		exit_with_message 125 \
			"Unable to chmod the following directory, aborting: ${path}"
}

# This script may rely on its invoked name to fetch extra configuration
# directives.
invoked_name="$(basename "${0}")"

# By default, invoke "proot" and "firefox":
proot_path="$(which 'proot')"
browser_path='firefox'

# Base directory for our crap
work_dir_base="/run/user/$(id -u)/browser-wrapper"
mk_dir "${work_dir_base}" 0700
work_dir="$(mktemp --directory "${work_dir_base}/$$.XXXXXXXX")" || \
	exit_with_message 110 "Unable to create working directory, aborting."

# Directory to replace /home
slash_home_dir="${work_dir}/slash_home"
# Directory to replace $HOME
home_dir="${work_dir}/home"
# Directory to replace /tmp
tmp_dir="${work_dir}/tmp"
# Empty dir to replace /dev
dev_dir="${work_dir}/dev"
# Directory for uploads
upload_dir="${HOME}/Uploads"
# Directory for downloads
download_dir="${HOME}/Downloads"

for directory in {{slash_,}home,tmp,{up,down}load,dev}_dir; do
	mk_dir "${!directory}" 0700
done

# Leverage proot to make the browser believe that:
#  - some directories located right under the root, such as /boot, /root and
#    /srv, are absent as they are not expected to be of any use to any web
#    browser.
root_dir_options="
	--bind=/dev/null:/boot \
	--bind=/dev/null:/root \
	--bind=/dev/null:/srv"
#   - /home is either empty or contains only our home
slash_home_options="--bind=${slash_home_dir}:/home"
#   - the home directory is almost empty
home_options="--bind=${home_dir}:${HOME}"
#   - its configuration directory is still there
#   - its cache directory is still there
browser_conf_options="
	--bind=${HOME}/.mozilla/firefox:${HOME}/.mozilla/firefox \
	--bind=${HOME}/.cache/mozilla/firefox:${HOME}/.cache/mozilla/firefox"
#   - its download directory is still there
#     (at least the one for English speakers)
download_options="--bind=${download_dir}:${HOME}/Downloads"
#   - a ~/Uploads directory is present
upload_options="--bind=${upload_dir}:${HOME}/Uploads"
#   - ~/.adobe and ~/.macromedia are still there
#     (for the sake of Flash Player and other plugins)
adobe_options="
	--bind=${HOME}/.adobe:${HOME}/.adobe \
	--bind=${HOME}/.macromedia:${HOME}/.macromedia"
#   - various directories related to Java applets are still there
#     (assuming IcedTea-Web >= 1.5 / OpenJDK)
java_options="
	--bind=${HOME}/.config/icedtea-web:${HOME}/.config/icedtea-web
	--bind=${HOME}/.cache/icedtea-web:${HOME}/.cache/icedtea-web
	--bind=${HOME}/.java:${HOME}/.java
	--bind=${HOME}/.local/share/applications/javaws:${HOME}/.local/share/application/javaws"
#   - /tmp is empty
#     (as in: do not even try to mess with the Unix sockets to ssh-agent)
tmp_options="--bind=${tmp_dir}:/tmp"
#   - /sys is simply absent
#     (Firefox tries to reach various things under /sys/devices/system/ but
#     seems to cope without it)
sys_options="--bind=/dev/null:/sys"
#   - /proc is still there, unless you want to hit that very detailed error:
#       too much recursion
#   - /proc/sys is still there, unless you want to hit that charming error:
#       FATAL: error reading `/proc/sys/crypto/fips_enabled' in libgcrypt: Not a directory
proc_options=""
#   - /dev is present yet minimalist
#     /dev/dri is included for the sake of webGL performance and to avoid the
#     following error message:
#       libGL error: failed to open drm device: No such file or directory
dev_options="
	--bind=${dev_dir}:/dev
	--bind=/dev/dri:/dev/dri
	--bind=/dev/null:/dev/null
	--bind=/dev/random:/dev/random
	--bind=/dev/urandom:/dev/urandom"

# Let users override this through "configuration files":
generic_config_path="${HOME}/.config/browser-wrapper/main.conf"
[ -f "${generic_config_path}" ] && source "${generic_config_path}"
specific_config_path="${HOME}/.config/browser-wrapper/${invoked_name}.conf"
[ -f "${specific_config_path}" ] && source "${specific_config_path}"

# Let users override some things through environment variables:
[ -n "${BW_PROOT_PATH}" ] && proot_path="${BW_PROOT_PATH}"
[ -n "${BW_BROWSER_PATH}" ] && browser_path="${BW_BROWSER_PATH}"

if [ -x "${proot_path}" ]; then
	echo "proot is present and will be used to mask some directories"

	echo 'Tips:'
	echo "  Your fake home is ${home_dir}"
	echo "  Your fake /tmp is ${tmp_dir}"
	echo '  Do you need to upload files?'
	echo "    Put them in ${upload_dir}"
	echo '  Do you need to download files?'
	echo "    They should end up in ${download_dir}"

	"${proot_path}" \
		${pre_options} \
		${root_dir_options} \
		${slash_home_options} \
		${home_options} \
		${browser_conf_options} \
		${download_options} \
		${upload_options} \
		${adobe_options} \
		${java_options} \
		${tmp_options} \
		${sys_options} \
		${proc_options} \
		${dev_options} \
		${post_options} \
		--cwd="${HOME}" \
		"${browser_path}" "$@"
else
	echo "proot is missing; the browser will NOT be launched"
	exit 127
fi