#!/bin/bash

#
# AirVPN suite switch user
#
# Version 1.2.0 - 3 April 2025
#
# Written by ProMIND
#

if [ "$BASH_VERSION" = ""  ]; then
    echo "ERROR: airsu requires bash"

    exit 1
fi

XID=$(id -u)
AIRVPNUSER="airvpn"
VERSION="1.2.0"
RELEASEDATE="3 April 2025"
ENVLIST=""
EXPORTLIST=""
DBUSLAUNCHPATH=$(which dbus-launch)
DBUSENV="no"
XAUTH=""
RCFILE=""
DISPLAY_IS_WAYLAND="no"

T1="Change the effective user ID and group ID to that of <user> and properly set the environment in order to allow application execution by using cuckoo, in particular graphical ones. If <user> is not given, ${AIRVPNUSER} is assumed."
T2="Please note the environment for the switched user is replicated from that of the calling user."
T3="Use 'cuckoo' to run applications with network traffic outside the VPN tunnel"

if [ "${DBUSLAUNCHPATH}" != "" ]; then
    DBUSENV="yes"
else
    DBUSENV="no"
fi

if [ -v SHELL ]; then
    SUSHELL=${SHELL}
else
    SUSHELL=$(which sh)
fi

show_help()
{
    echo "AirVPN Suite switch user version ${VERSION} - ${RELEASEDATE}"
    echo
    echo "airsu [user] [options]"
    echo
    echo "Options:"
    echo

    if [[ "${DBUSLAUNCHPATH}" != "" && "${SUSHELL}" =~ "bash" ]]; then
        echo "-d, --dbus        Launch and set D-Bus environment (default)"
        echo "-n, --no-dbus     Do not launch and set D-Bus environment"
    fi

    echo "-h, --help        This help screen"
    echo "-v, --version     Show version"
    echo
    echo "${T1}"
    echo
    echo "${T2}"
    echo
    echo "${T3}"
}

show_version()
{
    echo "AirVPN Suite switch user version ${VERSION} - ${RELEASEDATE}"
    echo
    echo "${T1}"
    echo
    echo "${T2}"
    echo
    echo "${T3}"
}

for i in "$@"; do
  case $i in
    -h|--help)
        show_help

        exit 0
    ;;

    -v|--version)
        show_version

        exit 0
    ;;

    -d|--dbus)
        if [ "${DBUSLAUNCHPATH}" != "" ]; then
            DBUSENV="yes"
        else
            echo "ERROR: it seems D-Bus is not available in this system"

            exit 1
        fi

        shift
    ;;

    -n|--no-dbus)
        if [ "${DBUSLAUNCHPATH}" != "" ]; then
            DBUSENV="no"
        else
            echo "ERROR: it seems D-Bus is not available in this system"

            exit 1
        fi

        shift
    ;;

    -*|--*)
        echo "Unknown option ${i}"

        exit 1
    ;;

    *)
      ;;
  esac
done

if [ ${XID} -eq 0 ]; then
    echo "ERROR: airsu cannot be run as root"

    exit 1
fi

if [ "${1}" != ""  ]; then
    AIRVPNUSER=${1}
fi

id "${AIRVPNUSER}" >/dev/null 2>&1

if [ "$?" = "1"  ]; then
    echo "ERROR: user ${AIRVPNUSER} does not exist"

    exit 1
fi

getent group root | grep $(whoami) >/dev/null 2>&1

if [ "$?" = "0" ]; then
    echo "ERROR: airsu cannot be run by users belonging to the root group"

    exit 1
fi

if ! id -nG "${AIRVPNUSER}" | grep -qw "airvpn"; then
    echo "ERROR: user ${AIRVPNUSER} does not belong to the airvpn group"

    exit 1
fi

if [[ ! -v DISPLAY && ! -v WAYLAND_DISPLAY ]]; then
    echo "ERROR: current user does not have a graphic desktop or environment defined. Use 'su' command. Graphic applications will not however run."

    exit 1
fi

# ACL

if [ -d "/run/user/${XID}" ]; then
    setfacl -m ${AIRVPNUSER}:rwx -- /run/user/${XID}
fi

if [ -d "/run/user/${XID}/dconf" ]; then
    setfacl -m ${AIRVPNUSER}:rwx -- /run/user/${XID}/dconf
fi

if [ -f "/run/user/${XID}/dconf/user" ]; then
    setfacl -m ${AIRVPNUSER}:rwx -- /run/user/${XID}/dconf/user
fi

if [ -S "/run/user/${XID}/bus" ]; then
    setfacl -m ${AIRVPNUSER}:rwx -- /run/user/${XID}/bus
fi

if [ -S "/run/user/${XID}/wayland-0" ]; then
    setfacl -m ${AIRVPNUSER}:rwx -- /run/user/${XID}/wayland-0
fi

if [ -f "/run/user/${XID}/wayland-0.lock" ]; then
    setfacl -m ${AIRVPNUSER}:rw -- /run/user/${XID}/wayland-0.lock
fi

# Environment variables

ENVLIST="DISPLAY"
EXPORTLIST="DISPLAY=${DISPLAY}"

if [ -v WAYLAND_DISPLAY ]; then
    ENVLIST="${ENVLIST},WAYLAND_DISPLAY"
    EXPORTLIST="${EXPORTLIST} WAYLAND_DISPLAY=${WAYLAND_DISPLAY}"
    DISPLAY_IS_WAYLAND="yes"
else
    DISPLAY_IS_WAYLAND="no"
fi

if [ -v DBUS_SESSION_BUS_ADDRESS ]; then
    ENVLIST="${ENVLIST},DBUS_SESSION_BUS_ADDRESS"
    EXPORTLIST="${EXPORTLIST} DBUS_SESSION_BUS_ADDRESS=${DBUS_SESSION_BUS_ADDRESS}"
