#!/usr/bin/python
import sys, os, configobj, re, pwd, subprocess, gettext, StringIO, tempfile

from gettext import gettext as _
gettext.textdomain("arkose-wrapper-gui")

if len(sys.argv) == 1:
    print(_("Usage: %s <profile> [start]") % sys.argv[0])
    sys.exit(1)

if not os.path.exists(sys.argv[1]):
    print(_("File not found: %s") % sys.argv[1])
    sys.exit(1)

profile_schema=[
    'cmd',
    'runas',
    'network',
    'xserver',
    'dbus',
    'pulseaudio',
    'video',
    'mount_bind',
    'mount_cow',
    'mount_restrict']

# Get the current user
user=pwd.getpwuid(int(os.getenv("PKEXEC_UID",os.getenv("UID",1000))))

# Initialize the config
running_config = open(sys.argv[1],"r").read()
config=None
name=None
profile=None
dbus=None
dbusproxy=None

def refresh_config():
    global config, name, profile, dbus, dbusproxy

    config=configobj.ConfigObj(StringIO.StringIO(running_config))
    name=os.path.split(sys.argv[1])[-1]
    profile=config['container']

    if "dbus-proxy" in config:
        dbus=config['dbus-proxy']
    else:
        dbus=None

    if not set(profile) == set(profile_schema):
        print(_("Invalid profile"))
        sys.exit(1)

    # Some type conversion
    if profile['network'] in ("true","True"):
        profile['network'] = True
    elif profile['network'] in ("false","False"):
        profile['network'] = False

    profile["pulseaudio"] = profile["pulseaudio"] in ("true","True")
    profile["video"] = profile["video"] in ("true","True")

    if not "devices" in profile:
        profile['devices']=[]

    if dbus and "session" in dbus:
        dbusproxy=dbus['session']
        if type(dbusproxy) != list:
            dbusproxy=[dbusproxy]
    else:
        dbusproxy=['*;*;*;*']

    for mount in ("mount_bind","mount_cow","mount_restrict"):
        if not profile[mount]:
            profile[mount]=[]
        elif type(profile[mount]) == str:
            profile[mount]=[profile[mount]]

        for entry in profile[mount]:
            new=re.sub("^\$HOME/","%s/" %
                user.pw_dir,
                entry)
            new=re.sub("\$USER","%s" %
                user.pw_name,
                new)

            profile[mount].remove(entry)
            profile[mount].append(new)

refresh_config()

if len(sys.argv) == 3 and sys.argv[2] == "start":
    # Work around pkexec not keeping PWD
    if 'PWD' in os.environ:
        os.chdir(os.getenv("PWD"))
    currentdir=os.path.split(os.path.realpath(__file__))[0]
    if os.path.exists(os.path.join(currentdir, "../arkose/__init__.py")):
        sys.path.insert(0, os.path.join(currentdir, ".."))

    if not os.geteuid() == 0:
        print(_("You must be root to start this command"))
        sys.exit(1)

    if profile["dbus"] in ("session","both") and not os.getenv("DBUS_SESSION_BUS_ADDRESS",None):
        print(_("Asked for dbus session support but no DBUS_SESSION_BUS_ADDRESS"))
        sys.exit(1)

    if profile["runas"] == "user" and not os.getenv("PKEXEC_UID",None):
        print(_("Missing PKEXEC_UID"))
        sys.exit(1)

    if "/tmp" in sys.argv[1]:
        os.remove(sys.argv[1])

    # We seem to be ready, let's start
    import arkose

    if profile['video']:
        import glob
        profile['devices']+=glob.glob("/dev/video*")

    dbusproxy_config=[]
    for entry in dbusproxy:
        if re.match("^.*;.*;.*;.*$", entry):
            # Append valid dbusproxy rules
            dbusproxy_config.append(entry)
        else:
            for path in ["dbus-proxy/profiles/%s.conf" % entry, "/usr/share/arkose/dbus-profiles/%s.conf" % entry]:
                # When not valid, check if that's a valid profile name
                if os.path.exists(path):
                    # Parse the profile and append the new rules
                    for line in open(path):
                        if re.match("^.*;.*;.*;.*$", line):
                            dbusproxy_config.append(line)

                    # Only parse one file if multiple exist
                    break

    container=arkose.ArkoseContainer(
        dbus=profile["dbus"],
        dbusproxy=dbusproxy_config,
        xserver=profile["xserver"],
        network=profile["network"],
        pulseaudio=profile["pulseaudio"],
        devices=profile['devices'],
        bind=profile["mount_bind"],
        cow=profile["mount_cow"],
        restrict=profile["mount_restrict"]
    )

    try:
        if profile["runas"] == "user":
            container.run_command("su %s -c \"%s\"" % (user.pw_name, profile["cmd"]))
        else:
            subprocess.Popen(["xhost","+SI:localuser:root"]).wait()
            container.run_command("%s" % profile["cmd"])
            subprocess.Popen(["xhost","-SI:localuser:root"]).wait()
        container.cleanup()
    except KeyboardInterrupt:
        container.cleanup()

    # Done, exiting
    sys.exit(0)

# Otherwise, start the UI
try:
    from gi.repository import Gtk as gtk
except:
    import gtk

builder = gtk.Builder()
builder.set_translation_domain("arkose-wrapper-gui")
if os.path.exists("wrapper/arkose-wrapper-gui.xml"):
    xml_file="wrapper/arkose-wrapper-gui.xml"
