#!/usr/bin/env python3

# README:
#   In order to use this, you need pythondialog for python3.
#   To install it:
#     sudo apt-get install python3-pip
#     pip3 install --user pythondialog

import locale
from dialog import Dialog
import subprocess
import tempfile
import sys
import os
import psutil
import pwd
import time
import ftplib
import shutil
import hashlib
import stat

locale.setlocale(locale.LC_ALL, 'en_US.UTF-8')

OPEN_CASH_DRAWER_APP = "open_cash_drawer"

BACKUP_DEVICES = ["/dev/sdb1", "/dev/sdc1", "/dev/sdd1"]
BACKUP_TARGET = "/dev/sda"
BACKUP_MOUNT_POINT = "/mnt/backup"
BACKUP_FILE = os.path.join(BACKUP_MOUNT_POINT, "fusion_sda.img.gz")

DEFAULT_FUSION_USER = "client"
MYSQL_ROOT_PASSWORD = "root"
MYSQL_FUSION_DATABASE = "datahub"

FUSION_DEPLOY_DIR = "fusion-linux"
FUSION_ARCHIVE = FUSION_DEPLOY_DIR + ".tar.gz"
FUSION_USER_PASSWORD = "success"
FUSION_START_SCRIPT = "start-fusion.sh"

FUSION_FTP_SERVER = "d.uktech.co"
FUSION_FTP_USR = "fusion@uktech.co"
FUSION_FTP_PWD = "!C],{dUta,k+"
FUSION_FTP_TAGS_DIR = "/tags/"
FUSION_DOWNLOAD_URL_PREFIX = "ftp://{}".format(FUSION_FTP_SERVER)

FUSION_A_SCRIPT_URL = "{}/a".format(FUSION_DOWNLOAD_URL_PREFIX)
FUSION_A_SCRIPT_TARGET = "{}/a".format(os.path.expanduser("~"))
FUSION_A_SCRIPT_TMP_TARGET = "{}/.a.tmp".format(os.path.expanduser("~"))

FUSION_DOWNLOAD_URL = "{}/{}/{}".format(FUSION_DOWNLOAD_URL_PREFIX, "{0}", FUSION_ARCHIVE)

FUSION_BOOT_SCRIPT = "fusion-boot-script"
FUSION_BOOT_SCRIPT_URL = "{}/{}".format(FUSION_DOWNLOAD_URL_PREFIX, FUSION_BOOT_SCRIPT)
FUSION_BOOT_SCRIPT_TARGET = "/{}".format(FUSION_BOOT_SCRIPT)

SCRIPT_DOWNLOAD_TARGET = os.path.basename(__file__)
SCRIPT_DOWNLOAD_URL = "{}/{}".format(FUSION_DOWNLOAD_URL_PREFIX, SCRIPT_DOWNLOAD_TARGET)

FUSION_PIN_VERSION_FILE = "PIN_FUSION_VERSION"

OPENVPN_DOWNLOAD_URL = "{}/openvpn_win/client_vpn/".format(FUSION_DOWNLOAD_URL_PREFIX)
OPENVPN_FILES = [ "client.conf", "client.crt", "client.key", "client.ovpn", "client_ca.crt"]

EBN_EGALAXTOUCH_DIR = "eGTouch_v2.5.4109.L-x"
EBN_EGALAXTOUCH_ARCHIVE = EBN_EGALAXTOUCH_DIR + ".zip"
EBN_EGALAXTOUCH_ARCHIVE_URL = "{}/{}".format(FUSION_DOWNLOAD_URL_PREFIX, EBN_EGALAXTOUCH_ARCHIVE)
EBN_EGALAXTOUCH_CONFIG_APP = "/usr/local/eGTouch32withX/eGTouchU"

EBN_3MTOUCH_DIR = "/opt/twscreen"
EBN_3MTOUCH_ARCHIVE = "twscreen.7.14.1.tar.gz"
EBN_3MTOUCH_ARCHIVE_URL = "{}/{}".format(FUSION_DOWNLOAD_URL_PREFIX, EBN_3MTOUCH_ARCHIVE)
EBN_3MTOUCH_INSTALL_DIR = "/opt"

VNC_PASSWORD = "t2sexe"
VNC_PASSWORD_FILE = "/etc/x11vnc.pass"

# NOTE: Qt5.4 crashes when trying to display the WebView component
# NOTE: Qt5.5 is the minimum Qt5.x version that has the required WebChannel <> WebEngine integration
QT5_PPA = "ppa:beineri/opt-qt55-trusty"
QT5_INSTALL_PACKAGES = "{qt}base {qt}declarative {qt}webengine {qt}webchannel {qt}location {qt}xmlpatterns {qt}serialport {qt}quickcontrols {qt}multimedia".format(qt="qt55")
QT5_UNINSTALL_PACKAGES = "{qt}base"
QT5_LIB_DIR = "/opt/qt55/lib"
QT5_ENV_SH = "/opt/qt55/bin/qt55-env.sh"

BACKGROUND_TITLE = "Fusion Setup"

USER_MANAGEMENT_ID = "1"
USER_MANAGEMENT_MENU = "User Management"
PACKAGE_MANAGEMENT_ID = "2"
PACKAGE_MANAGEMENT_MENU = "System Management"
INSTALL_ALL_ID = "3.1"
INSTALL_ALL_MENU = "Install Fusion (from \"stable\"), enable autologin and reboot"
INSTALL_FUSION8_ID = "3.2"
INSTALL_FUSION8_MENU = "Install Fusion8 (from \"stable-fusion8\"), enable autologin and reboot"
UPGRADE_FUSION_ID = "3.7"
UPGRADE_FUSION_MENU = "Install/Upgrade Fusion"
AUTO_UPGRADE_ENABLE_ID = "3.7.1"
AUTO_UPGRADE_ENABLE_MENU = "Enable auto-upgrade (default)"
AUTO_UPGRADE_DISABLE_ID = "3.7.2"
AUTO_UPGRADE_DISABLE_MENU = "Disable auto-upgrade (pin fusion to installed version)"
CONFIGURE_VNC_ID = "3.10"
CONFIGURE_VNC_MENU = "Configure VNC to start at system boot"
KILL_FUSION_ID = "4"
KILL_FUSION_MENU = "Stop Fusion"
START_FUSION_ID = "5"
START_FUSION_MENU = "Start Fusion"
CONFIGURE_EBN_ID = "7"
CONFIGURE_EBN_MENU = "Install and configure EBN drivers"
MAKE_HDD_BACKUP_ID = "8.1"
MAKE_HDD_BACKUP_MENU = "Backup Linux on USB"
RESTORE_HDD_BACKUP_ID = "8.2"
RESTORE_HDD_BACKUP_MENU = "Restore Linux backup from USB"
INCREASE_VOLUME_ID = "9.1"
INCREASE_VOLUME_MENU = "Increase volume"
DECREASE_VOLUME_ID = "9.2"
DECREASE_VOLUME_MENU = "Decrease volume"
MUTE_VOLUME_ID = "9.3"
MUTE_VOLUME_MENU = "Mute volume"
CUPS_ID="10"
CUPS_MENU="Configure CUPS"
SYSTEM_INFO_ID = "0.1"
SYSTEM_INFO_MENU = "Get some info about the system"
TEST_SCRIPT_ID = "0.2"
TEST_SCRIPT_MENU = "Run a test version of this script"
REBOOT_ID = "*"
REBOOT_MENU = "Reboot system"

BACKGROUND_TITLE = [BACKGROUND_TITLE]

def update_background_title():
    d.set_background_title(" > ".join(BACKGROUND_TITLE))

def push_background_title(title):
    BACKGROUND_TITLE.append(title)
    update_background_title()

def pop_background_title():
    BACKGROUND_TITLE.pop()
    update_background_title()

def print_separator():
    print("-----------------------------------------------")

def print_header(header):
    print_separator()
    print(header)
    print_separator()

def fusion_user_home(fusion_user):
    return os.path.join("/home", fusion_user)

def fusion_deploy_dir(fusion_user):
    return os.path.join(fusion_user_home(fusion_user), FUSION_DEPLOY_DIR)

def fusion_start_script(fusion_user):
    return os.path.join(fusion_deploy_dir(fusion_user), FUSION_START_SCRIPT)

def fusion_exe(fusion_user):
    return os.path.join(fusion_deploy_dir(fusion_user), "exe")

