#!/usr/bin/python
try:
    import http.client as client
except ImportError:
    import httplib as client
import base64
import ctypes
import ctypes.util
import os
import select
import socket
import subprocess
import ssl
import sys
import time
import re
import hashlib
try:
    import json
    import hmac
except ImportError:
    json = None
    hmac = None

# Ignore proxy-based environment variables
for proxy_var in ['http_proxy', 'https_proxy', 'HTTP_PROXY', 'HTTPS_PROXY', 'ALL_PROXY', 'all_proxy', 'NO_PROXY', 'no_proxy']:
    os.environ.pop(proxy_var, None)

class InvalidApiKey(Exception):
    pass

def msg_align(len):
    return (len + 3) & ~3

try:
    _protoparm = ssl.PROTOCOL_TLS_CLIENT
except AttributeError:    
    _protoparm = ssl.PROTOCOL_SSLv23
cryptname = ctypes.util.find_library('crypt')
if not cryptname:
    if os.path.exists('/lib64/libcrypt.so.2') or os.path.exists('/usr/lib64/libcrypt.so.2'):
        cryptname = 'libcrypt.so.2'
    else:
        cryptname = 'libcrypt.so.1'
c_libcrypt = ctypes.CDLL(cryptname)
c_crypt = c_libcrypt.crypt
c_crypt.argtypes = (ctypes.c_char_p, ctypes.c_char_p)
c_crypt.restype = ctypes.c_char_p


def get_my_addresses():
    addrs = []
    for ifa in get_ifaddrs():
        if ifa[0] == 'ip':
            addrs.append((ifa[1], ifa[2], ifa[3]))
    return addrs

def get_mac_addresses():
    macs = []
    for ifa in get_ifaddrs():
        if ifa[0] == 'ETHER':
            macs.append((ifa[1], ifa[2]))
    return macs

def get_ifaddrs():
    class sockaddr(ctypes.Structure):
        _fields_ = [
            ('sa_family', ctypes.c_uint16),
            ('sa_data', ctypes.c_ubyte * 14),
        ]
    
    class sockaddr_in(ctypes.Structure):
        _fields_ = [
            ('sin_family', ctypes.c_uint16),
            ('sin_port', ctypes.c_uint16),
            ('sin_addr', ctypes.c_ubyte * 4),
            ('sin_zero', ctypes.c_ubyte * 8),
        ]
    
    class sockaddr_in6(ctypes.Structure):
        _fields_ = [
            ('sin6_family', ctypes.c_uint16),
            ('sin6_port', ctypes.c_uint16),
            ('sin6_flowinfo', ctypes.c_uint32),
            ('sin6_addr', ctypes.c_ubyte * 16),
            ('sin6_scope_id', ctypes.c_uint32),
        ]
    
    class sockaddr_ll(ctypes.Structure):
        _fields_ = [
            ('sll_family', ctypes.c_uint16),
            ('sll_protocol', ctypes.c_uint16),
            ('sll_ifindex', ctypes.c_int32),
            ('sll_hatype', ctypes.c_uint16),
            ('sll_pkttype', ctypes.c_uint8),
            ('sll_halen', ctypes.c_uint8),
            ('sll_addr', ctypes.c_ubyte * 8),
        ]
    
    class ifaddrs(ctypes.Structure):
        pass

    ifaddrs._fields_ = [
            ('ifa_next', ctypes.POINTER(ifaddrs)),
            ('ifa_name', ctypes.c_char_p),
            ('ifa_flags', ctypes.c_uint),
            ('ifa_addr', ctypes.POINTER(sockaddr)),
            ('ifa_netmask', ctypes.POINTER(sockaddr)),
            ('ifa_ifu', ctypes.POINTER(sockaddr)),
            ('ifa_data', ctypes.c_void_p),
        ]
    
    libc = ctypes.CDLL(ctypes.util.find_library('c'))
    libc.getifaddrs.argtypes = [ctypes.POINTER(ctypes.POINTER(ifaddrs))]
    libc.getifaddrs.restype = ctypes.c_int
    libc.freeifaddrs.argtypes = [ctypes.POINTER(ifaddrs)]
    libc.freeifaddrs.restype = None
    ifap = ctypes.POINTER(ifaddrs)()
    result = libc.getifaddrs(ctypes.pointer(ifap))
    if result != 0:
        return []
    addresses = []
    ifa = ifap
    try:
        while ifa:
            if ifa.contents.ifa_addr:
                family = ifa.contents.ifa_addr.contents.sa_family
                name = ifa.contents.ifa_name.decode('utf-8') if ifa.contents.ifa_name else None
                if family in (socket.AF_INET, socket.AF_INET6):
                    # skip loopback and non-multicast interfaces
                    if ifa.contents.ifa_flags & 8 or not ifa.contents.ifa_flags & 0x1000:
                        ifa = ifa.contents.ifa_next
                        continue
                if family == socket.AF_INET:
                    addr_ptr = ctypes.cast(ifa.contents.ifa_addr, ctypes.POINTER(sockaddr_in))
                    addr_bytes = bytes(addr_ptr.contents.sin_addr)
                    if_index = socket.if_nametoindex(name) if name else 0
                    addresses.append(('ip', family, addr_bytes, if_index))
                elif family == socket.AF_INET6:
                    addr_ptr = ctypes.cast(ifa.contents.ifa_addr, ctypes.POINTER(sockaddr_in6))
                    addr_bytes = bytes(addr_ptr.contents.sin6_addr)
                    scope_id = addr_ptr.contents.sin6_scope_id
                    addresses.append(('ip', family, addr_bytes, scope_id))          
                elif family == socket.AF_PACKET:
                    addr_ptr = ctypes.cast(ifa.contents.ifa_addr, ctypes.POINTER(sockaddr_ll))
                    halen = addr_ptr.contents.sll_halen
                    if addr_ptr.contents.sll_hatype in (1, 32) and halen > 0:  # ARPHRD_ETHER or ARPHRD_INFINIBAND
                        if addr_ptr.contents.sll_hatype == 1 and addr_ptr.contents.sll_addr[0] & 2:  # skip locally administered MACs
                            ifa = ifa.contents.ifa_next
                            continue
                        mac_bytes = bytes(addr_ptr.contents.sll_addr[:halen])
                        macaddr = ':'.join('{:02x}'.format(b) for b in mac_bytes)
                        addresses.append(('ETHER', name, macaddr))                
            ifa = ifa.contents.ifa_next
    finally:
        libc.freeifaddrs(ifap)
    
    return addresses