elif os.path.exists("/usr/share/arkose/gui/arkose-wrapper-gui.xml"):
    xml_file="/usr/share/arkose/gui/arkose-wrapper-gui.xml"
else:
    print(_("Unable to find .xml UI file"))
    sys.exit(1)

builder.add_from_file(xml_file)

winArkose = builder.get_object("winArkose")
winArkoseEdit = builder.get_object("winArkoseEdit")
tvConfig = builder.get_object("tvConfig")

configText = gtk.TextBuffer()

tvConfig.set_buffer(configText)
lblWarning = builder.get_object("lblWarning")
lblAccess = builder.get_object("lblAccess")
start = False

def refresh_ui():
    winArkose.set_title(_("Starting %s") % name)

    lblWarning.set_markup(
"<b>%s</b>" % _("Restricted application: %s") % name + "\n<small>" +
_("""In order to work properly, the application you started requires
some of the access rights listed below.
Please review them. If approved, click Execute.""") + "</small>")

    access = "<b>%s</b>" % _("Rights:")
    access += "\n - %s" % (_("Run the following command: <b>%s</b>") % profile['cmd'])
    if profile['runas'] == 'root':
        access += "\n - %s" % (_("Run software as the <b>root user</b>."))

    if profile['network'] == "direct":
        access += "\n - %s" % (_("Direct and unlimited access to the network"))
    elif profile['network'] == "filtered":
        access += "\n - %s" % (_("Filtered access to the network"))

    if profile['xserver'] == "direct":
        access += "\n - %s" % (_("Direct access to your X server"))
    elif profile['xserver'] == "isolated":
        access += "\n - %s" % (_("Isolated access to the X server"))

    if profile['dbus'] in ("session","both"):
        access += "\n - %s" % (_("Access the DBUS session bus"))

    if profile['dbus'] in ("system","both"):
        access += "\n - %s" % (_("Access the DBUS system bus"))

    if profile['pulseaudio']:
        access += "\n - %s" % (_("Play/Record sound (pulseaudio)"))

    if profile['video']:
        access += "\n - %s" % (_("Access your video devices (webcam)"))

    if len(profile['mount_bind']) > 0 or\
       len(profile['mount_cow']) > 0 or\
       len(profile['mount_restrict']) > 0:
        access += "\n\n<b>%s</b>" % _("Files and directories:")
        if len(profile['mount_bind']) > 0:
            access += "\n - %s" % _("Direct access: <b>%s</b>") % ", ".join(profile['mount_bind'])
        if len(profile['mount_cow']) > 0:
            access += "\n - %s" % _("Copy-on-Write: <b>%s</b>") % ", ".join(profile['mount_cow'])
        if len(profile['mount_restrict']) > 0:
            access += "\n - %s" % _("Temporary: <b>%s</b>") % ", ".join(profile['mount_restrict'])

    if len(dbusproxy) > 0:
        access += "\n\n<b>%s</b>" % _("DBUS session bus access:")
        for rule in dbusproxy:
            if rule == "*;*;*;*":
                access += "\n - %s" % _("Everything")
            else:
                access += "\n - %s" % rule

    lblAccess.set_markup(access)

refresh_ui()

def execute(button):
    global start
    start=True
    winArkose.hide()
    gtk.main_quit()

def apply_edit(button):
    global running_config
    running_config=configText.get_text(configText.get_start_iter(), configText.get_end_iter(), True)
    winArkoseEdit.hide()

    # Refresh the UI
    refresh_config()
    refresh_ui()

def hide_edit(button):
    configText.set_text(running_config)
    winArkoseEdit.hide()

def show_edit(button):
    configText.set_text(running_config)
    winArkoseEdit.show()

builder.connect_signals({
    "on_winArkose_destroy" : gtk.main_quit,
    "on_btnCancel_clicked" : gtk.main_quit,
    "on_btnCancel1_clicked" : hide_edit,
    "on_btnEdit_clicked" : show_edit,
    "on_btnExec_clicked" : execute,
    "on_btnApply_clicked" : apply_edit,
})

winArkose.show()
gtk.main()

if start:
    env_whitelist=["PWD","PATH","HOME","UBUNTU_MENUPROXY"]
    if profile["xserver"] != "none":
        env_whitelist+=["DISPLAY","XAUTHORITY"]

    if profile["pulseaudio"]:
        env_whitelist+=["PULSE_SERVER"]

    if profile["dbus"] in ("session","both"):
        env_whitelist.append("DBUS_SESSION_BUS_ADDRESS")

    env=[]
    for variable in env_whitelist:
        if variable == "UBUNTU_MENUPROXY" and profile['xserver'] == "isolated":
            # Global menu doesn't understand windows rendered on another X server
            continue

        value=os.getenv(variable,None)
        if value:
            env.append("%s=%s" % (variable, value))

    if profile['xserver'] == "isolated":
        screen = winArkose.get_screen()
        env.append("XPRA_XVFB_RESOLUTION=%sx%s" % (screen.get_width(),screen.get_height()))

    config_path=tempfile.mktemp()
    tmpconfig=open(config_path,"w+")
    tmpconfig.write(running_config)
    tmpconfig.close()

    cmd=["pkexec","env"]
    cmd+=env
    cmd+=[os.path.realpath(sys.argv[0]),os.path.realpath(config_path),"start"]

    os.execv("/usr/bin/pkexec",cmd)
