#!/usr/bin/python
# @package      hubzero-vncproxyd-ws
# @file         hzvncproxyd-ws-config
# @author       Nicholas J. Kisseberth <nkissebe@purdue.edu>
# @copyright    Copyright (c) 2014-2015 HUBzero Foundation, LLC.
# @license      http://www.gnu.org/licenses/lgpl-3.0.html LGPLv3
#
# Copyright (c) 2014-2015 HUBzero Foundation, LLC.
#
# This file is part of: The HUBzero(R) Platform for Scientific Collaboration
#
# The HUBzero(R) Platform for Scientific Collaboration (HUBzero) is free
# software: you can redistribute it and/or modify it under the terms of
# the GNU Lesser General Public License as published by the Free Software
# Foundation, version 3.
#
# HUBzero 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# HUBzero is a registered trademark of HUBzero Foundation, LLC.
#

import re, glob, string, ConfigParser, sys, MySQLdb, time, warnings, argparse, os, pwd, grp, subprocess

debug = False

os.umask(0027)

def configureVncproxyd_ws(args):

    uid = os.geteuid()

    if uid != 0:            
        print 'hzvncproxyd-ws-config configure: must be run with root privileges'
        return 1

    if args.enable:
        return enableVncproxyd_ws(args)
    else:
        return disableVncproxyd_ws()


