#!/usr/bin/python

import os
import sys
# Add lib directory to search path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../lib')))

import getopt
import log
import ed25519
import jsonpickle
import logging
import logging.config
import config
import argparse
import configargparse
import util
import pprint
import time
import services
import sdp
import sdps
import random
import string
import requests
import json
import socket
import re
import atexit
import dns

def parseUri(cfg, uri):
    # authid@provider/service
    # Where provider can be providerid or domain name
    # service can be serviceid or service name
    # authid can be ommited so it will be autogenerated
    p = re.search("(.*)@(.*)/(.*)", uri) # Uri with fqdn aaa@bb.cc/dd
    if (p):
        cfg.authId = p.group(1).upper()
        providerid = util.parseProvider(p.group(2))
        cfg.serviceId = p.group(3).upper()
        if not providerid:
            log.L.error("No TXT record for %s" % uri)
            sys.exit(1)
        else:
            cfg.providerid = providerid.upper()
    else:
        p = re.search("(.*)/(.*)", uri)
        if (p):
            cfg.uniqueId = "_random_"
            cfg.authId = "_random_"
            providerid = util.parseProvider(p.group(1))
            cfg.serviceId = p.group(2).upper()
            if not providerid:
                log.L.error("No TXT record for %s" % uri)
                sys.exit(1)
            else:
                cfg.providerid = providerid.upper()
        else:
            providerid = util.parseProvider(uri)
            return(cfg)
    if (cfg.authId != "_random_" and cfg.authId[:2] != cfg.serviceId):
        log.L.error("Authid must start with serviceid!")
        return(None)
    return(cfg)

def generateAuthId(sid):
    return (sid+''.join(random.choice('ABCDEF0123456789') for _ in range(14)))

def generateMgmtId():
    return (''.join(random.choice('ABCDEF0123456789') for _ in range(16)))

def loadService(pid, sid):
    if (not sid or not pid):
        log.L.error("You must specify serviceid and providerid!")
        return(None)
    else:
        s = sdps.SDPS.getProviderSDP(pid)
        if s:
            services.SERVICES.loadClient(s, sid)
            services.SERVICES.mgmt.disable()
            services.SERVICES.http.disable()
            if services.SERVICES.get(sid):
                return(True)
            else:
                log.L.error("Service id %s does not exists!" % (sid))
                return(None)
        else:
            log.L.error("Provider id %s does not exists!" % (pid))
            return(None)
    