def scan_confluents(confuuid=None):
    srvs = {}
    s6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
    s6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1)
    try:
        s6.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
    except Exception:
        # REUSPORT is ideal, but if python is missing it, just live with the limitation
        pass
    try:  # We would like to bind for firewall rules, however if not possible then leave it alone
        s6.bind(('::', 1900))
    except Exception:
        pass
    s4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s4.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
    try:
        s4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
    except Exception:
        # REUSPORT is ideal, but if python is missing it, just live with the limitation
        pass
    try:
        s4.bind(('0.0.0.0', 1900))
    except Exception:
        pass
    doneidxs = set([])
    msg = 'M-SEARCH * HTTP/1.1\r\nST: urn:xcat.org:service:confluent:'
    if not confuuid and os.path.exists('/etc/confluent/confluent.deploycfg'):
        with open('/etc/confluent/confluent.deploycfg') as dcfg:
            for line in dcfg.read().split('\n'):
                if line.startswith('confluent_uuid:'):
                    confluentuuid = line.split(': ')[1]
                    msg += '/confluentuuid=' + confluentuuid
                    break
    if not confuuid and os.path.exists('/confluent_uuid'):
        with open('/confluent_uuid') as cuuidin:
            confluentuuid = cuuidin.read().strip()
            msg += '/confluentuuid=' + confluentuuid
    try:
        with open('/sys/devices/virtual/dmi/id/product_uuid') as uuidin:
            msg += '/uuid=' + uuidin.read().strip()
    except Exception:
        pass
    for iface, hwaddr in get_mac_addresses():
        msg += '/mac=' + hwaddr
    msg = msg.encode('utf8')
    for addr in get_my_addresses():
        if addr[0] == socket.AF_INET6:
            if addr[-1] in doneidxs:
                continue
            try:
                s6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, addr[-1])
            except TypeError:
                s6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, addr[-1].tobytes())
            try:
                s6.sendto(msg, ('ff02::c', 1900))
            except OSError:
                pass
            doneidxs.add(addr[-1])
        elif addr[0] == socket.AF_INET:
            try:
                s4.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, addr[1])
            except TypeError:
                s4.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, addr[1].tobytes())
            try:
                s4.sendto(msg, ('239.255.255.250', 1900))
            except OSError:
                pass
    r = select.select((s4, s6), (), (), 4)
    srvlist = []
    if r:
        r = r[0]
    nodename = None
    while r:
        for s in r:
            (rsp, peer) = s.recvfrom(9000)
            rsp = rsp.split(b'\r\n')
            current = None
            for line in rsp:
                if line.startswith(b'NODENAME: '):
                    nodename = line.replace(b'NODENAME: ', b'').strip().decode('utf8')
                    current = {}
                elif line.startswith(b'DEFAULTNET: 1'):
                    current['isdefault'] = True
                elif line.startswith(b'MGTIFACE: '):
                    current['mgtiface'] = line.replace(b'MGTIFACE: ', b'').strip().decode('utf8')
            if len(peer) > 2:
                current['myidx'] = peer[-1]
            currip = peer[0]
            if currip.startswith('fe80::') and '%' not in currip:
                currip = '{0}%{1}'.format(currip, peer[-1])
            srvs[currip] = current
            if currip not in srvlist:
                srvlist.append(currip)
        r = select.select((s4, s6), (), (), 2)
        if r:
            r = r[0]
    if not os.path.exists('/etc/confluent/confluent.info'):
        with open('/etc/confluent/confluent.info', 'w+') as cinfo:
            if nodename:
                cinfo.write('NODENAME: {0}\n'.format(nodename))
            for srv in srvlist:
                cinfo.write('MANAGER: {0}\n'.format(srv))
    return srvlist, srvs