def use_fusion_user_privileges(current_user):
    pw = pwd.getpwnam(current_user)
    os.setegid(pw.pw_gid)
    os.seteuid(pw.pw_uid)

def restore_super_privileges():
    os.seteuid(0)
    os.setegid(0)

def launch_remote_fusion(current_user, script_name):
    process = subprocess.Popen(["sudo", "-u", current_user, "/bin/bash", script_name],
        cwd = fusion_deploy_dir(current_user),
        close_fds = True,
        stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
    time.sleep(0.5) # allow the fusion to start
    process.stdout.close()
    process.stderr.close()
    process.stdin.close()

def print_done():
    print(" Done.")

def make_hdd_backup():
    print_header("Saving fusion backup...")
    success = False
    subprocess.call(["mkdir", "-p", BACKUP_MOUNT_POINT])
    for backup_device in BACKUP_DEVICES:
        if os.path.exists(backup_device):
            if subprocess.call(["mkfs.ntfs", "--fast", backup_device]) != 0:
                print(" ERROR: couldn't format backup device")
                break
            else:
                print(" SUCCESS: formated backup device")

            if subprocess.call(["mount", "-t", "ntfs", backup_device, BACKUP_MOUNT_POINT]) != 0:
                print(" ERROR: couldn't mount backup device")
                break
            else:
                print(" SUCCESS: mounted backup device")

            if subprocess.call("dd if={} bs=4096 conv=notrunc,noerror,sync | gzip -c >> {}".format(BACKUP_TARGET, BACKUP_FILE), shell = True) != 0:
                print(" ERROR: couldn't create backup file")
                subprocess.call(["umount", BACKUP_MOUNT_POINT])
                break
            else:
                print(" SUCCESS: created backup file")

            if subprocess.call("sync") != 0:
                print(" ERROR: couldn't sync devices")
                subprocess.call(["umount", BACKUP_MOUNT_POINT])
                break
            else:
                print(" SUCCESS: synced devices")

            subprocess.call(["ls", "-lh", BACKUP_MOUNT_POINT])

            if subprocess.call(["umount", BACKUP_MOUNT_POINT]) != 0:
                print(" ERROR: couldn't unmount backup device")
            else:
                print(" SUCCESS: unmounted backup device")

            success = True
            print(" SUCCESS: Backup done on " + backup_device)
            print_done()
            break
        else:
            print(" Backup device {} not found".format(backup_device))
    if success == False:
        print(" ERROR: No compatible backup device found. BACKUP FAILED!")

def restore_hdd_backup():
    print_header("Restoring fusion from backup...")
    success = False
    subprocess.call(["mkdir", "-p", BACKUP_MOUNT_POINT])
    for backup_device in BACKUP_DEVICES:
        if os.path.exists(backup_device):
            if subprocess.call(["mount", "-t", "ntfs", backup_device, BACKUP_MOUNT_POINT]) != 0:
                print(" ERROR: couldn't mount backup device")
                break
            else:
                print(" SUCCESS: mounted backup device")

            if subprocess.call("gunzip -c {} | dd of={}".format(BACKUP_FILE, BACKUP_TARGET), shell = True) != 0:
                print(" ERROR: couldn't restore from backup file")
                subprocess.call(["umount", BACKUP_MOUNT_POINT])
                break
            else:
                print(" SUCCESS: restored from backup file")

            if subprocess.call("sync") != 0:
                print(" ERROR: couldn't sync devices")
                subprocess.call(["umount", BACKUP_MOUNT_POINT])
                break
            else:
                print(" SUCCESS: synced devices")

            if subprocess.call(["umount", BACKUP_MOUNT_POINT]) != 0:
                print(" ERROR: couldn't unmount backup device")
            else:
                print(" SUCCESS: unmounted backup device")

            success = True
            print(" SUCCESS: Backup restored from " + backup_device)
            print_done()
            break
        else:
            print(" Backup device {} not found".format(backup_device))
    if success == False:
        print(" ERROR: No compatible backup device found. RESTORE FAILED!")
    print_done()
    return success

def configure_vnc():
    print_header("Install x11vnc")
    x11vnc_path = "/usr/bin/x11vnc"
    if os.path.exists(x11vnc_path):
        print(" x11vnc already installed.")
    else:
        subprocess.call("apt-get -y install x11vnc", shell = True)
    print_done()

    print_header("Configure x11vnc password")
    subprocess.call([x11vnc_path, "-storepasswd", VNC_PASSWORD, VNC_PASSWORD_FILE])
    print_done()
    conf_path = "/etc/init/x11vnc.conf"
    allowed_subnets = "10.10.,10.11.,192.168.80."
    print_header("Configure " + conf_path)
    with open(conf_path, "w+t") as conf:
        conf.write("""start on login-session-start
script
    /usr/bin/x11vnc -rfbauth {} -rfbport 5900 -display :0 -auth /var/run/lightdm/root/:0 -forever -bg -o /var/log/x11vnc.log -noxrecord -noxfixes -noxdamage -nomodtweak -noipv6 -shared -allow {}
end script
""".format(VNC_PASSWORD_FILE, allowed_subnets))
    print_done()

def configure_lightdm_conf(current_user):
    conf_path = "/etc/lightdm/lightdm.conf"
    print_header("Configuring " + conf_path)
    with open(conf_path, "w+t") as conf:
        conf.write("""[SeatDefaults]
autologin-guest=false
autologin-user={}
autologin-user-timeout=0
autologin-session=lightdm-autologin
""".format(current_user))
    print_done()

def enable_fusion_session(current_user, enabled):
    conf_path = "/var/lib/AccountsService/users/" + current_user
    print_header("Configuring " + conf_path)
    if enabled:
        session = "fusion"
    else:
        session = "ubuntu"
    with open(conf_path, "w+t") as conf:
        conf.write("""[User]
Language=
XSession={}
SystemAccount=false
""".format(session))
        print_done()

def disable_fusion_autostart(current_user):
    configure_lightdm_conf("")
    enable_fusion_session(current_user, False)

def enable_fusion_autostart(current_user):

    print_header("Install openbox")
    subprocess.call("apt-get -y install openbox", shell = True)
    print_done()

    configure_lightdm_conf(current_user)

    enable_fusion_session(current_user, True)

    start_script = fusion_start_script(current_user)
    conf_path = "/usr/share/xsessions/fusion.desktop"
    print_header("Configuring " + conf_path)
    with open(conf_path, "w+t") as conf:
        conf.write("""[Desktop Entry]
Name=Fusion
Exec=/bin/bash {}
""".format(start_script))
        print_done()

    print_header("Writing " + start_script)
    use_fusion_user_privileges(current_user)
    if not os.path.exists(os.path.dirname(start_script)):
        os.makedirs(os.path.dirname(start_script))
    with open(start_script, "w+t") as script:
        script.write("""#!/bin/bash
xset s off
xset -dpms

FUSION_DIR="{fusion_dir}"
cd "$FUSION_DIR"
# disable the second screen on EBN
xrandr --output VGA1 --off
/usr/bin/openbox &

source {qt5_env_sh}

UNREGISTERED_STATUS=4
RESTART_STATUS=3
EXE_STATUS=$UNREGISTERED_STATUS
RESTART_FUSION_AFTER_CRASH=1
export LD_LIBRARY_PATH="$FUSION_DIR":$LD_LIBRARY_PATH

MAX_STARTUP_DELAY=10
for i in {{1..$MAX_STARTUP_DELAY}} ; do
    for interface in eth0 eth1 ; do
        test -n "`ip link show $interface up`" && break 2
    done
    echo "waiting for the network..."
    sleep 1
done

CRASH_HANDLER_APP="/home/client/fusion-linux/crash_handler"
if [ ! -x $CRASH_HANDLER_APP ]; then
    CRASH_HANDLER_APP="xeyes"
    RESTART_FUSION_AFTER_CRASH=0
else
    RESTART_FUSION_AFTER_CRASH=1
fi

while true;
do
    if test -f {pin_file} ; then
        logger "Fusion is PINNED. Not checking for newer versions!"
    else
        pushd $HOME
        VERSION=$(cat {version_file})
        logger "+ Checking for a newer \\"$VERSION\\" fusion on FTP..."
        logger "+ Downloading \\"$VERSION\\" fusion..."
        DLD_URL="{download_url}"
        DLD_PWD="{download_pwd}"
        DLD_USR="{download_usr}"
        OUTPUT=$(wget -N --progress=dot:mega --user $DLD_USR --password $DLD_PWD $DLD_URL 2>&1 | tee /dev/stderr)
        DOWNLOADED=$(echo $OUTPUT | grep "not retrieving")
        if [ -z "$DOWNLOADED" ] ; then
                logger "+ Found newer \\"$VERSION\\" fusion..."
                logger "+ Removing old fusion..."
                rm -fv "$FUSION_DIR"/exe*
                rm -fv "$FUSION_DIR"/lib*so*
                rm -fv "$FUSION_DIR"/README
                logger "+ Extracting new \\"$VERSION\\" fusion..."
                tar xfv $HOME/fusion-linux.tar.gz
                SOURCE_VERSION=$(grep "SOURCE VERSION:" "$FUSION_DIR"/README | cut -d ":" -f 2 | tr -d ' ')
                logger "+ Finished upgrading \\"$VERSION\\" fusion to version: $SOURCE_VERSION."
        else
                logger "+ You already have the latest \\"$VERSION\\" fusion."
        fi
        popd
    fi

    {executable}
    EXE_STATUS=$?

    if [[ $EXE_STATUS != $UNREGISTERED_STATUS && $EXE_STATUS != $RESTART_STATUS ]] ; then
        $CRASH_HANDLER_APP
        EXE_STATUS=$?
        if [[ $EXE_STATUS == 1 && $RESTART_FUSION_AFTER_CRASH == 1 ]] ; then
            echo "restarting fusion..."
        else
            echo "crashing to desktop..."
            break
        fi
    fi
done
""".format(fusion_dir=fusion_deploy_dir(current_user),
           qt5_lib_dir=QT5_LIB_DIR,
           qt5_env_sh=QT5_ENV_SH,
           executable=fusion_exe(current_user),
           pin_file=fusion_pin_version_filename(current_user),
           version_file=fusion_version_filename(current_user),
           download_url="{}/$VERSION/{}".format(FUSION_DOWNLOAD_URL_PREFIX, FUSION_ARCHIVE),
           download_usr=FUSION_FTP_USR,
           download_pwd=FUSION_FTP_PWD,
        ))
        script.close()
        print_done()
    restore_super_privileges()

    print("You must reboot in order to activate autostart and autoload.")

def start_fusion(current_user):
    try:
        use_fusion_user_privileges(current_user)

        deploy_dir = fusion_deploy_dir(current_user)

        with tempfile.NamedTemporaryFile(mode="w+t", delete = False) as script:
            script.write("""#!/bin/bash
cd {0}
DISPLAY=:0 LD_LIBRARY_PATH={0} {1}
""".format(deploy_dir, fusion_exe(current_user)))
            script.close()
            launch_remote_fusion(current_user, script.name)
            os.unlink(script.name)

        restore_super_privileges()
        print_done()
    except KeyError:
        print(" User {} not found on this system. Please create it and try again.".format(current_user))

def kill_fusion(current_user):
    terminated = False
    for proc in psutil.process_iter():
        try:
            if proc.is_running() and "exe" in proc.exe() and proc.cwd() == fusion_deploy_dir(current_user):
                print(" Found Fusion process with PID: {}.".format(proc.pid))
                print(" Terminating process...")
                proc.terminate()
                terminated = True
                print_done()
                break
        except psutil.NoSuchProcess:
            pass
    if not terminated:
        print(" Fusion process not found.")

def configure_window_manager(current_user):
    # NOTE: use `obxprop | grep "^_OB_APP"` to obtain the class, name, role, title and type of an app
    print_header("Writing ~/.config/openbox/rc.xml")

    config_dir = os.path.join(fusion_user_home(current_user), ".config", "openbox")
    if os.path.exists(config_dir) == False:
        os.makedirs(config_dir, 0o755)
    with open(os.path.join(config_dir, "rc.xml"), "w+t") as config:
        config.write("""<?xml version="1.0" encoding="UTF-8"?>
<openbox_config xmlns="http://openbox.org/3.4/rc" xmlns:xi="http://www.w3.org/2001/XInclude">
    <applications>
        <application class="*">
            <decor>no</decor>
        </application>
        <application class="Exe" title="EXE-Touch2Success">
            <maximized>yes</maximized>
        </application>
        <application class="fusion-qml" title="Fusion QML">
	    <fullscreen>yes</fullscreen>
            <maximized>yes</maximized>
        </application>
    </applications>
</openbox_config>
""")
    print_done()

def fusion_pin_version_filename(current_user):
    deploy_dir = fusion_deploy_dir(current_user)
    return os.path.join(deploy_dir, FUSION_PIN_VERSION_FILE)

def set_auto_upgrade(current_user, state):
    def create_pin_file():
        open(fusion_pin_version_filename(current_user), 'a').close()

    def remove_pin_file():
        try:
            os.remove(fusion_pin_version_filename(current_user))
        except FileNotFoundError:
            pass

    print_header("Setting Fusion auto-upgrade to: {}".format(state))
    if state == True:
        remove_pin_file()
    else:
        create_pin_file()

def fusion_version_filename(current_user):
    deploy_dir = fusion_deploy_dir(current_user)
    return os.path.join(deploy_dir, "VERSION")

def upgrade_fusion(current_user, version):
    def get_old_version():
        print_header("Checking old VERSION")
        filename = fusion_version_filename(current_user)
        print(" checking version file: " + filename)
        if os.path.exists(filename):
            with open(filename) as version_file:
                 version = version_file.read()
                 print(" old version: " + version)
                 return version
        else:
            return None

    def download_archive():
        ret = True
        with subprocess.Popen(["wget",
                               "-N",
                               "--progress=dot:mega",
                               "--user", FUSION_FTP_USR,
                               "--password", FUSION_FTP_PWD,
                               FUSION_DOWNLOAD_URL.format(version)],
                universal_newlines = True,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT) as proc:
            for line in proc.stdout:
                line = line.rstrip("\n")
                print(line)
                if "not retrieving" in line:
                    ret = False
        return ret

    def delete_old_fusion(dirName):
        def remove_files(pattern):
            subprocess.call("rm -frv {}/{}".format(dirName, pattern), shell = True)
        remove_files("crash_handler")
        remove_files("exe")
        remove_files("lib*so*")
        remove_files("logo.png")
        remove_files("plugins")
        remove_files("README")
        remove_files("qml")
        remove_files("qt.conf")

    def extract_new_fusion():
        subprocess.call("tar xfv " + FUSION_ARCHIVE, shell = True)
        with open(fusion_version_filename(current_user), "wt") as version_file:
            version_file.write(version)
            version_file.close()

    def display_readme_if_exists(top_dir, deploy_dir):
        print_header("Fusion README")
        readme = os.path.join(deploy_dir, "README")
        if os.path.exists(readme):
            with open(readme) as readme:
                print(readme.read())
        else:
            print(" No README found.")

    if os.path.exists(fusion_pin_version_filename(current_user)):
        print("Fusion PINNED to current version.\nPlease enable AUTO-UPGRADE if you want to install a newer version!")
        return

    try:
        use_fusion_user_privileges(current_user)

        top_dir = fusion_user_home(current_user)
        deploy_dir = fusion_deploy_dir(current_user)
        os.chdir(top_dir)

        old_version = get_old_version()
        if old_version is None or old_version != version:
            delete_old_fusion(deploy_dir)

        print_header("Downloading new {0} Fusion version".format(version))
        if download_archive():
            print_header("Deleting old files")
            delete_old_fusion(deploy_dir)
            print_header("Extract new files")
            extract_new_fusion()
        else:
            print(" You already have the latest {0} Fusion version.".format(version))

        display_readme_if_exists(top_dir, deploy_dir)

        restore_super_privileges()
    except KeyError:
        print(" User {} not found on this system. Please create it and try again.".format(current_user))

def configure_user_permissions(current_user):
    def print_operation_status(modified):
        if modified:
            print_done()
        else:
            print(" Already configured.")

    def fix_groups():
        groups = subprocess.check_output(["groups", current_user], universal_newlines=True).split(":")[1].strip().split()
        modified = False
        for group in ("lp", "dialout"):
            if group not in groups:
                print(" adding to secondary group \"{}\"".format(group))
                subprocess.check_output(["usermod", "-a", "-G", group, current_user])
                modified = True
        print_operation_status(modified)

    def fix_sudoers():
        sudo_required_lines = (
            "Cmnd_Alias CAT_BOARD_SERIAL = /bin/cat /sys/class/dmi/id/board_serial",
            "Defaults!CAT_BOARD_SERIAL !requiretty",
            "Cmnd_Alias REBOOT = /sbin/reboot",
            "Defaults!REBOOT !requiretty",
            "Cmnd_Alias OPEN_CASH_DRAWER = {}/{}".format(fusion_deploy_dir(current_user), OPEN_CASH_DRAWER_APP),
            "Defaults!OPEN_CASH_DRAWER !requiretty",
            "{} ALL= CAT_BOARD_SERIAL,REBOOT,OPEN_CASH_DRAWER, NOPASSWD: CAT_BOARD_SERIAL,REBOOT,OPEN_CASH_DRAWER".format(current_user)
        )

        sudoers = subprocess.check_output(["sudo", "cat", "/etc/sudoers"], universal_newlines=True).split("\n")

        for prefix in (
            "{} ALL=".format(current_user)
        ):
            for line in sudoers:
                if line.startswith(prefix):
                    sudoers.remove(line)

        modified = False
        for line in sudo_required_lines:
            if line not in sudoers:
                sudoers.append(line)
                modified = True
        if modified:
            with open("/etc/sudoers", "w+") as sudoers_file:
                print(" updating /etc/sudoers")
                sudoers_file.write("\n".join(sudoers) + "\n")
                sudoers_file.close()
        print_operation_status(modified)

    try:
        print_header("Fixing seconday groups for user \"{}\"".format(current_user))
        fix_groups()
        print_header("Fixing /etc/sudoers")
        fix_sudoers()
    except subprocess.CalledProcessError:
        pass

def install_openvpn():
    print_header("Installing openvpn")
    subprocess.call("apt-get -y install openvpn", shell = True)
    for file in OPENVPN_FILES:
        subprocess.Popen(["wget",
                          "-N",
                          "--user", FUSION_FTP_USR,
                          "--password", FUSION_FTP_PWD,
                          "{}/{}".format(OPENVPN_DOWNLOAD_URL, file)
                          ], cwd = "/etc/openvpn").wait()
    subprocess.call("service openvpn restart", shell = True)
    print_done()

def install_ssh():
    subprocess.call("apt-get -y install openssh-server", shell = True)

def display_system_info(current_user):
    print_header("motherboard serial number")
    subprocess.call("cat /sys/class/dmi/id/board_serial", shell = True)
    print_header("lsb_release -a")
    subprocess.call("lsb_release -a", shell = True)
    print_header("ifconfig")
    subprocess.call("ifconfig", shell = True)
    print_header("lsusb")
    subprocess.call("lsusb", shell = True)
    print_header("sensors")
    subprocess.call("sensors")
    print_header("fusion README")
    readme = "/home/" + current_user + "/fusion-linux/README"
    if os.path.exists(readme):
        subprocess.call("cat " + readme, shell = True)
    else:
        print(" File " + readme + " not found.\n Have you installed fusion for user '" + current_user + "'?")

def reboot_system():
    push_background_title(REBOOT_MENU)
    if d.yesno("Are you REALLY sure you want to reboot?") == d.OK:
        subprocess.call("shutdown -r now", shell = True)
        sys.exit(0)
    pop_background_title()

def reconfigure_database():
    def mysql_command(command):
        subprocess.call("mysql -u root -p{} -e \"{}\"".format(MYSQL_ROOT_PASSWORD, command), shell = True)

    mysql_command("drop database if exists {};".format(MYSQL_FUSION_DATABASE))
    mysql_command("create database {};".format(MYSQL_FUSION_DATABASE))
    mysql_command("grant all privileges on {}.* to root@localhost identified by 'root';".format(MYSQL_FUSION_DATABASE))

def install_mysql():
    with tempfile.NamedTemporaryFile(mode="w+t", delete = False) as selections:
        content = """mariadb-server mysql-server/root_password password {0}
mariadb-server mysql-server/root_password_again password {0}
""".format(MYSQL_ROOT_PASSWORD)
        selections.write(content)
        selections.close()
        subprocess.call(["debconf-set-selections", selections.name])
        subprocess.call("apt-get -y install --purge mariadb-server", shell = True)
        os.unlink(selections.name)
        subprocess.check_output("echo PURGE | sudo debconf-communicate mariadb-server", shell = True)
        reconfigure_database()

def remove_mysql():
    subprocess.call("apt-get -y remove --purge mysql-server mariadb-server", shell = True)
    subprocess.call("apt-get -y autoremove --purge", shell = True)
    subprocess.call("rm -rf /var/lib/mysql", shell = True)

def install_runtime_dependencies():
    subprocess.call("apt-get -y install phonon-backend-gstreamer1.0", shell = True)
    subprocess.call("apt-get -y install phonon libqt4-sql-mysql", shell = True)
    subprocess.call("apt-get -y install lm-sensors thermald", shell = True)
    subprocess.call("apt-get -y install flashplugin-installer", shell = True)
    subprocess.call("apt-get -y purge modemmanager", shell = True)
    subprocess.call("apt-get -y install jmeter", shell=True)
    subprocess.call("apt-get -y install ntp", shell=True)
    subprocess.call("service ntp stop", shell=True)
    subprocess.call("ntpdate pool.ntp.org", shell=True)
    subprocess.call("service ntp start", shell=True)

def install_runtime_dependencies_fusion8():
    subprocess.call("apt-get -y install libavahi-compat-libdnssd1", shell=True)

def uninstall_runtime_dependencies_fusion8():
    subprocess.call("apt-get -y remove --purge libavahi-compat-libdnssd1", shell=True)

def update_system():
    subprocess.call("apt-get update", shell = True)
    subprocess.call("apt-get -y upgrade", shell = True)
    subprocess.call("apt-get -y dist-upgrade", shell = True)

def fusion_release_menu(fusion_user):
    def retrieve_tagged_releases():
        f = ftplib.FTP()
        f.connect(FUSION_FTP_SERVER)
        f.login(FUSION_FTP_USR, FUSION_FTP_PWD)
        ls = []
        for name, facts in f.mlsd(FUSION_FTP_TAGS_DIR):
            if facts["type"] == "dir":
                ls.append(FUSION_FTP_TAGS_DIR + name)
        ls.sort(reverse=True)
        return ls

    releases = ["stable", "alpha", "beta", "test1", "test2", "test3", "test4", "test5"]
    releases += retrieve_tagged_releases()
    push_background_title("Available Releases")
    terminal_rows, terminal_cols = d.maxsize()

    code = d.OK
    while code == d.OK:
        code, tag = d.menu("Available fusion releases:",
                           choices=[(x, "") for x in releases],
                           height=max(terminal_rows - 10, 15),
                           width=max(terminal_cols - 16, 54),
                           menu_height=max(terminal_rows - 10, 7))
        if code == d.OK:
            upgrade_fusion(fusion_user, tag)
            console_pause()

def cups_menu(fusion_user):
    def get_printer_port(identifier):
        lines = subprocess.check_output("lpinfo -v|grep '" + identifier + "'|head -1|cut -f2 -d' '",
                                        shell=True, universal_newlines=True).splitlines()
        if len(lines) > 0:
            return lines[0]

        return ""

    def install_cups_t20(archive, driver_dir, install_prompt, ppd, success_message):
        delete_cups_t20()
        install_cups()

        prev_wd = os.getcwd()
        print_header("Downloading CUPS T20 driver")
        subprocess.call(["wget",
                         "-N",
                         "--user", FUSION_FTP_USR,
                         "--password", FUSION_FTP_PWD,
                         "{}/{}".format(FUSION_DOWNLOAD_URL_PREFIX, archive)])
        print_done()
        print_header("Extracting the driver")
        subprocess.call("tar xvf " + archive, shell=True)
        print_done()
        print_header("Installing the driver:\nWhen prompted, chose option \"" + install_prompt + "\"")
        os.chdir(driver_dir)
        subprocess.call("./install.sh", shell=True)
        print_done()
        print_header("Configure the default printer")

        # try to detect the printer using tmbparn
        port = get_printer_port("tmbaprn")
        if port == "":
            subprocess.call("echo 'No tmbaprn printers found. Trying now usb...'", shell=True)
            port = get_printer_port("usb://EPSON/")
        if port == "":
            subprocess.call("echo 'No usb printers found. No EPSON CUPS printer configured!'", shell=True)
        else:
            subprocess.call("lpadmin -p TM-T20 -v " + port + " -P " + ppd + " -E", shell=True)
            subprocess.call("lpadmin -d TM-T20", shell=True)
            subprocess.call("lpstat -v", shell=True)
            subprocess.call("echo \"" + success_message + "\" | lp", shell=True)
        print_done()
        os.chdir(prev_wd)

    def delete_cups_t20():
        prev_wd = os.getcwd()
        print_header("Removing cups drivers")
        for p in ["tmt-cups", "tmx-cups"]:
            if os.path.exists(p):
                os.chdir(p)
                subprocess.call("./uninstall.sh", shell=True)
                os.chdir(prev_wd)
                shutil.rmtree(p, ignore_errors=True)
        print_done()
        print_header("Removing other Epson stuff")
        subprocess.call("apt-get -y purge --auto-remove epson-port-communication-service tmt-cups-backend tmx-cups-backend", shell=True)
        print_done()
        os.chdir(prev_wd)

    def install_cups_t20_v2010():
        archive = "tmx-cups-2.0.1.0.tar.gz"
        driver_dir = "tmx-cups"
        install_prompt = "1.Ubuntu 12.04 x86(32bit) option"
        ppd = "ppd/tm-ba-thermal-rastertotmt.ppd.gz"
        success_message = "CUPS for TM-T20 (v.2.0.1.0) installed"
        install_cups_t20(archive, driver_dir, install_prompt, ppd, success_message)

    def install_cups_t20_v1410():
        archive = "tmt-cups-1.4.1.0.tar.gz"
        driver_dir = "tmt-cups"
        install_prompt = "1.Ubuntu 9.04 x86(32bit)"
        ppd = "ppd/tm-t20ii-rastertotmt.ppd"
        success_message = "CUPS for TM-T20 (v.1.4.1.0) installed"
        install_cups_t20(archive, driver_dir, install_prompt, ppd, success_message)

    def install_cups():
        print_header("Make sure the system is updated")
        subprocess.call("apt-get update", shell=True)
        subprocess.call("apt-get -y upgrade", shell=True)
        subprocess.call("apt-get -y dist-upgrade", shell=True)
        subprocess.call("apt-get -f install", shell=True)
        subprocess.call("apt-get -y install cups", shell=True)
        print_done()

    def delete_cups():
        delete_cups_t20() # make sure we don't leave dangling EPSON stuff before removing CUPS, or it will be difficult do fix it later
        print_header("Uninstalling CUPS")
        subprocess.call("apt-get -y purge --auto-remove cups-daemon", shell=True)
        print_done()

    def force_fix_apt():
        print_header("Removing EPSON leftovers")
        subprocess.call("rm -f /var/lib/dpkg/info/tmt-cups-backend*", shell=True)
        subprocess.call("rm -f /var/lib/dpkg/info/tmx-cups-backend*", shell=True)
        subprocess.call("dpkg --remove --force-remove-reinstreq tmt-cups-backend", shell=True)
        subprocess.call("dpkg --remove --force-remove-reinstreq tmx-cups-backend", shell=True)
        subprocess.call("apt-get clean", shell=True)
        subprocess.call("apt-get update", shell=True)
        subprocess.call("apt-get upgrade", shell=True)
        subprocess.call("apt-get -f install", shell=True)
        print_done()

    push_background_title(CUPS_MENU)
    code = d.OK

    install_cups_t20_v1410_id = "1"
    install_cups_t20_v2010_id = "2"
    delete_cups_t20_id = "3"
    install_cups_id = "4"
    delete_cups_id = "5"
    force_fix_apt_id = "6"

    while code == d.OK:
        terminal_rows, terminal_cols = d.maxsize()
        code, tag = d.menu(CUPS_MENU,
                           choices=[
                               (install_cups_t20_v1410_id, "Install CUPS and CUPS drivers for TM-T20 (new - v.1.4.1.0)"),
                               (install_cups_t20_v2010_id, "Install CUPS and CUPS drivers for TM-T20 (old - v.2.0.1.0)"),
                               (delete_cups_t20_id, "Uninstall TM-T20 printer from CUPS"),
                               ("",""),
                               (install_cups_id, "Install CUPS"),
                               (delete_cups_id, "Uninstall CUPS"),
                               ("",""),
                               (force_fix_apt_id, "Fix APT in case of broken dependecies (DANGEROUS)"),
                           ],
                           height=max(terminal_rows - 10, 15),
                           width=max(terminal_cols - 16, 54),
                           menu_height=max(terminal_rows - 10, 7),
                           )

        if code == d.OK:
            if tag == install_cups_t20_v2010_id:
                install_cups_t20_v2010()
                console_pause()
            elif tag == install_cups_t20_v1410_id:
                install_cups_t20_v1410()
                console_pause()
            elif tag == delete_cups_t20_id:
                delete_cups_t20()
                console_pause()
            elif tag == install_cups_id:
                install_cups()
                console_pause()
            elif tag == delete_cups_id:
                delete_cups()
                console_pause()
            elif tag == force_fix_apt_id:
                force_fix_apt()
                console_pause()

    pop_background_title()

def fix_user_home_permisions(current_user):
    print_header("Fixing permisions in /home/" + current_user)
    subprocess.call("sudo chown {0}.{0} -R /home/{0}".format(current_user), shell = True)
    print_done()

def change_volume(current_user, value):
    print_header("Changing volume: " + value)
    subprocess.call("sudo -u {} -H sh -c \"pactl -- set-sink-volume 0 {}\"".format(current_user, value), shell=True)
    print_done()

def install_newrelic():
    newrelic_uktech_key = "0b56ca79a5429d394bc0bac3b756eae263358a66"
    print_header("Installing newrelic")
    if subprocess.call("wget -O- https://download.newrelic.com/548C16BF.gpg | apt-key add -", shell=True):
        print("FAILED: couldn't add the newrelic repo key")
        return

    if not os.path.exists("/etc/apt/sources.list.d/newrelic.list"):
        subprocess.call("echo deb http://apt.newrelic.com/debian/ newrelic non-free >> /etc/apt/sources.list.d/newrelic.list", shell=True)

    subprocess.call("apt-get update", shell=True)
    subprocess.call("apt-get install newrelic-sysmond", shell=True)
    subprocess.call("nrsysmond-config --set license_key=" + newrelic_uktech_key, shell=True)
    subprocess.call("service newrelic-sysmond start", shell=True)

def install_qt5():
    # files to be removed:
    #  /etc/apt/sources.list.d/beineri-opt-qt54-trusty-trusty.list
    #  /etc/apt/sources.list.d/beineri-opt-qt541-trusty-trusty.list
    #  /etc/apt/sources.list.d/beineri-opt-qt532-trusty-trusty.list
    subprocess.call("rm -rf /etc/apt/sources.list.d/beineri-opt-qt5*", shell=True)
    subprocess.call("add-apt-repository -y " + QT5_PPA, shell=True)
    subprocess.call("apt-get update", shell = True)
    subprocess.call("apt-get install -y " + QT5_INSTALL_PACKAGES, shell=True)

def uninstall_qt5():
    subprocess.call("rm -rf /etc/apt/sources.list.d/beineri-opt-qt5*", shell=True)
    for qt in ["qt53", "qt54", "qt55"]:
        for package in QT5_UNINSTALL_PACKAGES.format(qt=qt).split(" "):
            subprocess.call("apt-get remove -y --purge " + package, shell=True)
    subprocess.call("apt-get autoremove -y --purge", shell = True)

def enable_fusion_boot_script(enable):
    def replace_line(old_lines, prefix, desired, required=False):
        lines = []
        found = False
        for line in old_lines:
            if line.startswith(prefix):
                if not found:
                    lines.append(desired)
                found = True
            elif not (line == "" or line.isspace() or line[0] == "#"):
                lines.append(line)
        if required and not found:
            lines.append(desired)
        return lines

    def fix_grub():
        grub_file_name = "/etc/default/grub"
        grub_cmdline_linux_default = "text" if enable else "splash"

        print_header("Generating new " + grub_file_name)
        with open(grub_file_name) as f:
            lines = f.read().split("\n")
            f.close()

            lines = replace_line(lines,
                                 "GRUB_CMDLINE_LINUX_DEFAULT=\"quiet",
                                 "GRUB_CMDLINE_LINUX_DEFAULT=\"quiet {}\"".format(grub_cmdline_linux_default))
            lines = replace_line(lines,
                                 "GRUB_CMDLINE_LINUX_DEFAULT=\"i8042.nomux=1 i8042.reset quiet",
                                 "GRUB_CMDLINE_LINUX_DEFAULT=\"i8042.nomux=1 i8042.reset quiet {}\"".format(grub_cmdline_linux_default))
            lines = replace_line(lines,
                                 "GRUB_HIDDEN_TIMEOUT=",
                                 "GRUB_HIDDEN_TIMEOUT=0",
                                 required=True)
            lines = replace_line(lines,
                                 "GRUB_HIDDEN_TIMEOUT_QUIET=",
                                 "GRUB_HIDDEN_TIMEOUT_QUIET=\"true\"",
                                 required=True)
            lines = replace_line(lines,
                                 "GRUB_TIMEOUT=",
                                 "GRUB_TIMEOUT=0",
                                 required=True)
            lines = replace_line(lines,
                                 "GRUB_RECORDFAIL_TIMEOUT=",
                                 "GRUB_RECORDFAIL_TIMEOUT=0",
                                 required=True)

            if lines:
                with open(grub_file_name, "w+") as f:
                        f.write("\n".join(lines) + "\n")
                        f.close()

                print("\n".join(lines)+"\n")
        print_done()

        print_header("Running update-grub")
        subprocess.call("update-grub")

    def write_fusion_boot(name):
        name = "/etc/init.d/" + name
        print_header("Creating " + name)
        with open(name, "w+") as f:
            f.write("""#! /bin/sh
### BEGIN INIT INFO
# Provides:          fusion-boot
# Required-Start:    $named $network $local_fs $remote_fs $syslog
# Required-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# X-Start-Before:    $x-display-manager gdm kdm xdm wdm ldm sdm nodm
# X-Interactive:     true
# Short-Description: fusion script to run at boot-time
# Description:       Checks the ftp for a script file and executes it.
### END INIT INFO

# Author: Touch2Success <mds@touch2success.com>

# Do NOT "set -e"

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Fusion boot script"
NAME=fusion-boot
SCRIPTNAME=/etc/init.d/$NAME

# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh

# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions

do_start()
{{
    SCRIPT_FILE={boot_script_target}
    /usr/bin/wget --user "{download_usr}" --password "{download_pwd}" "{boot_script_url}" -O "$SCRIPT_FILE"
    if test -f "$SCRIPT_FILE" ; then
        chmod +x "$SCRIPT_FILE"
        "$SCRIPT_FILE"
    fi
}}

case "$1" in
  start)
	do_start
	;;
  stop)
	echo "$NAME: nothing to stop"
	;;
  *)
	echo "Usage: $SCRIPTNAME {{start}}" >&2
	exit 3
	;;
esac

:""".format(download_usr=FUSION_FTP_USR,
            download_pwd=FUSION_FTP_PWD,
            boot_script_url=FUSION_BOOT_SCRIPT_URL,
            boot_script_target=FUSION_BOOT_SCRIPT_TARGET,
            ))
            f.close()
            subprocess.call("chmod +x {}".format(name), shell=True)
            print_done()

    def enable_service(service):
        subprocess.call("update-rc.d {} defaults".format(service), shell=True)

    def disable_service(service):
        subprocess.call("update-rc.d -f {} remove".format(service), shell=True)

    fusion_boot = "fusion-boot"
    login_manager = "lightdm"

    fix_grub()

    if enable:
        write_fusion_boot(fusion_boot)
        print_header("Enabling extra services")
        enable_service(fusion_boot)
        enable_service(login_manager)
    else:
        print_header("Disabling extra services")
        disable_service(fusion_boot)
        disable_service(login_manager)
    print_done()

def configure_ebn(fusion_user):

    install_3mtouch_id = "1"
    install_3mtouch_menu = "Install 3M drivers"
    configure_egalaxtouch_grub_id = "2"
    configure_egalaxtouch_grub_menu = "Configure grub for eGalaxTouch and reboot"
    install_egalaxtouch_id = "3"
    install_egalxtouch_menu = "Install eGalaxTouch drivers"
    run_egalaxtouch_config_id = "4"
    run_egalaxtouch_config_menu = "Run eGalaxTouch config tool (on the EBN screen)"

    def install_egalaxtouch_drivers():
        prev_wd = os.getcwd()
        print_header("Download EBN eGalaxTouch driver")
        subprocess.call(["wget",
                         "-N",
                         "--user", FUSION_FTP_USR,
                         "--password", FUSION_FTP_PWD,
                         EBN_EGALAXTOUCH_ARCHIVE_URL])
        print_done()
        print_header("Extracting EBN eGalaxTouch archive")
        subprocess.call("unzip -x " + EBN_EGALAXTOUCH_ARCHIVE, shell=True)
        print_done()
        print_header("Install EBN eGalaxTouch drivers")
        os.chdir(EBN_EGALAXTOUCH_DIR)
        subprocess.call(["bash", "setup.sh"])
        print_done()
        os.chdir(prev_wd)

    def run_egalaxtouch_config():
        print_header("Configuring EBN - you must continue on the EBN display")
        with tempfile.NamedTemporaryFile(mode="w+t", delete=False) as script:
            script.write("DISPLAY=:0 " + EBN_EGALAXTOUCH_CONFIG_APP)
            script.close()
            subprocess.call(["bash", script.name])
            os.unlink(script.name)
        print_done()

    def configure_grub_for_egalaxtouch():
        print_header("Updating the /etc/default/grub")
        with open("/etc/default/grub", "w+t") as conf:
            conf.write("""GRUB_DEFAULT=0
GRUB_HIDDEN_TIMEOUT_QUIET="true"
GRUB_TIMEOUT=0
GRUB_HIDDEN_TIMEOUT=0
GRUB_RECORDFAIL_TIMEOUT=0
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="i8042.nomux=1 i8042.reset quiet splash"
GRUB_CMDLINE_LINUX=""
""")
        print_done()
        print_header("Update grub")
        subprocess.call("update-grub", shell=True)
        print_done()

    def install_ebn_3mtouch_drivers():
        prev_wd = os.getcwd()
        os.chdir(EBN_3MTOUCH_INSTALL_DIR)
        print_header("Download EBN 3M Touch driver")
        subprocess.call(["wget",
                         "-N",
                         "--user", FUSION_FTP_USR,
                         "--password", FUSION_FTP_PWD,
                         EBN_3MTOUCH_ARCHIVE_URL])
        print_done()
        print_header("Removing old driver, if present")
        subprocess.call("rm -rf " + EBN_3MTOUCH_DIR, shell=True, stderr=subprocess.PIPE)
        print_done()
        print_header("Extracting EBN 3M Touch archive")
        subprocess.call("tar -xvf " + EBN_3MTOUCH_ARCHIVE, shell=True, stderr=subprocess.PIPE)
        print_done()
        print_header("Install EBN 3M Touch drivers")
        os.chdir(EBN_3MTOUCH_DIR)
        subprocess.call(["bash", "./Install"])
        print_done()
        os.chdir(prev_wd)

    push_background_title(CONFIGURE_EBN_MENU)
    terminal_rows, terminal_cols = d.maxsize()

    choices = [
        (install_3mtouch_id, install_3mtouch_menu),
        (configure_egalaxtouch_grub_id, configure_egalaxtouch_grub_menu),
        (install_egalaxtouch_id, install_egalxtouch_menu),
        (run_egalaxtouch_config_id, run_egalaxtouch_config_menu),
    ]

    code = d.OK
    update_background_title()
    while code == d.OK:

        try:
            output = subprocess.check_output("sudo -u {} DISPLAY=:0 xinput list".format(fusion_user), shell=True, universal_newlines=True)
            device_name_3m_default = "3M 3M MicroTouch USB controller"
            device_name_3m_configured = "3M_MT7_USBFFFFFFFF"
            is_3mtouch_configured = device_name_3m_configured in output
            has_3mtouch = is_3mtouch_configured or (device_name_3m_default in output)
            info = """3M Touch hardware:
- present: {}
- configured: {}""".format("YES" if has_3mtouch else "NO", "YES" if is_3mtouch_configured else "NO")
        except:
            info = "Could not determine if 3M Touch hardware is present. Is fusion running?"

        code, tag = d.menu(info,
                           choices=choices,
                           height=max(terminal_rows - 10, 15),
                           width=max(terminal_cols - 16, 54),
                           menu_height=max(terminal_rows - 10, 7),
                           )

        if tag == configure_egalaxtouch_grub_id:
            configure_grub_for_egalaxtouch()
            console_pause()
            reboot_system()
            console_pause()
        elif tag == install_egalaxtouch_id:
            install_egalaxtouch_drivers()
            console_pause()
            reboot_system()
            console_pause()
        elif tag == run_egalaxtouch_config_id:
            run_egalaxtouch_config()
            console_pause()
        elif tag == install_3mtouch_id:
            install_ebn_3mtouch_drivers()
            console_pause()
            reboot_system()
            console_pause()

    pop_background_title()

def console_pause():
    input("Press Enter to continue...")

def create_user(user):
    def shadow_password(password):
        return subprocess.check_output(["mkpasswd", "-m", "sha-512", password, ], universal_newlines=True).rstrip("\n")

    subprocess.call(["groupadd", user])
    subprocess.call([
        "useradd", user,
        "-s", "/bin/bash",
        "-m",
        "-p", shadow_password(FUSION_USER_PASSWORD),
        "-g", user
        ])

def user_management_menu(user):

    def change_user_dialog(usr):
        available_users = (DEFAULT_FUSION_USER, "dev", "uktech")
        push_background_title(change_user_menu)
        code1, tag1 = d.radiolist("Current Fusion user: " + usr, choices=[(x, "", x == usr) for x in available_users])
        pop_background_title()
        if code1 == d.OK:
            return tag1
        return usr

    def delete_user(usr):
        push_background_title(delete_user_menu)
        if d.yesno("Are you REALLY sure you want to delete user {}?".format(usr)) == d.OK:
            subprocess.call(["killall", "-KILL", "-u", usr])
            subprocess.call(["userdel", "-r", usr])
        pop_background_title()

    push_background_title(USER_MANAGEMENT_MENU)
    code = d.OK
    while code == d.OK:

        change_user_id = "1"
        change_user_menu = "Select Fusion User"
        create_user_id = "2"
        create_user_menu = "Create user '{}'".format(user)
        delete_user_id = "3"
        delete_user_menu = "Delete user '{}'".format(user)
        configure_user_permissions_id = "4"
        configure_user_permissions_menu = "Give user '{}' access to modem, printer and reboot".format(user)
        fix_user_home_permissions_id = "5"
        fix_user_home_permissions_menu = "Fix permissions in '/home/{}'".format(user)
        configure_windowmanager_id = "6"
        configure_windowmanager_menu = "Configure the window manager for user '{}'".format(user)
        enable_fusion_autostart_id = "7.1"
        enable_fusion_autostart_menu = "Enable autologin and Fusion autostart for user '{}'".format(user)
        disable_fusion_autostart_id = "7.2"
        disable_fusion_autostart_menu = "Disable autologin and Fusion autostart"

        terminal_rows, terminal_cols = d.maxsize()
        code, tag = d.menu("Current Fusion user: " + user,
                           choices=[
                               (change_user_id, change_user_menu),
                               (create_user_id, create_user_menu),
                               (delete_user_id, delete_user_menu),
                               ("", ""),
                               (configure_user_permissions_id, configure_user_permissions_menu),
                               (fix_user_home_permissions_id, fix_user_home_permissions_menu),
                               (configure_windowmanager_id, configure_windowmanager_menu),
                               ("", ""),
                               (enable_fusion_autostart_id, enable_fusion_autostart_menu),
                               (disable_fusion_autostart_id, disable_fusion_autostart_menu),
                           ],
                           height=max(terminal_rows - 10, 15),
                           width=max(terminal_cols - 16, 54),
                           menu_height=max(terminal_rows - 10, 7),
                           )

        if code == d.OK:
            if tag == change_user_id:
                user = change_user_dialog(user)
            elif tag == create_user_id:
                create_user(user)
                console_pause()
            elif tag == delete_user_id:
                delete_user(user)
                console_pause()
            elif tag == configure_user_permissions_id:
                configure_user_permissions(user)
                console_pause()
            elif tag == fix_user_home_permissions_id:
                fix_user_home_permisions(user)
                console_pause()
            elif tag == configure_windowmanager_id:
                configure_window_manager(user)
                console_pause()
            elif tag == enable_fusion_autostart_id:
                enable_fusion_autostart(user)
                console_pause()
            elif tag == disable_fusion_autostart_id:
                disable_fusion_autostart(user)
                console_pause()

    pop_background_title()
    return user

def system_management_menu(user):
    update_system_id = "1"
    update_system_menu = "Update system with the latest packages"
    install_runtime_dependencies_id = "2"
    install_runtime_dependencies_menu = "Install Fusion runtime dependencies"
    install_ssh_id = "3"
    install_ssh_menu = "Install SSH"
    install_openvpn_id = "4"
    install_openvpn_menu = "Install OpenVPN"
    install_qt5_id = "5.1"
    install_qt5_menu = "Install Qt5 from PPA and other dependencies for Fusion8"
    uninstall_qt5_id = "5.2"
    uninstall_qt5_menu = "Uninstall Qt5, its PPA and dependencies installed for Fusion8"
    install_mysql_id = "6.1"
    install_mysql_menu = "Install MariaDB and do initial database setup"
    clear_fusion_database_id = "6.2"
    clear_fusion_database_menu = "Clear Fusion database (old data is removed)"
    uninstall_mysql_id = "6.3"
    uninstall_mysql_menu = "Uninstall MariaDB/MySQL and remove /var/lib/mysql"
    # enable_fusion_boot_id = "7.1"
    # enable_fusion_boot_menu = "Enable fusion boot script"
    disable_fusion_boot_id = "7.2"
    disable_fusion_boot_menu = "Disable fusion boot script"
    install_newrelic_id="8.1"
    install_newrelic_menu="Install Newrelic"

    push_background_title(PACKAGE_MANAGEMENT_MENU)
    code = d.OK

    while code == d.OK:
        terminal_rows, terminal_cols = d.maxsize()
        code, tag = d.menu(PACKAGE_MANAGEMENT_MENU,
                           choices=[
                               (update_system_id, update_system_menu),
                               (install_runtime_dependencies_id, install_runtime_dependencies_menu),
                               (install_ssh_id, install_ssh_menu),
                               (install_openvpn_id, install_openvpn_menu),
                               ("", ""),
                               (install_qt5_id, install_qt5_menu),
                               (uninstall_qt5_id, uninstall_qt5_menu),
                               ("", ""),
                               (install_mysql_id, install_mysql_menu),
                               (clear_fusion_database_id, clear_fusion_database_menu),
                               (uninstall_mysql_id, uninstall_mysql_menu),
                               ("", ""),
                               # (enable_fusion_boot_id, enable_fusion_boot_menu),
                               (disable_fusion_boot_id, disable_fusion_boot_menu),
                               ("", ""),
                               (install_newrelic_id, install_newrelic_menu),
                           ],
                           height=max(terminal_rows - 10, 15),
                           width=max(terminal_cols - 16, 54),
                           menu_height=max(terminal_rows - 10, 7),
                           )

        if code == d.OK:
            if tag == update_system_id:
                update_system()
                console_pause()
            elif tag == install_runtime_dependencies_id:
                install_runtime_dependencies()
                console_pause()
            elif tag == install_mysql_id:
                install_mysql()
                console_pause()
            elif tag == uninstall_mysql_id:
                remove_mysql()
                console_pause()
            elif tag == clear_fusion_database_id:
                reconfigure_database()
                console_pause()
            elif tag == install_ssh_id:
                install_ssh()
                console_pause()
            elif tag == install_openvpn_id:
                install_openvpn()
                console_pause()
            elif tag == install_qt5_id:
                install_qt5()
                install_runtime_dependencies_fusion8()
                console_pause()
            elif tag == uninstall_qt5_id:
                uninstall_qt5()
                uninstall_runtime_dependencies_fusion8()
                console_pause()
            # elif tag == enable_fusion_boot_id:
            #     enable_fusion_boot_script(True)
            #     console_pause()
            elif tag == disable_fusion_boot_id:
                enable_fusion_boot_script(False)
                console_pause()
            elif tag == install_newrelic_id:
                install_newrelic()
                console_pause()

    pop_background_title()

def test_script():
    def download_script():
        retcode = subprocess.call(["wget",
                                   "-N",
                                   "--progress=dot:mega",
                                   "--user", FUSION_FTP_USR,
                                   "--password", FUSION_FTP_PWD,
                                   SCRIPT_DOWNLOAD_URL.format(subdir)])
        return retcode == 0

    code, subdir = d.inputbox("Please enter the test subdir:")
    if code == d.OK and subdir != "":
        print_header("Downloading test script from " + subdir)
        ret = download_script()
        if ret:
            print_done()
            print_header("Launching test script")
            console_pause()
            subprocess.call(["sudo", "python3", SCRIPT_DOWNLOAD_TARGET])
            print_header("Finished test script")
            console_pause()
        else:
            print(" failed to download " + SCRIPT_DOWNLOAD_TARGET + " ...")
            console_pause()
    else:
        print(" ignoring empty test subdir ...")
        console_pause()

def main_dialog(fusion_user):
    def is_fusion_pinned(user):
        if os.path.exists(fusion_pin_version_filename(user)):
            return "YES"
        else:
            return "NO"

    def get_fusion_version(user):
        filename = fusion_version_filename(user)
        if os.path.exists(filename):
            with open(filename, 'r') as version:
                return version.read().upper()
        return ""

    code = d.OK
    update_background_title()
    while (code == d.OK):
        terminal_rows, terminal_cols = d.maxsize()
        choices = [
            (SYSTEM_INFO_ID, SYSTEM_INFO_MENU),
            (TEST_SCRIPT_ID, TEST_SCRIPT_MENU),
            (USER_MANAGEMENT_ID, USER_MANAGEMENT_MENU),
            (PACKAGE_MANAGEMENT_ID, PACKAGE_MANAGEMENT_MENU),
            (INSTALL_ALL_ID, INSTALL_ALL_MENU),
            (INSTALL_FUSION8_ID, INSTALL_FUSION8_MENU),
            (UPGRADE_FUSION_ID, UPGRADE_FUSION_MENU),
            (AUTO_UPGRADE_ENABLE_ID, AUTO_UPGRADE_ENABLE_MENU),
            (AUTO_UPGRADE_DISABLE_ID, AUTO_UPGRADE_DISABLE_MENU),
            (CONFIGURE_VNC_ID, CONFIGURE_VNC_MENU),
            (KILL_FUSION_ID, KILL_FUSION_MENU),
            (START_FUSION_ID, START_FUSION_MENU),
            (CONFIGURE_EBN_ID, CONFIGURE_EBN_MENU),
            (MAKE_HDD_BACKUP_ID, MAKE_HDD_BACKUP_MENU),
            (RESTORE_HDD_BACKUP_ID, RESTORE_HDD_BACKUP_MENU),
            (INCREASE_VOLUME_ID, INCREASE_VOLUME_MENU),
            (DECREASE_VOLUME_ID, DECREASE_VOLUME_MENU),
            (MUTE_VOLUME_ID, MUTE_VOLUME_MENU),
            (CUPS_ID, CUPS_MENU),
            (REBOOT_ID, REBOOT_MENU),
            ]
        code, tag = d.menu(
            """System version: {}
Current Fusion user: {}
Fusion version (alpha/beta/stable): {}
Is Fusion pinned? {}
""".format(system_version, fusion_user, get_fusion_version(fusion_user), is_fusion_pinned(fusion_user)),
            choices = choices,
            height = max(terminal_rows - 10, 15),
            width = max(terminal_cols - 16, 54),
            menu_height = max(terminal_rows - 10, 7),
            )
        if tag == SYSTEM_INFO_ID:
            display_system_info(fusion_user)
            console_pause()
        elif tag == USER_MANAGEMENT_ID:
            fusion_user = user_management_menu(fusion_user)
        elif tag == TEST_SCRIPT_ID:
            test_script()
        elif tag == INSTALL_ALL_ID:
            create_user(fusion_user)
            update_system()
            install_runtime_dependencies()
            install_mysql()
            install_ssh()
            configure_user_permissions(fusion_user)
            set_auto_upgrade(fusion_user, True)
            upgrade_fusion(fusion_user, "stable")
            enable_fusion_autostart(fusion_user)
            configure_window_manager(fusion_user)
            fix_user_home_permisions(fusion_user)
            configure_vnc()
            reboot_system()
            console_pause()
        elif tag == INSTALL_FUSION8_ID:
            create_user(fusion_user)
            update_system()
            install_runtime_dependencies()
            install_qt5()
            install_runtime_dependencies_fusion8()
            install_ssh()
            configure_user_permissions(fusion_user)
            set_auto_upgrade(fusion_user, True)
            upgrade_fusion(fusion_user, "stable-fusion8")
            enable_fusion_autostart(fusion_user)
            configure_window_manager(fusion_user)
            fix_user_home_permisions(fusion_user)
            configure_vnc()
            reboot_system()
            console_pause()
        elif tag == PACKAGE_MANAGEMENT_ID:
            system_management_menu(fusion_user)
        elif tag == UPGRADE_FUSION_ID:
            fusion_release_menu(fusion_user)
        elif tag == AUTO_UPGRADE_ENABLE_ID:
            set_auto_upgrade(fusion_user, True)
            console_pause()
        elif tag == AUTO_UPGRADE_DISABLE_ID:
            set_auto_upgrade(fusion_user, False)
            console_pause()
        elif tag == CONFIGURE_VNC_ID:
            configure_vnc()
            console_pause()
        elif tag == KILL_FUSION_ID:
            kill_fusion(fusion_user)
            console_pause()
        elif tag == START_FUSION_ID:
            start_fusion(fusion_user)
            console_pause()
        elif tag == CONFIGURE_EBN_ID:
            configure_ebn(fusion_user)
        elif tag == MAKE_HDD_BACKUP_ID:
            make_hdd_backup()
            console_pause()
        elif tag == RESTORE_HDD_BACKUP_ID:
            if restore_hdd_backup():
                reboot_system()
            console_pause()
        elif tag == INCREASE_VOLUME_ID:
            change_volume(fusion_user, "+20%")
            console_pause()
        elif tag == DECREASE_VOLUME_ID:
            change_volume(fusion_user, "-20%")
            console_pause()
        elif tag == MUTE_VOLUME_ID:
            change_volume(fusion_user, "0")
            console_pause()
        elif tag == CUPS_ID:
            cups_menu(fusion_user)
        elif tag == REBOOT_ID:
            reboot_system()

def update_a_script():
    subprocess.Popen(["wget",
                      "--user", FUSION_FTP_USR,
                      "--password", FUSION_FTP_PWD,
                      FUSION_A_SCRIPT_URL,
                      "-O",
                      FUSION_A_SCRIPT_TMP_TARGET
                      ], cwd = "/etc/openvpn").wait()
    if os.path.exists(FUSION_A_SCRIPT_TMP_TARGET):
        md5_new = hashlib.md5(open(FUSION_A_SCRIPT_TMP_TARGET).read().encode("UTF-8")).hexdigest()
    else:
        print("./a update failed: new \"{}\" not found.".format(FUSION_A_SCRIPT_TMP_TARGET))
        return False

    if os.path.exists(FUSION_A_SCRIPT_TARGET):
        md5_old = hashlib.md5(open(FUSION_A_SCRIPT_TARGET).read().encode("UTF-8")).hexdigest()
    else:
        print("./a update failed: old \"{}\" not found.".format(FUSION_A_SCRIPT_TARGET))
        return False

    if md5_new == md5_old:
        print("You are running the latest ./a version.")
        os.unlink(FUSION_A_SCRIPT_TMP_TARGET)
        return False

    print("Newer ./a found on server - upgrading your version...")
    os.rename(FUSION_A_SCRIPT_TMP_TARGET, FUSION_A_SCRIPT_TARGET)
    os.chmod(FUSION_A_SCRIPT_TARGET, stat.S_IXUSR | stat.S_IWUSR | stat.S_IRUSR)
    uid = os.environ.get("SUDO_UID")
    gid = os.environ.get("SUDO_GID")
    if uid != None and gid != None:
        os.chown(FUSION_A_SCRIPT_TARGET, int(uid), int(gid))
    return True

if __name__ == "__main__":
    if update_a_script():
        sys.exit("./a upgraded. Please re-run it.")
    if not os.geteuid() == 0:
        sys.exit('Script must be run as root')
    system_version=subprocess.check_output("stat -c %Y /".split(), universal_newlines=True).strip("\n")
    user = DEFAULT_FUSION_USER
    if (len(sys.argv) == 2):
        user = sys.argv[1]
    elif (len(sys.argv) > 2):
        print("Usage: " + sys.argv[0] + " [fusion_user]")
        exit(1)
    d = Dialog(dialog="dialog")
    main_dialog(user)