#
# steuermann remote execution daemon - use this when you can't use
# something better (like ssh) to get your commands running in the
# other machine.
#
# Copyright 2012 Association of Universities for Research in Astronomy (AURA) 
#

import os
import time
import CGIHTTPServer
import BaseHTTPServer
import SocketServer
import platform
import subprocess
import urllib
import urlparse

os.chdir('/')
print(os.getcwd())

#####
#

valid_client_ip = ( 
    '192.168.3.11',       # arzach
    '172.16.31.10',        # ssb
    '192.168.127.12',      # banana
    '192.168.3.11',       # vxp-dukat
    )

#####
#
# This uses the python stock http server.  Here is a request handler
# that services GET requests by executing the command passed in.
# All other requests are invalid.

password = "<PASSWORD>"
 
class my_handler( CGIHTTPServer.CGIHTTPRequestHandler ) :

    def __init__(self, request, client_address, server) :
        # init the superclass
        CGIHTTPServer.CGIHTTPRequestHandler.__init__(self, request, client_address, server)

    def reject_client(self) :
        print(self.client_address)
        if not ( self.client_address[0] in valid_client_ip ) :
            self.bad_client('a')
            return 1

        if self.path.startswith('/' + password + '/' ) :
            self.path = self.path[len(password)+2:]
        else :
            self.bad_client('p')
            return 1

        return 0

    def do_GET(self) :
        # GET /password/command...
        #   run the command

        if self.reject_client() :
            return

        path = self.path

        print("GET",path)
        self.send_response(200)
        self.send_header('Content-type', 'text/plain')
        self.end_headers()
        self.wfile.write("Hello world: %s\n"%path)
        self.wfile.flush()

    def bad_client(self, why) :
        self.send_response(500)
        self.send_header('Content-type', 'text/plain')
        self.end_headers()
        self.wfile.write("bad client: %s\n"%why)
        self.wfile.flush()
        return

    def do_INVALID(self) :
        self.send_response(500)
        self.send_header('Content-type', 'text/plain')
        self.end_headers()
        self.wfile.write("error\n")
        self.wfile.flush()
        return

    def do_POST(self) :
        # POST password=<PASSWORD>&name=filename&data=filedata
        #   upload data to a file

        if self.reject_client() :
            return

        print(self.path)


        length = int(self.headers['Content-Length'])
        data = self.rfile.read(length)
        d = urlparse.parse_qs(data)

        for x in sorted([ x for x in d]) :
            print(x,d[x])

        if d['password'][0] != password :
            self.bad_client('p')
            return

        dirname = d['dirname'][0]
        print("CD",dirname)
        os.chdir(dirname)

        if self.path == 'upload' :

            filename = d['filename'][0]

            mode = 'wb'
            if 'mode' in d :
                t = d['mode'][0]
                if t == 't' or t == 'text' :
                    mode = 'w'
                elif t == 'b' or t == 'binary' :
                    mode = 'wb'
                else :
                    return self.bad_client('mode')

            f = open(filename,'wb')
            f.write(d['data'][0])
            f.close()

            self.send_response(200)
            self.send_header('Content-type', 'text/plain')
            self.end_headers()
            self.wfile.write("uploaded %s\n"%filename)
            self.wfile.flush()

        elif self.path == 'run' :
            cmd = d['cmd'][0]
            self.send_response(200)
            self.send_header('Content-type', 'text/plain')
            self.end_headers()
            self.wfile.write("Hello world: %s\n"%cmd)
            run_child(cmd, self.wfile)
            self.wfile.write("done\n")
            self.wfile.flush()
        else :
            return self.bad_client('c')


    def do_HEAD(self) :
        self.do_invalid()


class MultiThreadedHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
    pass

####

windows = platform.system() == 'Windows'

def run_child(path, wfile) :
    env = os.environ
    cmd = urllib.unquote_plus(path)
    print("COMMAND",cmd)

    # bug: implement timeouts
    if windows :
        status = subprocess_windows( cmd, wfile, env )
    else :
        status = subprocess_unix( cmd, wfile, env )

    # subprocess gives you weird status values
    if status > 0 :
        t_status="exit %d"%(status >> 8)
        if status != 0 :
            return_status = 1
    else :
        return_status = 1
        t_status="signal %d" % ( - status )
        # subprocess does not tell you if there was a core
        # dump, but there is nothing we can do about it.

    print("COMMAND EXIT:",status,t_status)


def subprocess_windows(cmd, wfile, env ) :
    # You might think that subprocess.Popen() would be portable,
    # but you would be wrong.
    #
    # On Windows, sockets are NOT file descriptors.  Since we are in a web server, wfile here is a socket.

    # print wfile.fileno()
    # import msvcrt
    # print msvcrt.get_osfhandle(wfile.fileno())
    p = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True, env = env, creationflags = subprocess.CREATE_NEW_PROCESS_GROUP )
    while 1 :
        n = p.stdout.read(256)
        if n == '' :
            break
        wfile.write(n)
    return p.wait()

def subprocess_unix( cmd, wfile, env ) :
    p = subprocess.Popen( cmd, stdout=wfile, stderr=wfile, shell=True, env = env, preexec_fn=os.setpgrp )
    return p.wait()

#####


def run( args = [ ] ) :
    # you could parse args here if you wanted to.  I don't care to spend
    # the time.  This is just here for people who can't (or don't want to)
    # install a full featured web server just to try things out.
    if len(args) > 0 :
        ip = args[0]
    else :
        ip = platform.node()

    port = 7070

    print("http://%s:%s/"%(str(ip),str(port)))

    httpd = MultiThreadedHTTPServer( (ip, port) , my_handler)

    sa = httpd.socket.getsockname()
    print("Serving HTTP on", sa[0], "port", sa[1], "...")
    while 1 :
        httpd.handle_request()

if __name__ == '__main__' :
    run()


##
# in case we need to kill child processes:
# http://code.activestate.com/recipes/347462-terminating-a-subprocess-on-windows/