def get_net_apikey(nodename, mgr, hmackey=None, confuuid=None):
    alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./'
    newpass = ''.join([alpha[x >> 2] for x in bytearray(os.urandom(32))])
    salt = '$5$' + ''.join([alpha[x >> 2] for x in bytearray(os.urandom(8))])
    newpass = newpass.encode('utf8')
    salt = salt.encode('utf8')
    crypted = c_crypt(newpass, salt)
    if hmackey:
        hmacvalue = hmac.new(hmackey.encode('utf8'), crypted, hashlib.sha256).digest()
        hmacvalue = base64.b64encode(hmacvalue).decode('utf8')
        client = HTTPSClient(host=mgr, phmac=hmacvalue, nodename=nodename, confuuid=confuuid)
        try:
            status, rsp = client.grab_url_with_status('/confluent-api/self/registerapikey', data=crypted, returnrsp=True)
            if status == 200:
                return newpass.decode('utf8')
        except Exception:
            pass
    for addrinfo in socket.getaddrinfo(mgr, 13001, 0, socket.SOCK_STREAM):
        try:
            clisock = socket.socket(addrinfo[0], addrinfo[1])
            clisock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            if addrinfo[0] == socket.AF_INET:
                cliaddr = ('0.0.0.0', 302)
            else:
                cliaddr = ('::', 302)
            clisock.bind(cliaddr)
            clisock.settimeout(1)
            clisock.connect(addrinfo[-1])
            rsp = clisock.recv(8)
            if rsp != b'\xc2\xd1-\xa8\x80\xd8j\xba':
                raise Exception('Unrecognized credential banner')
            hellostr = bytearray([1, len(nodename)]) + bytearray(nodename.encode('utf8')) + bytearray(b'\x00\x00')
            clisock.send(hellostr)
            rsp = bytearray(clisock.recv(2))
            if not rsp:
                continue
            if rsp[0] == 128:
                continue
            if rsp[0] == 2:
                echotoken = clisock.recv(rsp[1])
                clisock.recv(2)  # drain \x00\x00
                clisock.send(bytes(bytearray([3, rsp[1]])))
                clisock.send(echotoken)
                clisock.send(bytes(bytearray([4, len(crypted)])))
                clisock.send(crypted)
                clisock.send(b'\x00\x00')
            rsp = bytearray(clisock.recv(2))
            if rsp[0] == 5:
                return newpass.decode('utf8')
        finally:
            clisock.close()
    return ''


def get_apikey(nodename, hosts, errout=None, hmackey=None, confuuid=None):
    apikey = ""
    if os.path.exists('/etc/confluent/confluent.apikey'):
        apikey = open('/etc/confluent/confluent.apikey').read().strip()
        if apikey:
            return apikey
    while not apikey:
        for host in hosts:
            try:
                apikey = get_net_apikey(nodename, host, hmackey=hmackey, confuuid=confuuid)
            except OSError:
                apikey = None
            if apikey:
                break
        else:
            srvlist, _ = scan_confluents(confuuid=confuuid)
            for host in srvlist:
                try:
                    apikey = get_net_apikey(nodename, host, hmackey=hmackey, confuuid=confuuid)
                except OSError:
                    apikey = None
                if apikey:
                    break
        if not apikey:
            errmsg = "Failed getting API token, check deployment.apiarmed attribute on {}\n".format(nodename)
            sys.stderr.write(errmsg)
            if errout:
                errout.write(errmsg)
            time.sleep(10)
    with open('/etc/confluent/confluent.apikey', 'w+') as apiout:
        apiout.write(apikey)
    apikey = apikey.strip()
    os.chmod('/etc/confluent/confluent.apikey', 0o600)
    return apikey