# Starting here
def main(argv):
    config.CONFIG = config.Config("dummy")    
    p = configargparse.getArgumentParser(ignore_unknown_config_file_keys=True, fromfile_prefix_chars='@')
    util.commonArgs(p)
    p.add_argument('--authid',                 dest='authId', metavar='AUTHID', required=None, default=None, help='Authentication ID. Use "random" to generate.')
    p.add_argument('--uniqueid',               dest='uniqueId', metavar='UNIQUEID', required=None, default=None, help='Unique ID of proxy. Use "random" to generate.')
    p.add_argument('--stunnel-port',           dest='stunnelPort', metavar='PORT', required=None, default=8187, help='Use this stunnel local port for connections over proxy.')
    p.add_argument('--outbound-proxy-host',    dest='httpsProxyHost', metavar='HOST', required=None, default=None, help='Use this https proxy host.')
    p.add_argument('--outbound-proxy-port',    dest='httpsProxyPort', metavar='PORT', required=None, default=3128, help='Use this https proxy port.')
    p.add_argument('--proxy-port',             dest='proxyPort', metavar='PORT', required=None, default=8186, help='Use this port as local bind port for proxy.')
    p.add_argument('--proxy-bind',             dest='proxyBind', metavar='IP', required=None, default="127.0.0.1", help='Use this host as local bind for proxy.')
    p.add_argument('--connect-timeout',        dest='connectTimeout', metavar='S', required=None, default=30, help='Timeout for connect to service.')
    p.add_argument('--payment-timeout',        dest='paymentTimeout', metavar='S', required=None, default=1200, help='Timeout for payment to service.')
    p.add_argument('--exit-on-no-payment',     dest='exitNoPayment', action='store_const', const='vpncStandalone', metavar='Bool', required=None, default=None, help='Exit after payment is gone.')
    p.add_argument('--fork-on-connect',        dest='forkOnConnect', action='store_const', const='forkOnConnect', metavar='Bool', required=None, default=None, help='Fork after successful paid connection. Client will fork into background.')
    p.add_argument('--vpnc-tun',               dest='vpncTun', metavar='IF', required=None, default="tun1", help='Use specific tun device for client')
    p.add_argument('--vpnc-mgmt-port',         dest='vpncMgmtPort', metavar='PORT', required=None, default="11193", help='Use specific port for local Openvpn mgmt')
    p.add_argument('--proxyc-mgmt-port',       dest='proxyMgmtPort', metavar='PORT', required=None, default="11194", help='Use specific port for local Haproxy mgmt')
    p.add_argument('--proxyc-ssl-noverify',    dest='proxySSLNoVerify', action='store_const', const='proxySSLNoVerify', metavar='Bool', required=None, default=None, help='Do not verify SSL certificate of remote proxy. Dangerous, use only if you know what you are doing!')
    p.add_argument('--vpnc-block-route',       dest='vpncBlockRoute', action='store_const', const='vpncBlockRoute', metavar='Bool', required=None, default=None, help='Filter router changes from server')
    p.add_argument('--vpnc-block-dns',         dest='vpncBlockDns', action='store_const', const='vpncBlockDns', metavar='Bool', required=None, default=None, help='Filter router DNS server from server')
          
    p.add('cmd', metavar='{list|connect|help}', choices=["connect", "list","help"], help='Command to execute.')
        
    (cfg, args) = p.parse_known_args()
    util.parseCommonArgs(p, cfg, 'lthnvpnc')
    config.Config.CAP = cfg
    config.CONFIG = config.Config("dummy")
    cmd = cfg.cmd
    cfg.O = None
    cfg.L = None
    
    if (cmd == "help"):
        util.helpmsg(p)
        sys.exit()
    if (cmd == "list"):
        cfg.L = True
        if len(args)>0:
            for uri in args:
                cfg = parseUri(cfg, uri)
            
    elif (cmd == "connect"):
        cfg.O = True
        if len(args)==0:
            log.L.error("Expecting argument to connect!")
            sys.exit(1)
        uri = args[0]
        p = re.search("(.*)%(.*)", uri)
        if (p):
            log.L.error("Complex URI not supported yet :(")
            sys.exit(1)
        cfg = parseUri(cfg, uri)
        if not cfg:
            sys.exit(1)
            
    if cfg.authId == "_random_":
        cfg.authId = generateAuthId(cfg.serviceId)
    if cfg.uniqueId == "_random_":
        cfg.uniqueId = generateMgmtId()
    
    config.Config.CAP = cfg

    # Initialise services
    services.SERVICES = services.Services()
    log.A.audit(log.A.START, log.A.SERVICE, "lthnvpnc")
    sdps.SDPS = sdps.SDPList()
    sdps.SDPS.get()
    
    if (cfg.O):
        if (loadService(cfg.providerid, cfg.serviceId)):
            services.SERVICES.syslog.run()
            services.SERVICES.show()
            sid = services.SERVICES.get(cfg.serviceId)
            sid.enable()
            sid.cfg["uniqueid"] = cfg.uniqueId
            sid.cfg["paymentid"] = cfg.authId
            sdp = sdps.SDPS.getProviderSDP(cfg.providerid)
            atexit.register(sid.stop)
            sid.run()
            sid.connect(sdp)   
                      
    elif (cfg.L):
        print("SDP,ProviderId/ServiceId,serviceType,ProviderName,ServiceName")
        for pid in sdps.SDPS.list():
            sdp = sdps.SDPS.getProviderSDP(pid)
            for srv in sdp["services"]:
                sid = srv["id"]
                print("%s:%s/%s,%s,%s,%s" % (sdp["provider"]["fqdn"], pid, sid, srv["type"], sdp["provider"]["name"], srv["name"]))
    else:
        log.L.error("You must specify command (list|connect|show)")
        sys.exit(1)
            
if __name__ == "__main__":
    main(sys.argv[1:])
    
