#!/usr/bin/python
# chickenfoot-launcher - run chickenfoot scripts from command line
# Copyright (C) 2007,2010  Timo Lindfors <timo.lindfors@iki.fi>

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import os,sys,shutil,sys,tempfile,subprocess

opt = {}
opt["fakehome"] = "%s/.chickenfoot-launcher-home" % os.getenv("HOME")
opt["name"] = "chickenfoot-launcher"
opt["chickenfooturl"] = "http://groups.csail.mit.edu/uid/chickenfoot/"
opt["verbose"] = 0

def dbg(msg):
    global opt
    if opt["verbose"] > 1:
        sys.stderr.write("dbg: %s\n" % msg)
def verbose(msg):
    global opt
    if opt["verbose"] > 0:
        sys.stderr.write("%s\n" % msg)
def generateVncPassword():
    dbg("generateVncPassword")
    fd = open("/dev/random")
    key = fd.read(8)
    fd.close()
    fd = os.open("%s/.vnc/passwd" % opt["fakehome"], os.O_RDWR)
    os.write(fd, key)
    os.close(fd)
def startVnc():
    dbg("startVnc")
    cmd = "HOME=%s vncserver -geometry 1280x1024" % opt["fakehome"]
    dbg("cmd %s" % cmd)
    out, err = subprocess.Popen(cmd, stdin = None, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True).communicate()
    dbg("err: %s" % err)
    dbg("out: %s" % out)
    firstLine = err.split("\n")[1]
    dbg("firstLine %s" % firstLine)
    lastWord = firstLine.split(" ")[-1]
    dbg("lastWord %s" % lastWord)
    if lastWord.find(":") < 0:
        return False
    return lastWord
def stopVnc(display):
    dbg("stopVnc")
    cmd = "HOME=%s vncserver -kill %s" % (opt["fakehome"], display)
    out, err = subprocess.Popen(cmd, stdin = None, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True).communicate()
    dbg("err: %s" % err)
    dbg("out: %s" % out)
def updateFakeHome():
    global opt
    dbg("updateFakeHome")
    if not os.path.exists(opt["fakehome"]):
        os.mkdir(opt["fakehome"])
        os.mkdir("%s/.vnc" % opt["fakehome"])
        fd = os.open("%s/.vnc/passwd" % opt["fakehome"], os.O_CREAT | os.O_RDWR, 0600)
        os.close(fd)
        fd = os.open("%s/.vnc/xstartup" % opt["fakehome"], os.O_CREAT | os.O_RDWR, 0700)
        os.write(fd, "#!/bin/sh\nwmaker &")
        os.close(fd)

        print "Created new fake home directory %s" % opt["fakehome"]
        print "Please configure your browser in this new home directory:"
        print "1) Start %s again without any arguments" % opt["name"]
        print "2) Connect to VNC using instructions printed by the above command"
        print "3) install chickenfoot extension inside VNC from %s" % opt["chickenfooturl"]
        print "4) Check that you can use the service you wish to automate"
        print "   (and that the browser does not ask if you wish to submit data to a form or something equally silly)"
        return False
    shutil.copyfile("%s/.Xauthority" % os.getenv("HOME"), "%s/.Xauthority" % opt["fakehome"])
    generateVncPassword()
    return True
def startBrowser(display, extraArgs, browser="firefox"):
    global opt
    dbg("startBrowser")
    cmd = "DISPLAY=%s HOME=%s %s -width 1280 -height 1024 %s" % (display, opt["fakehome"], browser, extraArgs)
    dbg("cmd %s" % cmd)
    out, err = subprocess.Popen(cmd, stdin = None, stdout = subprocess.PIPE, stderr = subprocess.PIPE, shell = True).communicate()
    dbg("err: %s" % err)
    dbg("out: %s" % out)
def vncConnectCmd(display):
    return "xvncviewer -passwd %s/.vnc/passwd %s" % (opt["fakehome"], display)
def parseArgs(argv):
    ret = []
    for i in range(len(argv)):
        if argv[i] == '-v':
            opt["verbose"] += 1
        else:
            ret += [ argv[i] ]
    return ret

def main(argv):
    # lockfile?
    # kill vnc on errors
    # handle errors better in general

    argv = parseArgs(argv)

    if not updateFakeHome():
        sys.exit(1)

    display = startVnc()
    if not display:
        print "Starting vnc failed"
        sys.exit(1)

    if len(argv) == 1:
        print "No script given, starting browser anyway. You can connect to VNC using"
        print vncConnectCmd(display)
        startBrowser(display, "")
    else:
        argvStr = "var ARGV = [%s];\n" % ",".join(map(lambda x: "\"%s\"" % x, argv[1:]))
        dbg("argvStr %s" % argvStr)

        (fd, outName) = tempfile.mkstemp(".stdout", "foot")
        os.close(fd)
        outStr = "function launcherPrint(s) { append(\"%s\",s); output(s); }\n" % outName
        dbg("outStr %s" % outStr)
        
        (fd, errName) = tempfile.mkstemp(".stderr", "foot")
        os.close(fd)
        errStr = "function launcherPrintErr(s) { append(\"%s\",s); output(\"err:\" + s); }\n" % errName
        dbg("errStr %s" % errStr)

        scriptStr = open(argv[1]).read()

        (fd, scriptName) = tempfile.mkstemp(".js", "foot")
        os.close(fd)
        script = open(scriptName, "w")
        script.write(argvStr)
        script.write(outStr)
        script.write(errStr)
        script.write(scriptStr)
        script.close()
        dbg("scriptName %s" % scriptName)
        
        verbose("Starting browser with script %s. You can connect to VNC using" % scriptName)
        verbose(vncConnectCmd(display))
        startBrowser(display, "-cf-run %s" % scriptName)
        
        stdout = open(outName).read()
        dbg("stdout %s" % stdout)
        sys.stdout.write(stdout)
        
        stderr = open(errName).read()
        dbg("stderr %s" % stderr)
        sys.stderr.write(stderr)

        os.unlink(scriptName)
        os.unlink(outName)
        os.unlink(errName)
        
        
    stopVnc(display)

main(sys.argv)


# TODO:
# more than one instance
# cleanup if after failure
# interactive IO