fi

if [ -v XDG_RUNTIME_DIR ]; then
    ENVLIST="${ENVLIST},XDG_RUNTIME_DIR"
    EXPORTLIST="${EXPORTLIST} XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR}"
fi

if [ -v XDG_MENU_PREFIX ]; then
    ENVLIST="${ENVLIST},XDG_MENU_PREFIX"
    EXPORTLIST="${EXPORTLIST} XDG_MENU_PREFIX=${XDG_MENU_PREFIX}"
fi

if [ -v XDG_SESSION_DESKTOP ]; then
    ENVLIST="${ENVLIST},XDG_SESSION_DESKTOP"
    EXPORTLIST="${EXPORTLIST} XDG_SESSION_DESKTOP=${XDG_SESSION_DESKTOP}"
fi

if [ -v XDG_CURRENT_DESKTOP ]; then
    ENVLIST="${ENVLIST},XDG_CURRENT_DESKTOP"
    EXPORTLIST="${EXPORTLIST} XDG_CURRENT_DESKTOP=${XDG_CURRENT_DESKTOP}"
fi

if [ -v XDG_SESSION_TYPE ]; then
    ENVLIST="${ENVLIST},XDG_SESSION_TYPE"
    EXPORTLIST="${EXPORTLIST} XDG_SESSION_TYPE=${XDG_SESSION_TYPE}"
fi

if [ -v XDG_DATA_DIRS ]; then
    ENVLIST="${ENVLIST},XDG_DATA_DIRS"
    EXPORTLIST="${EXPORTLIST} XDG_DATA_DIRS=${XDG_DATA_DIRS}"
fi

if [ -v XDG_SESSION_CLASS ]; then
    ENVLIST="${ENVLIST},XDG_SESSION_CLASS"
    EXPORTLIST="${EXPORTLIST} XDG_SESSION_CLASS=${XDG_SESSION_CLASS}"
fi

if [ -v DESKTOP_SESSION ]; then
    ENVLIST="${ENVLIST},DESKTOP_SESSION"
    EXPORTLIST="${EXPORTLIST} DESKTOP_SESSION=${DESKTOP_SESSION}"
fi

if [ -v GNOME_SETUP_DISPLAY ]; then
    ENVLIST="${ENVLIST},GNOME_SETUP_DISPLAY"
    EXPORTLIST="${EXPORTLIST} GNOME_SETUP_DISPLAY=${GNOME_SETUP_DISPLAY}"
fi

if [ -v GNOME_TERMINAL_SCREEN ]; then
    ENVLIST="${ENVLIST},GNOME_TERMINAL_SCREEN"
    EXPORTLIST="${EXPORTLIST} GNOME_TERMINAL_SCREEN=${GNOME_TERMINAL_SCREEN}"
fi

if [ -v GNOME_TERMINAL_SERVICE ]; then
    ENVLIST="${ENVLIST},GNOME_TERMINAL_SERVICE"
    EXPORTLIST="${EXPORTLIST} GNOME_TERMINAL_SERVICE=${GNOME_TERMINAL_SERVICE}"
fi

if [ -v MUTTER_DISPLAY ]; then
    ENVLIST="${ENVLIST},MUTTER_DISPLAY"
    EXPORTLIST="${EXPORTLIST} MUTTER_DISPLAY=${MUTTER_DISPLAY}"
fi

if [ "${DISPLAY_IS_WAYLAND}" = "no"  ]; then
    which xauth >/dev/null 2>&1

    if [ "$?" = "0"  ]; then
        XAUTH=$(xauth list ${DISPLAY} | head -n 1)

        echo $XAUTH | grep -i "unix${DISPLAY}" >/dev/null 2>&1

        if [ "$?" = "1"  ]; then
            which sed >/dev/null 2>&1

            if [ "$?" = "0"  ]; then
                XAUTH=$(echo $XAUTH | sed -e "s/unix:/unix${DISPLAY}/g")

                echo $XAUTH | grep -i "unix${DISPLAY}" >/dev/null 2>&1

                if [ "$?" = "0"  ]; then
                    echo "WARNING: Fixed malformed xauth main entry"
                else
                    echo "ERROR: Malformed xauth list. Display not found in main entry."

                    exit 1
                fi
            else
                echo "ERROR: Malformed xauth list. Display not found in main entry. (sed not found)"

                exit 1
            fi
        fi
    else
        XAUTH=""
    fi
else
    XAUTH=""
fi

if [[ "${DBUSENV}" = "yes" || "${XAUTH}" != "" ]] && [[ "${SUSHELL}" =~ "bash" ]]; then
    RCFILE="--rcfile <(echo '. ~/.bashrc; "

    if [ "${DBUSENV}" = "yes" ]; then
        RCFILE="${RCFILE}export \$(${DBUSLAUNCHPATH} 2>/dev/null)"
    fi

    if [[ "${XAUTH}" != "" && "${DISPLAY_IS_WAYLAND}" = "no" ]]; then
        if [ "${DBUSENV}" = "yes" ]; then
            RCFILE="${RCFILE}; "
        fi

        RCFILE="${RCFILE}touch ~/.Xauthority; xauth add ${XAUTH}"
    fi

    RCFILE="${RCFILE}')"
else
    RCFILE=""
fi

if [[ ${XAUTH} = "" && "${DISPLAY_IS_WAYLAND}" = "no" ]]; then
    echo "WARNING: xauth command not found. X11 environment will not be properly set."
fi

su --login --whitelist-environment="${ENVLIST}" --command="export ${EXPORTLIST}; ${SUSHELL} ${RCFILE}" --pty ${AIRVPNUSER}
