2019-07-10 19:10:14 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
libexec_dir = __LIBEXECDIR__
|
2019-07-10 19:44:20 +00:00
|
|
|
|
2019-07-12 22:02:13 +00:00
|
|
|
import select
|
2019-07-10 19:44:20 +00:00
|
|
|
import subprocess
|
|
|
|
import sys
|
2019-07-12 22:02:13 +00:00
|
|
|
|
2019-07-14 20:25:10 +00:00
|
|
|
own_nick = None
|
|
|
|
own_status = None
|
|
|
|
|
2019-07-10 20:37:39 +00:00
|
|
|
def writeall(f, b):
|
|
|
|
written = 0
|
|
|
|
while written < len(b):
|
|
|
|
written += f.write(b[written:])
|
|
|
|
|
2019-07-13 16:34:54 +00:00
|
|
|
def readall(f, length):
|
|
|
|
read = bytearray()
|
|
|
|
while len(read) < length:
|
|
|
|
data = f.read(length - len(read))
|
2019-07-14 17:05:54 +00:00
|
|
|
if data == b'':
|
2019-07-13 16:34:54 +00:00
|
|
|
raise ConnectionError('Could not satisfy read of %i bytes' % length)
|
|
|
|
read.extend(data)
|
|
|
|
return bytes(read)
|
|
|
|
|
2019-07-14 17:36:12 +00:00
|
|
|
def readall_u16(f):
|
|
|
|
u16_bytes = readall(f, 2)
|
2019-07-14 20:13:39 +00:00
|
|
|
return (u16_bytes[0] << 8) | u16_bytes[1]
|
2019-07-14 17:36:12 +00:00
|
|
|
|
2019-07-10 20:37:39 +00:00
|
|
|
def parse_mac(text):
|
|
|
|
parts = text.split(':')
|
|
|
|
if len(parts) != 6:
|
|
|
|
raise ValueError('Invalid MAC format: %s' % text)
|
|
|
|
|
|
|
|
try:
|
|
|
|
parsed = bytes(int(field, 16) for field in parts)
|
|
|
|
except ValueError:
|
|
|
|
raise ValueError('Invalid MAC format %s' % text)
|
|
|
|
|
|
|
|
return parsed
|
|
|
|
|
2019-07-13 16:34:54 +00:00
|
|
|
def format_mac(mac):
|
|
|
|
return ':'.join(mac[i:i+1].hex() for i in range(len(mac)))
|
|
|
|
|
2019-07-13 17:51:15 +00:00
|
|
|
def format_status(status):
|
|
|
|
if status == 0:
|
|
|
|
return 'available'
|
|
|
|
elif status == 1:
|
|
|
|
return 'unavailable'
|
|
|
|
elif status == 2:
|
|
|
|
return 'offline'
|
|
|
|
else:
|
|
|
|
raise ValueError('Unknown status %i' % status)
|
|
|
|
|
2019-07-14 20:13:39 +00:00
|
|
|
def send_message(backend, mac, message):
|
|
|
|
encoded = message.encode('utf-8')
|
|
|
|
writeall(backend, b'm' + mac + bytes([len(encoded) >> 8, len(encoded) & 0xff]) + encoded)
|
2019-07-10 19:44:20 +00:00
|
|
|
|
2019-07-14 20:13:39 +00:00
|
|
|
def send_status_request(backend, mac):
|
|
|
|
writeall(backend, b'r' + mac)
|
2019-07-12 22:02:13 +00:00
|
|
|
|
2019-07-14 20:25:10 +00:00
|
|
|
def set_status_nick(backend, status, nick):
|
|
|
|
encoded = nick.encode('utf-8')
|
|
|
|
writeall(backend, b's' + bytes([status, len(encoded)]) + encoded)
|
|
|
|
|
2019-07-14 20:13:39 +00:00
|
|
|
def handle_user_command(backend, line):
|
2019-07-14 20:25:10 +00:00
|
|
|
global own_nick, own_status
|
|
|
|
|
2019-07-14 20:13:39 +00:00
|
|
|
if len(line) > 0 and line[0] == '/':
|
|
|
|
command, _, rest = line.partition(' ')
|
|
|
|
if command == '/msg':
|
|
|
|
# Send message to target
|
|
|
|
mac_str, _, message = rest.partition(' ')
|
|
|
|
mac = parse_mac(mac_str)
|
|
|
|
send_message(backend, mac, message)
|
|
|
|
|
|
|
|
elif command == '/status':
|
|
|
|
# Request status
|
|
|
|
mac = parse_mac(rest)
|
|
|
|
send_status_request(backend, mac)
|
|
|
|
|
2019-07-14 20:25:10 +00:00
|
|
|
elif command == '/available' and rest == '':
|
|
|
|
own_status = 0
|
|
|
|
set_status_nick(backend, own_status, own_nick)
|
|
|
|
|
|
|
|
elif command == '/unavailable' and rest == '':
|
|
|
|
own_status = 1
|
|
|
|
set_status_nick(backend, own_status, own_nick)
|
|
|
|
|
|
|
|
elif command == '/nick':
|
|
|
|
own_nick = rest
|
|
|
|
set_status_nick(backend, own_status, own_nick)
|
|
|
|
|
2019-07-14 20:13:39 +00:00
|
|
|
else:
|
|
|
|
# Display usage
|
2019-07-14 20:25:10 +00:00
|
|
|
print('/msg <MAC> <message>; /status <MAC>; /available; /unavailable; /nick')
|
2019-07-12 22:02:13 +00:00
|
|
|
|
2019-07-14 17:05:54 +00:00
|
|
|
else:
|
2019-07-14 20:13:39 +00:00
|
|
|
# Display usage
|
2019-07-14 20:25:10 +00:00
|
|
|
print('/msg <MAC> <message>; /status <MAC>; /available; /unavailable; /nick')
|
2019-07-10 21:00:44 +00:00
|
|
|
|
2019-07-14 17:12:45 +00:00
|
|
|
def eventloop(proc):
|
|
|
|
# Create unbuffered version of stdin
|
|
|
|
unbuf_stdin = open(sys.stdin.buffer.fileno(), 'rb', buffering = 0)
|
|
|
|
|
|
|
|
# Set up a poll for inputs (but do output blockingly)
|
|
|
|
poll = select.poll()
|
|
|
|
poll.register(proc.stdout, select.POLLIN)
|
|
|
|
poll.register(unbuf_stdin, select.POLLIN)
|
|
|
|
|
|
|
|
input_buffer = bytearray()
|
|
|
|
|
|
|
|
running = True
|
|
|
|
while running:
|
|
|
|
for fd, event in poll.poll():
|
|
|
|
if fd == proc.stdout.fileno() and event & select.POLLIN:
|
|
|
|
event_type = readall(proc.stdout, 1)
|
|
|
|
if event_type == b's':
|
|
|
|
# Status
|
|
|
|
source_mac = readall(proc.stdout, 6)
|
|
|
|
status, = readall(proc.stdout, 1)
|
|
|
|
nick_length, = readall(proc.stdout, 1)
|
|
|
|
nick = readall(proc.stdout, nick_length,)
|
|
|
|
|
|
|
|
print('%s (%s) ~%s' % (format_mac(source_mac), format_status(status), nick.decode('utf-8')))
|
2019-07-10 20:37:39 +00:00
|
|
|
|
2019-07-14 17:26:29 +00:00
|
|
|
elif event_type == b'i':
|
|
|
|
# Msgid for message
|
2019-07-14 17:39:45 +00:00
|
|
|
msgid = readall_u16(proc.stdout)
|
2019-07-14 17:36:12 +00:00
|
|
|
print('(msgid: %i)' % msgid) #debg
|
|
|
|
|
2019-07-14 19:49:12 +00:00
|
|
|
elif event_type == b'I':
|
|
|
|
# Failed to get msgid for message
|
|
|
|
print('(msgid fail)') #debg
|
|
|
|
|
|
|
|
elif event_type == b'a':
|
|
|
|
# ACK received
|
|
|
|
source_mac = readall(proc.stdout, 6)
|
|
|
|
msgid = readall_u16(proc.stdout)
|
|
|
|
print('(ack: %s %i)' % (format_mac(source_mac), msgid)) #debg
|
|
|
|
|
|
|
|
elif event_type == b'A':
|
|
|
|
# ACK not received (and message send failed)
|
2019-07-14 20:13:39 +00:00
|
|
|
print('(ack failed)') #debg
|
2019-07-14 19:49:12 +00:00
|
|
|
|
2019-07-14 17:36:12 +00:00
|
|
|
elif event_type == b'm':
|
|
|
|
# Message received
|
|
|
|
source_mac = readall(proc.stdout, 6)
|
|
|
|
message_length = readall_u16(proc.stdout)
|
|
|
|
message = readall(proc.stdout, message_length)
|
|
|
|
|
2019-07-14 19:49:12 +00:00
|
|
|
print('<%s> %s' % (format_mac(source_mac), message.decode('utf-8'))) #debg
|
2019-07-14 17:39:45 +00:00
|
|
|
|
2019-07-14 17:12:45 +00:00
|
|
|
else:
|
|
|
|
# Not sth we handle yet
|
|
|
|
data = proc.stdout.read(1023)
|
|
|
|
if data == b'':
|
|
|
|
data = b'[!] ' + event_type
|
|
|
|
else:
|
|
|
|
data = b'[!] ' + event_type + data
|
|
|
|
sys.stdout.buffer.write(data)
|
|
|
|
sys.stdout.buffer.flush()
|
|
|
|
|
|
|
|
elif fd == proc.stdout.fileno() and event & select.POLLHUP:
|
|
|
|
print('Backend exited')
|
|
|
|
running = False
|
2019-07-12 22:02:13 +00:00
|
|
|
|
2019-07-14 17:12:45 +00:00
|
|
|
elif fd == unbuf_stdin.fileno() and event & select.POLLIN:
|
|
|
|
data = unbuf_stdin.read(1024)
|
|
|
|
input_buffer.extend(data)
|
2019-07-10 20:37:39 +00:00
|
|
|
|
2019-07-14 17:12:45 +00:00
|
|
|
while True:
|
|
|
|
newline_location = input_buffer.find(b'\n')
|
|
|
|
if newline_location == -1:
|
|
|
|
break
|
2019-07-12 22:02:13 +00:00
|
|
|
|
2019-07-14 17:12:45 +00:00
|
|
|
line, _, input_buffer = input_buffer.partition(b'\n')
|
|
|
|
handle_user_command(proc.stdin, line.decode('utf-8'))
|
2019-07-13 17:51:15 +00:00
|
|
|
|
2019-07-14 17:12:45 +00:00
|
|
|
if data == b'':
|
|
|
|
# ^D
|
|
|
|
writeall(proc.stdin, b'q')
|
|
|
|
running = False
|
2019-07-13 17:51:15 +00:00
|
|
|
|
|
|
|
else:
|
2019-07-14 17:12:45 +00:00
|
|
|
raise Exception('Unreachable')
|
|
|
|
def main():
|
2019-07-14 20:25:10 +00:00
|
|
|
global own_nick, own_status
|
|
|
|
|
|
|
|
_, interface, own_nick = sys.argv
|
2019-07-13 16:34:54 +00:00
|
|
|
|
2019-07-14 17:12:45 +00:00
|
|
|
proc = subprocess.Popen(['sudo', libexec_dir + '/ethermess-backend', interface], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = sys.stderr, bufsize = 0)
|
2019-07-10 20:37:39 +00:00
|
|
|
|
2019-07-14 17:12:45 +00:00
|
|
|
# Tell the backend the status and nick
|
2019-07-14 20:25:10 +00:00
|
|
|
own_status = 0
|
|
|
|
encoded = own_nick.encode('utf-8')
|
|
|
|
writeall(proc.stdin, bytes([own_status, len(encoded)]) + encoded)
|
2019-07-12 22:02:13 +00:00
|
|
|
|
2019-07-14 17:12:45 +00:00
|
|
|
# Read our MAC
|
|
|
|
mac = readall(proc.stdout, 6)
|
2019-07-12 22:02:13 +00:00
|
|
|
|
2019-07-14 17:12:45 +00:00
|
|
|
print('Own mac: %s' % format_mac(mac))
|
2019-07-12 22:02:13 +00:00
|
|
|
|
2019-07-14 17:12:45 +00:00
|
|
|
eventloop(proc)
|
2019-07-12 22:02:13 +00:00
|
|
|
|
2019-07-14 17:12:45 +00:00
|
|
|
proc.wait()
|
2019-07-12 22:05:33 +00:00
|
|
|
|
2019-07-14 17:12:45 +00:00
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|