summaryrefslogtreecommitdiff
path: root/notmuch-tcp-server
diff options
context:
space:
mode:
Diffstat (limited to 'notmuch-tcp-server')
-rw-r--r--notmuch-tcp-server142
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
3from os import environ
4from os.path import expanduser
5import socket
6import ssl
7import shlex
8from sys import argv, stdin, stdout, exit
9from multiprocessing import Process
10from select import select
11from io import BytesIO
12import subprocess
13from time import sleep
14
15import logging
16
17
18logger = logging.getLogger('notmuch-tcp-server')
19logger.setLevel(logging.DEBUG)
20lh = logging.StreamHandler()
21lh.setLevel(logging.DEBUG)
22lh.setFormatter(logging.Formatter( fmt = '{levelname} - {message}', style = '{' ))
23logger.addHandler(lh)
24
25
26port = environ.get('NOTMUCH_TCP')
27host = environ.get('NOTMUCH_HOST')
28hostname = socket.gethostname()
29
30if port is None:
31 port = 2010
32if host is None:
33 host = "odin.asgard.yggdrasil"
34
35sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
36sslcontext.load_verify_locations(cafile = expanduser('~/.notmuch-tcp/ca.pem'))
37sslcontext.load_cert_chain(certfile = expanduser(f"~/.notmuch-tcp/{hostname}.pem"), keyfile = expanduser(f"~/.notmuch-tcp/{hostname}.key"))
38
39s = None
40for 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
57if s is None:
58 logger.error('Could not open any sockets')
59 exit(1)
60
61with 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()