diff options
Diffstat (limited to 'notmuch-tcp-server')
-rw-r--r-- | notmuch-tcp-server | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/notmuch-tcp-server b/notmuch-tcp-server new file mode 100644 index 0000000..328b72c --- /dev/null +++ b/notmuch-tcp-server | |||
@@ -0,0 +1,142 @@ | |||
1 | #!@python@/bin/python | ||
2 | |||
3 | from os import environ | ||
4 | from os.path import expanduser | ||
5 | import socket | ||
6 | import ssl | ||
7 | import shlex | ||
8 | from sys import argv, stdin, stdout, exit | ||
9 | from multiprocessing import Process | ||
10 | from select import select | ||
11 | from io import BytesIO | ||
12 | import subprocess | ||
13 | from time import sleep | ||
14 | |||
15 | import logging | ||
16 | |||
17 | |||
18 | logger = logging.getLogger('notmuch-tcp-server') | ||
19 | logger.setLevel(logging.DEBUG) | ||
20 | lh = logging.StreamHandler() | ||
21 | lh.setLevel(logging.DEBUG) | ||
22 | lh.setFormatter(logging.Formatter( fmt = '{levelname} - {message}', style = '{' )) | ||
23 | logger.addHandler(lh) | ||
24 | |||
25 | |||
26 | port = environ.get('NOTMUCH_TCP') | ||
27 | host = environ.get('NOTMUCH_HOST') | ||
28 | hostname = socket.gethostname() | ||
29 | |||
30 | if port is None: | ||
31 | port = 2010 | ||
32 | if host is None: | ||
33 | host = "odin.asgard.yggdrasil" | ||
34 | |||
35 | sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) | ||
36 | sslcontext.load_verify_locations(cafile = expanduser('~/.notmuch-tcp/ca.pem')) | ||
37 | sslcontext.load_cert_chain(certfile = expanduser(f"~/.notmuch-tcp/{hostname}.pem"), keyfile = expanduser(f"~/.notmuch-tcp/{hostname}.key")) | ||
38 | |||
39 | s = None | ||
40 | for res in socket.getaddrinfo(host, int(port), socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE): | ||
41 | af, socktype, proto, canonname, sa = res | ||
42 | try: | ||
43 | s = socket.socket(af, socktype, proto) | ||
44 | except OSError as msg: | ||
45 | logger.error(f"Could not create socket: {msg}") | ||
46 | s = None | ||
47 | continue | ||
48 | try: | ||
49 | s.bind(sa) | ||
50 | s.listen() | ||
51 | except OSError as msg: | ||
52 | logger.error(f"Could not bind socket: {msg}") | ||
53 | s.close() | ||
54 | s = None | ||
55 | continue | ||
56 | break | ||
57 | if s is None: | ||
58 | logger.error('Could not open any sockets') | ||
59 | exit(1) | ||
60 | |||
61 | with s as sock: | ||
62 | with sslcontext.wrap_socket(sock, server_side = True) as ssock: | ||
63 | while True: | ||
64 | logger.debug('waiting...') | ||
65 | conn = None | ||
66 | try: | ||
67 | conn, client_addr = ssock.accept() | ||
68 | client_host, client_port = client_addr | ||
69 | logger.info(f"Connected: {client_host}:{client_port}") | ||
70 | |||
71 | with BytesIO() as buffer: | ||
72 | while True: | ||
73 | resp = conn.recv(256) | ||
74 | buffer.write(resp) | ||
75 | buffer.seek(0) | ||
76 | start_index = 0 | ||
77 | for line in buffer: | ||
78 | start_index += len(line) | ||
79 | escaped_args = line[:-1].decode() | ||
80 | break | ||
81 | if start_index: | ||
82 | buffer.seek(start_index) | ||
83 | remaining = buffer.read() | ||
84 | buffer.truncate(0) | ||
85 | buffer.seek(0) | ||
86 | buffer.write(remaining) | ||
87 | break | ||
88 | else: | ||
89 | buffer.seek(0, 2) | ||
90 | |||
91 | logger.info(f"Arguments: {escaped_args}") | ||
92 | |||
93 | sproc = subprocess.Popen(["notmuch"] + shlex.split(escaped_args), shell = False, stdin = subprocess.PIPE, stdout = subprocess.PIPE) | ||
94 | |||
95 | def send_stdout(): | ||
96 | while True: | ||
97 | to_send = sproc.stdout.read(256) | ||
98 | logger.debug(f"Sending: {to_send}") | ||
99 | |||
100 | if to_send: | ||
101 | conn.sendall(to_send) | ||
102 | else: | ||
103 | logger.debug(f"Done sending") | ||
104 | break | ||
105 | |||
106 | def recv_stdin(): | ||
107 | remaining = buffer.read() | ||
108 | logger.debug(f"Received (buffered): {remaining}") | ||
109 | sproc.stdin.write(remaining) | ||
110 | |||
111 | while True: | ||
112 | logger.debug("Waiting on input...") | ||
113 | ready = select([conn], [], [], 5) | ||
114 | if ready[0]: | ||
115 | resp = conn.recv(256) | ||
116 | logger.debug(f"Received: {resp}") | ||
117 | |||
118 | if len(resp) <= 0: | ||
119 | break | ||
120 | |||
121 | sproc.stdin.write(resp) | ||
122 | else: | ||
123 | break | ||
124 | logger.debug(f"Done receiving") | ||
125 | sproc.stdin.close() | ||
126 | |||
127 | sleep(5) | ||
128 | |||
129 | sproc.kill() | ||
130 | |||
131 | send = Process(target = send_stdout) | ||
132 | recv = Process(target = recv_stdin) | ||
133 | send.start() | ||
134 | recv.start() | ||
135 | sproc_res = sproc.wait() | ||
136 | logger.info(f"Subprocess result: {sproc_res}") | ||
137 | send.join() | ||
138 | recv.terminate() | ||
139 | logger.debug(f"Handled I/O") | ||
140 | finally: | ||
141 | if conn is not None: | ||
142 | conn.close() | ||