#!/usr/bin/python

# rawz - raw zephyr using only batteries (sockets, mostly)

import socket
import struct
import re
import time

def zbytes(bytes):
    return ("0x" + "%02X" * len(bytes)) % struct.unpack("B" * len(bytes), bytes)

def zascii(data):
    return " ".join([zbytes(s) for s in re.findall("..?.?.?", data)])
    
def zlong(val):
    return zascii(struct.pack(">L", val))

def zshort(val):
    return zascii(struct.pack(">H", val))

def getservbyname(serv, proto, fallback):
    try:
        return socket.getservbyname(serv, proto)
    except socket.error:
        return fallback

class zsetup:
    def __init__(self):
        # we can't actually bounce through a remote zhm anymore
        # but if we don't have a local one, we can just *be* one
        self.localaddr = socket.gethostbyname(socket.gethostname())
        if os.getenv("ZSERVER"):
            self.zsendto_addr = os.getenv("ZSERVER")
            self.zsendto_addr = socket.gethostbyname(self.zsendto_addr)
            self.zsendto_port = getservbyname("zephyr-clt", "udp", 2103)
            self.bindto = (self.localaddr, getservbyname("zephyr-hm", "udp", 2104))
        else:
            # ok, use the local zhm
            self.zsendto_addr = "127.0.0.1"
            self.zsendto_port = getservbyname("zephyr-hm", "udp", 2104)
            self.bindto = ("127.0.0.1", 0)

        # uid setup stuff
        self.uidinc = 0
        self.oldtime = None

    def zpacket(self, uid, zclass, inst, opcode, sender, recip, default, msg):
        if not uid:
            newtime = long(time.time())
            if self.oldtime != newtime:
                self.uidinc = 0
            self.uidinc += 1
            uid = struct.pack(">4s L L", socket.inet_aton(self.localaddr), newtime, self.uidinc)
            self.oldtime = newtime
                         
        zfields = ["ZEPH0.2",
                   zlong(17),
                   zlong(0),			# UNSAFE
                   zascii(uid),
                   zshort(0),	# no response port
                   zlong(0),			# no auth
                   zlong(0),			# authlen = 0
                   "",			# no authdata
                   zclass,
                   inst,
                   opcode,
                   sender,
                   recip,
                   default,
                   zlong(0),			# no checksum
                   "",			# no fragmentation
                   zascii(uid),
                   msg]
        return "\0".join(zfields)

    def zinit(self, retsock=[]):
        if retsock:
            return retsock[0]

        zsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        zsock.bind(self.bindto)
        retsock.append(zsock)
        return zsock

    def zsend(self, packet):
        print "sending to", (self.zsendto_addr, self.zsendto_port)
        return self.zinit().sendto(packet, (self.zsendto_addr, self.zsendto_port))

def addrealm(s):
    return s + "@ATHENA.MIT.EDU"

import sys,os
if __name__ == "__main__":
    prog, who, what = sys.argv
    z = zsetup()
    zpack = z.zpacket(None, "MESSAGE", "PERSONAL", "", addrealm(who) , addrealm(who) , "strings!", what)
    print zpack.split("\0")
    z.zsend(zpack)