#!@python@/bin/python from os import environ from os.path import expanduser import socket import ssl import shlex from sys import argv, stdin, stdout, exit from multiprocessing import Process from select import select from io import BytesIO import subprocess from time import sleep import logging logger = logging.getLogger('notmuch-tcp-server') logger.setLevel(logging.DEBUG) lh = logging.StreamHandler() lh.setLevel(logging.DEBUG) lh.setFormatter(logging.Formatter( fmt = '{name} {module} {lineno} {levelname}: {message}', style = '{' )) logger.addHandler(lh) port = environ.get('NOTMUCH_TCP') host = environ.get('NOTMUCH_HOST') hostname = socket.gethostname() if port is None: port = 2010 if host is None: host = "odin.asgard.yggdrasil" sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) sslcontext.load_verify_locations(cafile = expanduser('~/.notmuch-tcp/ca.pem')) sslcontext.load_cert_chain(certfile = expanduser(f"~/.notmuch-tcp/{hostname}.pem"), keyfile = expanduser(f"~/.notmuch-tcp/{hostname}.key")) s = None for res in socket.getaddrinfo(host, int(port), socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE): af, socktype, proto, canonname, sa = res try: s = socket.socket(af, socktype, proto) except OSError as msg: logger.error(f"Could not create socket: {msg}") s = None continue try: s.bind(sa) s.listen() except OSError as msg: logger.error(f"Could not bind socket: {msg}") s.close() s = None continue break if s is None: logger.error('Could not open any sockets') exit(1) with s as sock: with sslcontext.wrap_socket(sock, server_side = True) as ssock: while True: logger.debug('waiting...') conn = None try: conn, client_addr = ssock.accept() client_host, client_port = client_addr logger.info(f"Connected: {client_host}:{client_port}") with BytesIO() as buffer: while True: logger.debug("Reading arguments...") resp = conn.recv(256) buffer.write(resp) buffer.seek(0) start_index = 0 for line in buffer: if line[-1:] != b"\n": break start_index += len(line) escaped_args = line[:-1].decode() break if start_index: buffer.seek(start_index) remaining = buffer.read() buffer.truncate(0) buffer.seek(0) buffer.write(remaining) break else: buffer.seek(0, 2) logger.info(f"Arguments: {escaped_args}") sproc = subprocess.Popen(["notmuch"] + shlex.split(escaped_args), shell = False, stdin = subprocess.PIPE, stdout = subprocess.PIPE) def send_stdout(): while True: to_send = sproc.stdout.read(256) logger.debug(f"Sending: {to_send}") if to_send: conn.sendall(to_send) else: logger.debug(f"Done sending") break def recv_stdin(): remaining = buffer.read() logger.debug(f"Received (buffered): {remaining}") sproc.stdin.write(remaining) while True: logger.debug("Waiting on input...") ready = select([conn], [], [], 5) if ready[0]: resp = conn.recv(256) logger.debug(f"Received: {resp}") if len(resp) <= 0: break sproc.stdin.write(resp) else: break logger.debug(f"Done receiving") sproc.stdin.close() sleep(5) sproc.kill() send = Process(target = send_stdout) recv = Process(target = recv_stdin) send.start() recv.start() sproc_res = sproc.wait() logger.info(f"Subprocess result: {sproc_res}") send.join() recv.terminate() logger.debug(f"Handled I/O") finally: if conn is not None: conn.close()