class HTTPSClient(client.HTTPConnection, object):
    def __init__(self, usejson=False, port=443, host=None, errout=None, phmac=None, checkonly=False, hmackey=None, nodename=None, confuuid=None):
        self.ignorehosts = set([])
        self.phmac = phmac
        self.hmackey = hmackey
        self.confuuid = confuuid
        self.errout = None
        self.stdheaders = {}
        if nodename:
            self.stdheaders['CONFLUENT_NODENAME'] = nodename
        if errout:
            self.errout = open(errout, 'w')
            self.errout.flush()
        mgtiface = None
        if usejson:
            self.stdheaders['ACCEPT'] = 'application/json'
        if host:
            self.hosts = [host]
            if not nodename:
                with open('/etc/confluent/confluent.info') as cinfo:
                    info = cinfo.read().split('\n')
                    for line in info:
                        if line.startswith('NODENAME:'):
                            nodename = line.split(' ')[1]
            self.stdheaders['CONFLUENT_NODENAME'] = nodename
        else:
            self.hosts = []
            try:
                info = open('/etc/confluent/confluent.info').read().split('\n')
            except Exception:
                info = []
            havedefault = '0'
            plainhost = ''
            for line in info:
                host = ''
                if line.startswith('NODENAME:'):
                    nodename = line.split(' ')[1]
                    self.stdheaders['CONFLUENT_NODENAME'] = nodename
                if line.startswith('MANAGER:') and not host:
                    host = line.split(' ')[1]
                    self.hosts.append(host)
                    if not plainhost:
                        plainhost = host
                if line.startswith('EXTMGRINFO:'):
                    extinfo = line.split(' ')[1]
                    extinfo = extinfo.split('|')
                    if havedefault == '0' and extinfo[2] == '1':
                        host, mgtiface, havedefault = extinfo[:3]
                        if '%' in host:
                            ifidx = host.split('%', 1)[1]
                            with open('/tmp/confluent.ifidx', 'w+') as ifout:
                                ifout.write(ifidx)    
                    if not mgtiface:
                        host, mgtiface, havedefault = extinfo[:3]
                    if host:
                        if havedefault == '0':
                            if '%' in host:
                                ifidx = host.split('%', 1)[1]
                                with open('/tmp/confluent.ifidx', 'w+') as ifout:
                                    ifout.write(ifidx)
                        self.hosts.append(host)
            try:
                info = open('/etc/confluent/confluent.deploycfg').read().split('\n')
            except Exception:
                info = None
            if info:
                for line in info:
                    if line.startswith('deploy_server: ') or line.startswith('deploy_server_v6: '):
                        self.hosts.append(line.split(': ', 1)[1])
            if plainhost and not self.hosts:
                self.hosts.append(plainhost)
        if self.phmac:
            self.stdheaders['CONFLUENT_CRYPTHMAC'] = self.phmac
        elif not checkonly:
            self.stdheaders['CONFLUENT_APIKEY'] = get_apikey(nodename, self.hosts, errout=self.errout, hmackey=hmackey, confuuid=self.confuuid)
        if mgtiface:
            self.stdheaders['CONFLUENT_MGTIFACE'] = mgtiface
        self.port = port
        self.host = None
        self.node = nodename
        host = self.check_connections()
        client.HTTPConnection.__init__(self, host, port)
        self.connect()


    def set_header(self, key, val):
        self.stdheaders[key] = val

    def check_connections(self):
        foundsrv = None
        hosts = self.hosts
        ctx = ssl.SSLContext(_protoparm)
        ctx.load_verify_locations('/etc/confluent/ca.pem')
        ctx.verify_mode = ssl.CERT_REQUIRED
        ctx.check_hostname = True
        for timeo in (0.1, 5):
            for host in hosts:
                if host in self.ignorehosts:
                    continue
                try:
                    addrinf = socket.getaddrinfo(host, self.port)[0]
                    psock = socket.socket(addrinf[0])
                    psock.settimeout(timeo)
                    psock.connect(addrinf[4])
                    chost = host.split('%', 1)[0]
                    ctx.wrap_socket(psock, server_hostname=chost)
                    foundsrv = host
                    psock.close()
                    break
                except (OSError, socket.error):
                    continue
                except ssl.SSLError:
                    continue
                except ssl.CertificateError:
                    continue
            else:
                continue
            break
        if not foundsrv:
            srvlist, srvs = scan_confluents(self.confuuid)
            hosts = []
            for srv in srvlist:
                if srvs[srv].get('isdefault', False):
                    hosts = [srv] + hosts
                else:
                    hosts = hosts + [srv]
            for host in hosts:
                try:
                    addrinf = socket.getaddrinfo(host, self.port)[0]
                    psock = socket.socket(addrinf[0])
                    psock.settimeout(timeo)
                    psock.connect(addrinf[4])
                    foundsrv = host
                    psock.close()
                    break
                except OSError:
                    continue
            else:
                raise Exception('Unable to reach any hosts')
        return foundsrv

    def connect(self, tries=3):
        addrinf = socket.getaddrinfo(self.host, self.port)[0]
        psock = socket.socket(addrinf[0])
        psock.settimeout(15)
        psock.connect(addrinf[4])
        ctx = ssl.SSLContext(_protoparm)
        ctx.load_verify_locations('/etc/confluent/ca.pem')
        host = self.host.split('%', 1)[0]
        if '[' not in host and ':' in host:
            self.stdheaders['Host'] = '[{0}]'.format(host)
        else:
            self.stdheaders['Host'] = '{0}'.format(host)
        ctx.verify_mode = ssl.CERT_REQUIRED
        ctx.check_hostname = True
        try:
            self.sock = ctx.wrap_socket(psock, server_hostname=host)
        except ssl.SSLError:
            errmsg = 'Error validating certificate on deployer (try `osdeploy initialize -t` on the deployment server {0})\n'.format(host)
            sys.stderr.write(errmsg)
            if self.errout:
                self.errout.write(errmsg)
                self.errout.flush()
            sys.exit(1)
        except socket.timeout:
            if not tries:
                raise
            return self.connect(tries=tries-1)

    def grab_url(self, url, data=None, returnrsp=False):
        return self.grab_url_with_status(url, data, returnrsp)[1]

    def grab_url_with_status(self, url, data=None, returnrsp=False):
        if data:
            method = 'POST'
        else:
            method = 'GET'
        authed = False
        while not authed:
            authed = True
            self.request(method, url, data, headers=self.stdheaders)
            rsp = self.getresponse()
            if rsp.status >= 200 and rsp.status < 300:
                if returnrsp:
                    return rsp.status, rsp
                else:
                    return rsp.status, rsp.read()
            if rsp.status == 401:
                authed = False
                rsp.read()
                with open('/etc/confluent/confluent.apikey', 'w+') as akfile:
                    akfile.write('')
                self.stdheaders['CONFLUENT_APIKEY'] = get_apikey(
                    self.node, [self.host], errout=self.errout, hmackey=self.hmackey, confuuid=self.confuuid)
            if rsp.status == 503:  # confluent is down, but the server running confluent is otherwise up
                authed = False
                self.ignorehosts.add(self.host)
                host = self.check_connections()
                client.HTTPConnection.__init__(self, host, self.port)

        raise Exception(rsp.read())

