ethermess/ethermess.py

138 lines
3.4 KiB
Python
Raw Normal View History

2019-07-10 19:10:14 +00:00
#!/usr/bin/env python3
libexec_dir = __LIBEXECDIR__
2019-07-10 19:44:20 +00:00
import select
2019-07-10 19:44:20 +00:00
import subprocess
import sys
2019-07-10 20:37:39 +00:00
def writeall(f, b):
written = 0
while written < len(b):
written += f.write(b[written:])
def readall(f, length):
read = bytearray()
while len(read) < length:
data = f.read(length - len(read))
if data == b'':
raise ConnectionError('Could not satisfy read of %i bytes' % length)
read.extend(data)
return bytes(read)
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
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)
def handle_user_command(backend, command):
if command == 's':
mac = parse_mac(input('mac> '))
writeall(backend, b's' + mac)
2019-07-10 19:44:20 +00:00
elif command == 'i':
mac = parse_mac(input('mac> '))
writeall(backend, b'i' + mac)
elif command == 'm':
mac = parse_mac(input('mac> '))
message = input('message> ').encode('utf-8')
writeall(backend, b'm' + mac + bytes([len(message) >> 8, len(message) & 0xff]) + message)
else:
print('s - request status, i - request msgid, m - send message, ^D - quit')
2019-07-10 21:00:44 +00:00
# Create unbuffered version of stdin
unbuf_stdin = open(sys.stdin.buffer.fileno(), 'rb', buffering = 0)
_, interface, nick = sys.argv
2019-07-10 20:37:39 +00:00
proc = subprocess.Popen(['sudo', libexec_dir + '/ethermess-backend', interface], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = sys.stderr, bufsize = 0)
# Tell the backend the status and nick
status = 0
nick = nick.encode('utf-8')
writeall(proc.stdin, bytes([status, len(nick)]) + nick)
# Read our MAC
mac = readall(proc.stdout, 6)
print('Own mac: %s' % format_mac(mac))
2019-07-10 20:37:39 +00:00
print('s - request status, i - request msgid, m - send message, ^D - quit')
poll = select.poll()
poll.register(proc.stdout, select.POLLIN)
poll.register(unbuf_stdin, select.POLLIN)
2019-07-10 20:37:39 +00:00
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)
2019-07-13 17:51:15 +00:00
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,)
2019-07-13 17:51:15 +00:00
print('%s (%s) ~%s' % (format_mac(source_mac), format_status(status), nick.decode('utf-8')))
else:
# Not sth we handle yet
data = proc.stdout.read(1023)
if data == b'':
2019-07-13 17:51:15 +00:00
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-10 20:37:39 +00:00
elif fd == unbuf_stdin.fileno() and event & select.POLLIN:
data = unbuf_stdin.read(1024)
input_buffer.extend(data)
while True:
newline_location = input_buffer.find(b'\n')
if newline_location == -1:
break
line, _, input_buffer = input_buffer.partition(b'\n')
handle_user_command(proc.stdin, line.decode('utf-8'))
if data == b'':
# ^D
writeall(proc.stdin, b'q')
running = False
else:
raise Exception('Unreachable')
2019-07-12 22:05:33 +00:00
proc.wait()