def enableVncproxyd_ws(args):

    print "Configuring hubzero-vncproxyd-ws"

    try:
        grp.getgrnam('hzvncproxy')
    except KeyError:
        print 'Creating hzvncproxy group'

        adduser = ["/usr/sbin/groupadd", "--system", "hzvncproxy"]

        try:
            proc = subprocess.Popen(adduser, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    
            procStdOut, procStdErr = proc.communicate()

            rc = proc.returncode

        except Exception, ex:   
            print ex
            raise
            #raise Exception('exShellCommand error ' + str(argArray) + ' ' + str(ex) + "\n" + traceback.format_exc())

        if rc != 0:
            print procStdOut
            print procStdErr
            return 1

    try:
        pwd.getpwnam('hzvncproxy')
    except KeyError:
        print 'Creating hzvncproxy user'

        adduser = ["/usr/sbin/useradd", "--system", "--home-dir" , "/var/lib/hzvncproxy", "--gid", "hzvncproxy", "--shell", "/bin/false", "--comment", "HUBzero VNC Proxy Service", "hzvncproxy"]

        try:
            proc = subprocess.Popen(adduser, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    
            procStdOut, procStdErr = proc.communicate()

            rc = proc.returncode

        except Exception, ex:   
            print ex
            raise
            #raise Exception('exShellCommand error ' + str(argArray) + ' ' + str(ex) + "\n" + traceback.format_exc())

        if rc != 0:
            print procStdOut
            print procStdErr
            return 1

    try:
        h_uid = pwd.getpwnam('hzvncproxy').pw_uid
        h_gid = grp.getgrnam('hzvncproxy').gr_gid

        if not os.path.exists('/var/lib/hzvncproxy'):
            print "Creating /var/lib/hzvncproxy/"
            os.mkdir('/var/lib/hzvncproxy',0755)
            os.chown('/var/lib/hzvncproxy',h_uid,h_gid)
        if not os.path.exists('/etc/hzvncproxyd-ws'):
            print "Creating /etc/hzvncproxyd-ws/"
            os.mkdir('/etc/hzvncproxyd-ws',0755)
            os.chown('/etc/hzvncproxyd-ws',0,0)
        if not os.path.exists('/etc/hzvncproxyd-ws/targets'):
            print "Creating /etc/hzvncproxyd-ws/targest/"
            os.mkdir('/etc/hzvncproxyd-ws/targets',0750)
            os.chown('/etc/hzvncproxyd-ws/targets',h_uid,h_gid)
        if not os.path.exists('/var/log/hzvncproxyd-ws'):
            print "Creating /var/log/hzvncproxyd-ws"
            os.mkdir('/var/log/hzvncproxyd-ws',0755)
            os.chown('/var/log/hzvncproxyd-ws',0,0)
        if not os.path.exists('/var/log/hzvncproxyd-ws/daily'):
            print "Creating /var/log/hzvncproxyd-ws"
            os.mkdir('/var/log/hzvncproxyd-ws/daily',0755)
            os.chown('/var/log/hzvncproxyd-ws/daily',0,0)

        os.chown('/var/lib/hzvncproxy',h_uid,-1)
        os.chown('/etc/hzvncproxyd-ws',0,-1)
        os.chown('/etc/hzvncproxyd-ws/targets',h_uid,-1)
        os.chown('/var/log/hzvncproxyd-ws',0,-1)
        os.chown('/var/log/hzvncproxyd-ws/daily',0,-1)

    except Exception as e:
        print "Unable to configure required directories"
        print e
        if debug:
            raise
        return 1

    filename = '/etc/hzvncproxyd-ws/targets/default.conf'

    print "Configuring %s " % (filename)

    try:
        if not os.path.exists(filename):
            mode = 'w'
        else:
            mode = 'r+'

        with open(filename, mode) as f:
            ftxt = f.read()

            (txt, count) = re.subn(r'(?im)^\s*#.*$',r'',ftxt)
            (txt, count) = re.subn(r'(?im)^(?:[\t ]*(?:\r?\n|\r))+' ,'',txt)

            if len(txt) == 0:
                hzconfig = ConfigParser.ConfigParser()

                if hzconfig.read('/etc/hubzero.conf') == []:
                    print "Unable to continue, failed to read /etc/hubzero.conf."
                    return 1
                if hzconfig.has_option('DEFAULT','site'):
                    site = hzconfig.get('DEFAULT','site')
                else:
                    print "Unable to continue, failed to find default hub site in /etc/hubzero.conf"
                    return 2

                if hzconfig.has_option(site,'documentroot'):
                    document_root = hzconfig.get(site,'documentroot')
                else:
                    print "Unable to continue, no document root set for default site in /etc/hubzero.conf"
                    return 3

                data = { 'cmsconfig' : {} }

                try:
                    for m in re.finditer("\s*(?:var|public)\s+\$(\w+)\s*=\s*\'*(.*?)\'*\s*\;\s*", open(document_root + '/configuration.php','r').read()):
                        data['cmsconfig'][m.group(1)] = m.group(2).strip(" \'\"\t")
                except Exception as e:
                    print "Unable to continue, failed to read '" +  document_root + "/configuration.php'"
                    return 4

                if 'db' not in data['cmsconfig'] or 'user' not in data['cmsconfig'] or 'password' not in data['cmsconfig']:
                    print "Unable to continue, missing configuration in  '" +  document_root + "/configuration.php'"
                    return 5

                if data['cmsconfig']['db'] == '' or data['cmsconfig']['user']  == '' or data['cmsconfig']['password'] == '':
                    print "Unable to continue, invalid configuration in  '" +  document_root + "/configuration.php'"
                    return 6

                data['cmsconfig']['password'] = data['cmsconfig']['password'].replace(';','\;')

                f.seek(0)
                f.write(ftxt)
                f.write("*:%s:mysql:host=%s;user=%s;password=%s;db=%s\n" % (site, data['cmsconfig']['host'], data['cmsconfig']['user'], data['cmsconfig']['password'], data['cmsconfig']['db']))
                f.truncate()
                print "%s configuration complete." % (filename)
            else:
                print "%s was already configured." % (filename)

            os.chown(filename,h_uid,h_gid)

    except Exception as e:
            print "Unable to configure %s" % (filename)
            print e
            if debug:
                raise
            return 1
 
    listen = ''        
    port = None
    host = None

    if os.path.exists("/etc/mw-client/mw-client.conf"):
        try:
            execfile('/etc/mw-client/mw-client.conf')
        except IOError, (errno, strerror):
            print "I/O error(%s): %s" % (errno, strerror)
            print "The configuration file /etc/mw-client/mw-client.conf needs to be readable"
            os._exit(1)

        try:
            port = SESSION_CONF['WS_PROXY_PORT']
        except:
            try:
                port = wsproxy_port
            except:
                port = None

        try:
            host = SESSION_CONF['WS_PROXY_SERVER']
        except:
            try:
                host = wsproxy_host
            except:
                host = None

        if port == None and host != None:
            listen = "LISTEN=" + host
        elif port != None and host != None:
            listen = "LISTEN=" + host + ":" + port
        elif port != None:
            listen = "LISTEN=:" + port

    filename = '/etc/default/hzvncproxyd-ws'

    print "Configuring %s " % (filename)

    try:

        if not os.path.exists(filename):
            mode = 'w+'
        else:
            mode = 'r+'

        with open(filename, mode) as f:
            ftxt = f.read()
            txt = ftxt.split("\n")

            if args.sslcert == '':
                if os.path.exists('/etc/hzvncproxys-ws/ssl-cert-hzvncproxyd-ws.pem'):
                    sslcert = ''
                elif os.path.exists('/etc/ssl/certs/ssl-cert-server.pem'):
                    sslcert = "SSL_CERTFILE=/etc/ssl/certs/ssl-cert-server.pem"
                elif os.path.exists('/etc/ssl/certs/ssl-cert-server.crt'):
                    sslcert = "SSL_CERTFILE=/etc/ssl/certs/ssl-cert-server.crt"
                elif os.path.exists('/etc/ssl/certs/server.pem'):
                    sslcert = "SSL_CERTFILE=/etc/ssl/certs/server.pem"
                elif os.path.exists('/etc/ssl/certs/server.crt'):
                    sslcert = "SSL_CERTFILE=/etc/ssl/certs/server.crt"
                elif os.path.exists('/etc/apache2/ssl/ssl-cert-server.pem'):
                    sslcert = "SSL_CERTFILE=/etc/apache2/ssl/ssl-cert-server.pem"
                elif os.path.exists('/etc/apache2/ssl/ssl-cert-server.crt'):
                    sslcert = "SSL_CERTFILE=/etc/apache2/ssl/ssl-cert-server.crt"
                elif os.path.exists('/etc/apache2/ssl/server.pem'):
                    sslcert = "SSL_CERTFILE=/etc/apache2/ssl/server.pem"
                elif os.path.exists('/etc/apache2/ssl/server.crt'):
                    sslcert = "SSL_CERTFILE=/etc/apache2/ssl/server.crt"
                else:
                    sslcert = ''
            elif args.sslcert == False or args.sslcert == None:
                sslcert = ''
            else:
                sslcert = "SSL_CERTFILE=" + args.sslcert

            if sslcert == '':
                if port == None:
                    port = 8443
                else:
                    port = 8080

            if args.sslkey == '':
                if os.path.exists('/etc/hzvncproxys-ws/ssl-cert-hzvncproxyd-ws.key'):
                    sslkey = ''
                elif os.path.exists('/etc/ssl/certs/ssl-cert-server.key'):
                    sslkey = "SSL_KEYFILE=/etc/ssl/certs/ssl-cert-server.key"
                elif os.path.exists('/etc/ssl/certs/server.key'):
                    sslkey = "SSL_KEYFILE=/etc/ssl/certs/server.key"
                elif os.path.exists('/etc/apache2/ssl/ssl-cert-server.key'):
                    sslkey = "SSL_KEYFILE=/etc/apache2/ssl/ssl-cert-server.key"
                elif os.path.exists('/etc/apache2/ssl/server.key'):
                    sslkey = "SSL_KEYFILE=/etc/apache2/ssl/server.pem"
                else:
                    sslkey = ''
            elif args.sslkey == False or args.sslkey == None:
                sslkey = ''
            else:
                sslkey = "SSL_KEYFILE=" + args.sslcert

            if args.listen == False:
                listen = '';
            else:
                listen = "LISTEN=%s" % (listen)

            last_listen = -1
            last_certfile = -1
            last_keyfile = -1

            for i in range(0,len(txt)):
                if re.match(r'^SSL_CERTFILE\s*=\s*(.*)$',txt[i]):
                    last_certfile = i
                if re.match(r'^SSL_KEYFILE\s*=\s*(.*)$',txt[i]):
                    last_keyfile = i
                if re.match(r'^LISTEN\s*=\s*(.*)$',txt[i]):
                    last_listen = i

            #print "last_certfile = %d" % last_certfile
            #print "last_keyfile = %d" % last_keyfile
            #print "last_listen = %d" % last_listen
            #print "last line= [%s]" % txt[len(txt)-1]

            if txt[len(txt)-1] <> "":
                txt.append("")

            if sslcert <> None and sslcert <> '':
                if last_certfile > -1:
                    txt[last_certfile] = sslcert
                else:
                    txt.append(sslcert)

            if sslkey <> None and sslkey <> '':
                if last_keyfile > -1:
                    txt[last_keyfile] = sslkey
                else:
                    txt.append(sslkey)

            if listen <> None and listen <> '':
                if last_listen > -1:
                    txt[last_listen] = listen
                else:
                    txt.append(listen)

            txt = '\n'.join(txt)

            if sslcert == '':
                txt = re.sub(r'(?im)^SSL_CERTFILE\s*=\s*(.*)(\n|$)','',txt)
            if sslkey == '':
                txt = re.sub(r'(?im)^SSL_KEYFILE\s*=\s*(.*)(\n|$)','',txt)
            if listen == '':
                txt = re.sub(r'(?im)^LISTEN\s*=\s*(.*)(\n|$)','',txt)

            #print "============"
            #print txt,
            #print "============"

            if txt == ftxt:
                print "%s was already configured." % (filename)
            else:
                f.seek(0)
                f.write(txt)
                f.truncate()
                print "%s configuration complete." % (filename)

    except Exception as e:
            print "Unable to configure %s" % (filename)
            print e
            raise
            if debug:
                raise
            return 1

    port = str(port)

    filename = "/etc/firewall_on"

    print "Configuring " + filename

    if os.path.exists(filename):

        with open(filename, mode) as f:
            ftxt = f.read()
            newtxt = ftxt.rstrip() + "\n"
            if not re.search(r'^\s*iptables\s+\-A\s+INPUT\s+\-p\s+tcp\s+\-\-dport\s+' + port + '\s+\-j\s+ACCEPT',ftxt,re.MULTILINE):
                if re.search(r'^\s*iptables\s+\-A\s+INPUT\s+\-p\s+tcp\s+\-\-dport\s+(443|https)\s+\-j\s+ACCEPT',ftxt,re.MULTILINE):
                    newtxt += "\niptables -A INPUT -p tcp --dport 8443 -j ACCEPT # hzvncproxyd-ws\n"
                else:
                    print "Contents of /etc/firewall_on are unexpected"
                    print "Add a rule like:"
                    print "    iptables -A INPUT -p tcp --dport " + port + "-j ACCEPT"
                    print "to your iptables firewall to allow connectivity to the hzvncproxyd-ws port"

                if (ftxt != newtxt):
                    f.seek(0)
                    f.write(newtxt)
                    f.truncate()
                    f.close()
                    print "%s configuration complete." % (filename)
                else:
                    print "%s was already configured." % (filename)
            else:
                print "%s was already configured." % (filename)
    else:
        print filename + " does not exist, nothing to be done"

    if not isLiveIptablesPortOpen(port):
        openLiveIptablesPort(port)

def getLiveIptablesInputRules():
    iptables = ["/sbin/iptables", "-L", "INPUT", "-n"]

    try:
        proc = subprocess.Popen(iptables, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

        procStdOut, procStdErr = proc.communicate()

        rc = proc.returncode

    except Exception, ex:
        raise

    if rc != 0:
        print procStdOut
        print procStdErr
        return None

    return procStdOut
   

def isLiveIptablesPortOpen(port = None):

    if port == None:
        print "No port specified, nothing to be done."
        return

    port = str(port)

    iptlist = getLiveIptablesInputRules()

    if iptlist == None:
        return None

    m = re.search(r'^ACCEPT\s+tcp.*?tcp\s+dpt:'+port,iptlist,re.MULTILINE)

    return m != None

def openLiveIptablesPort(port = None):

    if port == None:
        print "No port specified, nothing to be done."
        return

    port = str(port)

    print "Opening port " + port  + " on live iptables firewall"

    iptables = ["/sbin/iptables", "-A", "INPUT", "-p", "tcp", "--dport", port, "-j", "ACCEPT"]

    try:
        proc = subprocess.Popen(iptables, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

        procStdOut, procStdErr = proc.communicate()

        rc = proc.returncode

    except Exception, ex:
        print ex
        raise
        #raise Exception('exShellCommand error ' + str(argArray) + ' ' + str(ex) + "\n" + traceback.format_exc())

    if rc != 0:
        print procStdOut
        print procStdErr
        return 1


def closeLiveIptablesPort(port = None):

    if port == None:
        print "No port specified, nothing to be done."
        return

    port = str(port)

    print "Closing port " + port + " on live iptables firewall"

    iptables = ["/sbin/iptables", "-D", "INPUT", "-p", "tcp", "--dport", port, "-j", "ACCEPT"]

    try:
        proc = subprocess.Popen(iptables, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

        procStdOut, procStdErr = proc.communicate()

        rc = proc.returncode

    except Exception, ex:
        print ex
        raise
        #raise Exception('exShellCommand error ' + str(argArray) + ' ' + str(ex) + "\n" + traceback.format_exc())

    if rc != 0:
        print procStdOut
        print procStdErr
        return 1


    # -A OUTPUT -j LOG --log-prefix "EGRESS ALERT: " --log-level 7

    # capture current iptables
    # look for: ^ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8443"
    # if not found run
    # iptables -A INPUT -p tcp --dport 8443 -j ACCEPT

def disableVncproxyd_ws():
    print "Unconfiguring hubzero-vncproxyd-ws"

    filename = '/etc/default/hzvncproxyd-ws'

    print "Configuring %s " % (filename)

    try:

        if not os.path.exists(filename):
            mode = 'w+'
        else:
            mode = 'r+'

        with open(filename, mode) as f:
            ftxt = f.read()
            txt = ftxt.split("\n")

            if args.enable == None:
                enabled = None
            elif args.listen == False:
                enabled = '';
            else:
                enabled = "ENABLED=0"

            last_enable = -1

            for i in range(0,len(txt)):
                if re.match(r'^ENABLED\s*=\s*(.*)$',txt[i]):
                    last_enabled = i

            print "last_enabled = %d" % last_enabled
            print "last line= [%s]" % txt[len(txt)-1]

            if txt[len(txt)-1] <> "":
                txt.append("")

            if enabled <> None and enabled <> '':
                if last_enabled > -1:
                    txt[last_enabled] = enabled
                else:
                    txt.append(enabled)

            txt = '\n'.join(txt)

            if enabled == '':
                txt = re.sub(r'(?im)^ENABLED\s*=\s*(.*)(\n|$)','',txt)

            #print "============"
            #print txt,
            #print "============"

            if txt == ftxt:
                print "%s was already configured disabled." % (filename)
            else:
                f.seek(0)
                f.write(txt)
                f.truncate()
                print "%s configuration complete (disabled)." % (filename)

    except Exception as e:
            print "Unable to disable package through configuration file %s " % (filename)
            print e
            if debug:
                raise
            return 1

    return 0


parser = argparse.ArgumentParser(prog="hzvncproxyd-ws-config")
parser.add_argument('--debug', dest='debug', action='store_true', help='enable debugging output', default=False)

subparsers = parser.add_subparsers()

parser_configure = subparsers.add_parser('configure', help='configure hubzero-vncproxyd-ws package')
parser_configure.set_defaults(func=configureVncproxyd_ws)
action = parser_configure.add_mutually_exclusive_group(required=True)
action.add_argument('--enable', dest='enable', action='store_true', help='enable hubzero-vncproxyd-ws',default=True)
action.add_argument('--disable', dest='enable', action='store_false', help='disable hubzero-vncproxyd-ws configuration',default=False)
parser_configure.add_argument('--listen', dest='listen', action='store', help='host:port to lisent on',default=None)
parser_configure.add_argument('--ssl-cert', dest='sslcert', action='store', help='SSL Certificate File',nargs='?',const='')
parser_configure.add_argument('--ssl-key', dest='sslkey', action='store', help='SSL Certificate Key',nargs='?',const='')
parser_configure.add_argument('--no-ssl-key', dest='sslkey', action='store_false', help='SSL Certificate Key')
parser_configure.add_argument('--no-ssl-cert', dest='sslcert', action='store_false', help='SSL Certificate Key')
args =  parser.parse_args()

debug = args.debug

ret = args.func(args)

sys.exit(ret)