def get_current_vmnic_vswitch():
    uplinkmatch = re.compile(r'^\s*Uplinks:\s*(.*)')
    switchmatch = re.compile(r'^\s*Name:\s*(.*)')

    vswinfo = subprocess.check_output(['localcli', 'network', 'vswitch', 'standard', 'list']).decode()

    vmnic = None
    vswitch_name = None

    for info in vswinfo.split('\n'):
        name_match = switchmatch.match(info)
        if name_match:
            vswitch_name = name_match.group(1).strip()

        upinfo = uplinkmatch.match(info)
        if upinfo:
            vmnic = upinfo.group(1).strip()
            if vmnic and 'vusb0' not in vmnic:
                return vswitch_name, vmnic

    return vswitch_name, vmnic

def get_available_nics():
    nicinfo = subprocess.check_output(['localcli', 'network', 'nic', 'list']).decode('utf8').split('\n')
    available_nics = {}

    # Skip headers and separators
    parsing_started = False
    for line in nicinfo:
        if re.match(r'^-+', line):
            parsing_started = True
            continue
        if not parsing_started or not line.strip():
            continue

        parts = re.split(r'\s{2,}', line.strip())
        if len(parts) >= 5:
            nic_name = parts[0]
            nic_status = parts[4]  # "Link Status" is the 5th field
            available_nics[nic_name] = nic_status

    return available_nics

