### This is a very quick and dirty code for David so he can work on its sikuli agent
# and report as nsca the results.
# This need to be clean a lot, it's still a server and should be a client class :)
# I can do it after my "new baby holidays" are finished ;)
# J.Gabes



import time
import select
import socket
import struct
import sys
import random


def decrypt_xor(data, key):
    keylen = len(key)
    crypted = [chr(ord(data[i]) ^ ord(key[i % keylen])) for i in xrange(len(data))]
    return ''.join(crypted)



#Just print some stuff
class NSCA_client():
    def __init__(self, host, port, encryption_method, password):
        self.host = host
        self.port = port
        self.encryption_method = encryption_method
        self.password = password
        self.rng = random.Random(password)


    #Ok, main function that is called in the CONFIGURATION phase
    def get_objects(self):
        print "[Dummy] ask me for objects to return"
        r = {'hosts' : []}
        h = {'name' : 'dummy host from dummy arbiter module',
             'register' : '0',
             }

        r['hosts'].append(h)
        print "[Dummy] Returning to Arbiter the hosts:", r
        return r

    def send_init_packet(self, socket):
        '''
        Build an init packet
         00-127  : IV
         128-131 : unix timestamp
        '''
        iv = ''.join([chr(self.rng.randrange(256)) for i in xrange(128)])
        init_packet = struct.pack("!128sI", iv, int(time.mktime(time.gmtime())))
        socket.send(init_packet)
        return iv

    def read_check_result(self, data, iv):
        '''
        Read the check result
         00-01 : Version
         02-05 : CRC32
         06-09 : Timestamp
         10-11 : Return code
         12-75 : hostname
         76-203 : service
         204-715 : output of the plugin
         716-720 : padding
        '''
        if len(data) != 720:
            return None

        if self.encryption_method == 1:
            data = decrypt_xor(data,self.password)
            data = decrypt_xor(data,iv)

        (version, pad1, crc32, timestamp, rc, hostname_dirty, service_dirty, output_dirty, pad2) = struct.unpack("!hhIIh64s128s512sh",data)
        hostname =  hostname_dirty.partition("\0", 1)[0]
        service = service_dirty.partition("\0", 1)[0]
        output = output_dirty.partition("\0", 1)[0]
        return (timestamp, rc, hostname, service, output)

    def post_command(self, timestamp, rc, hostname, service, output):
        '''
        Send a check result command to the arbiter
        '''
        if len(service) == 0:
            extcmd = "[%lu] PROCESS_HOST_CHECK_RESULT;%s;%d;%s\n" % (timestamp,hostname,rc,output)
        else:
            extcmd = "[%lu] PROCESS_SERVICE_CHECK_RESULT;%s;%s;%d;%s\n" % (timestamp,hostname,service,rc,output)

        print "want to send", extcmd
        
        #e = ExternalCommand(extcmd)
        #self.from_q.put(e)


    # When you are in "external" mode, that is the main loop of your process
    def main(self):
        #self.set_exit_handler()
        self.interrupted = False
        backlog = 5
        size = 8192
        server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        #server.setblocking(0)
        server.connect((self.host, self.port))
        #server.listen(backlog)
        input = [server]
        databuffer = {}
        IVs = {}

        init = server.recv(size)
        print "got init", init
        
        #init_packet = struct.pack("!128sI",iv,int(time.mktime(time.gmtime())))
        (iv, t) = struct.unpack("!128sI",init)
        print "IV", iv
        print "T", t

        version = 0
        pad1 = 0
        crc32= 0
        timestamp = int(time.time())
        rc = 2
        hostname_dirty = "moncul"
        service_dirty = "fonctionnne"
        output_dirty = "blablalba"
        pad2=0
        '''
        Read the check result
         00-01 : Version
         02-05 : CRC32
         06-09 : Timestamp
         10-11 : Return code
         12-75 : hostname
         76-203 : service
         204-715 : output of the plugin
         716-720 : padding
        '''
        init_packet = struct.pack("!hhIIh64s128s512sh", version, pad1, crc32, timestamp, rc, hostname_dirty, service_dirty, output_dirty, pad2)
        print "Create packent len", len(init_packet)
        #(version, pad1, crc32, timestamp, rc, hostname_dirty, service_dirty, output_dirty, pad2) = struct.unpack("!hhIIh64s128s512sh",data)

        data = decrypt_xor(init_packet,iv)        
        data = decrypt_xor(data,self.password)


        server.send(data)
        sys.exit(0)

        while not self.interrupted:
            print "Loop"
            inputready,outputready,exceptready = select.select(input,[],[], 1)

            for s in inputready:
                if s == server:
                    # handle the server socket
                    #client, address = server.accept()
                    iv = self.send_init_packet(client)
                    IVs[client] = iv
                    input.append(client)
                else:
                    # handle all other sockets
                    data = s.recv(size)
                    if s in databuffer:
                        databuffer[s] += data
                    else:
                        databuffer[s] = data
                    if len(databuffer[s]) == 720:
                        # end-of-transmission or an empty line was received
                        (timestamp, rc, hostname, service, output)=self.read_check_result(databuffer[s],IVs[s])
                        del databuffer[s]
                        del IVs[s]
                        self.post_command(timestamp,rc,hostname,service,output)
                        try:
                            s.shutdown(2)
                        except Exception , exp:
                            print exp
                        s.close()
                        input.remove(s)



nsca = NSCA_client('localhost', 5667, 1, 'toto')
nsca.main()