def is_esxi():
    return os.path.isdir("/etc/vmware")

def fix_vswitch():
    if is_esxi():
        start_time = time.time()
        while True:

            current_vswitch, current_vmnic = get_current_vmnic_vswitch()
            if current_vswitch is None:
                raise RuntimeError("Panic: current vswitch is None")
            if current_vmnic is None:
                raise RuntimeError("Panic: current vmnic is None")

            available_nics = get_available_nics()

            if current_vmnic and available_nics.get(current_vmnic) == 'Up':
                break

            new_vmnic = next((nic for nic, status in available_nics.items() if status == 'Up'), None)

            if new_vmnic and new_vmnic != current_vmnic:
                subprocess.check_call(['localcli', 'network', 'vswitch', 'standard', 'uplink', 'remove',
                                       '--uplink-name', current_vmnic, '--vswitch-name', current_vswitch])
                subprocess.check_call(['localcli', 'network', 'vswitch', 'standard', 'uplink', 'add',
                                       '--uplink-name', new_vmnic, '--vswitch-name', current_vswitch])
            elif not new_vmnic:
                if time.time() - start_time > 300:
                    break
            time.sleep(5)

        time.sleep(5)

if __name__ == '__main__':
    data = None

    if '-f' in sys.argv:
        try:
            fix_vswitch()
        except Exception as e:
            print("fix_vswitch() error: {}".format(e))
        sys.argv.remove('-f')
        sys.exit(0)
    usejson = False
    if '-j' in sys.argv:
        usejson = True
    if len(sys.argv) == 1:
        HTTPSClient()
        sys.exit(0)
    try:
        outbin = sys.argv.index('-o')
        sys.argv.pop(outbin)
        outbin = sys.argv.pop(outbin)
    except ValueError:
        outbin = None
    try:
        waitfor = sys.argv.index('-w')
        sys.argv.pop(waitfor)
        waitfor = int(sys.argv.pop(waitfor))
    except ValueError:
        waitfor = None
    try:
        data = sys.argv.index('-d')
        sys.argv.pop(data)
        data = sys.argv.pop(data)
    except ValueError:
        data = None
    try:
        errout = sys.argv.index('-e')
        sys.argv.pop(errout)
        errout = sys.argv.pop(errout)
    except ValueError:
        errout = None
    try:
        phmac = sys.argv.index('-p')
        sys.argv.pop(phmac)
        phmac = sys.argv.pop(phmac)
        with open(phmac, 'r') as hmacin:
            phmac = hmacin.read()
    except ValueError:
        phmac = None
    try:
        identfile = sys.argv.index('-i')
        sys.argv.pop(identfile)
        identfile = sys.argv.pop(identfile)
        with open(identfile) as idin:
            data = idin.read()
            identinfo = json.loads(data)
            nodename = identinfo.get('nodename', None)
            hmackey = identinfo.get('apitoken', None)
            confuuid = identinfo.get('confluent_uuid', None)
    except ValueError:
        hmackey = None
        nodename = None
        confuuid = None
    try:
        checkonly = False
        idxit = sys.argv.index('-c')
        sys.argv.pop(idxit)
        checkonly = True
    except Exception:
        pass
    if len(sys.argv) > 2 and os.path.exists(sys.argv[-1]):
        data = open(sys.argv[-1]).read()
    if outbin:
        with open(outbin, 'ab+') as outf:
            reader = HTTPSClient(usejson=usejson, errout=errout, hmackey=hmackey, nodename=nodename, confuuid=confuuid).grab_url(
                sys.argv[1], data, returnrsp=True)
            chunk = reader.read(16384)
            while chunk:
                outf.write(chunk)
                chunk = reader.read(16384)
        sys.exit(0)

    mclient = HTTPSClient(usejson, errout=errout, phmac=phmac, checkonly=checkonly, hmackey=hmackey, nodename=nodename, confuuid=confuuid)
    if waitfor:
        status = 201
        while status != waitfor:
            status, rsp = mclient.grab_url_with_status(sys.argv[1], data)
            sys.stdout.write(rsp.decode())
    elif checkonly:
        sys.stdout.write(mclient.check_connections())
    else:
        try:
            sys.stdout.buffer.write(mclient.grab_url(sys.argv[1], data))
        except AttributeError:
            sys.stdout.write(mclient.grab_url(sys.argv[1], data))
