File caching

This commit is contained in:
Lynn Ochs 2019-05-12 14:13:25 +02:00
parent 3fe444e5aa
commit 947ed5ab9e
Signed by: apo
GPG Key ID: 588AD47B1D06BE99
2 changed files with 76 additions and 20 deletions

View File

@ -5,6 +5,7 @@ import os
import sys import sys
import mimetypes import mimetypes
import subprocess import subprocess
import hashlib
s = socket.socket(socket.AF_UNIX) s = socket.socket(socket.AF_UNIX)
s.connect(os.path.join(os.environ["HOME"], ".mailcap.sock")) s.connect(os.path.join(os.environ["HOME"], ".mailcap.sock"))
@ -19,4 +20,11 @@ data = open(filename, "rb").read()
def strtr(x, c="B"): def strtr(x, c="B"):
return struct.pack(c, len(x)) + x return struct.pack(c, len(x)) + x
s.sendall(strtr(filename.split(os.pathsep)[-1].encode('utf-8')) + strtr(mime.encode('utf-8')) + strtr(data, "!I")) hash = hashlib.sha256()
hash.update(data)
s.sendall(strtr(filename.split(os.pathsep)[-1].encode('utf-8')) + strtr(mime.encode('utf-8')) + hash.digest())
response = s.recv(1)
if len(response) and response[0] == 1:
s.sendall(strtr(data, "!I"))

View File

@ -7,6 +7,7 @@ import mailcap
import sys import sys
import argparse import argparse
import daemon import daemon
import hashlib
sockpath = "mailcap.sock" sockpath = "mailcap.sock"
@ -27,6 +28,9 @@ class Server(object):
def recv(self, size): def recv(self, size):
data = self._conn.recv(size) data = self._conn.recv(size)
return data return data
def send(self, data):
return self._conn.send(data)
def recv_exact(self, length): def recv_exact(self, length):
retval = bytes() retval = bytes()
@ -37,11 +41,16 @@ class Server(object):
retval += data retval += data
return retval return retval
def __init__(self, path="mailcap.sock"): def __init__(self, path="mailcap.sock", dir = None):
self._socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self._socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self._socket.bind(path) self._socket.bind(path)
self._socket.listen(3) self._socket.listen(3)
self._dir = tempfile.TemporaryDirectory() if dir:
if not os.path.exists(dir):
os.makedirs(dir)
self._dir = dir
else:
self._dir = tempfile.TemporaryDirectory()
def close(self): def close(self):
self._socket.close() self._socket.close()
@ -51,6 +60,7 @@ class Server(object):
conn = self.Connection(self._socket.accept()[0]) conn = self.Connection(self._socket.accept()[0])
conn.timeout(initial_timeout) conn.timeout(initial_timeout)
# Receive filename length
data = conn.recv_exact(1) data = conn.recv_exact(1)
if data is None: if data is None:
return None return None
@ -58,31 +68,64 @@ class Server(object):
conn.timeout(timeout) conn.timeout(timeout)
# Receive filename
data = conn.recv_exact(fn_len) data = conn.recv_exact(fn_len)
if data is None: if data is None:
return None return None
fn = data.decode('utf-8') fn = data.decode('utf-8')
# Strip the filename of /, do sanity checks
fn = fn.replace("/", "_") fn = fn.replace("/", "_")
if len(fn) == 0 or fn in ['.', '..']: if len(fn) == 0 or fn in ['.', '..']:
s.close()
return None return None
# Receive the mime type length
data = conn.recv_exact(1) data = conn.recv_exact(1)
if data is None: if data is None:
return None return None
mime_len = struct.unpack("B", data)[0] mime_len = struct.unpack("B", data)[0]
# Receive the mime type
data = conn.recv_exact(mime_len) data = conn.recv_exact(mime_len)
if data is None: if data is None:
return None return None
mime = data.decode('utf-8') mime = data.decode('utf-8')
# Receive the SHA256 hash
data = conn.recv_exact(hashlib.sha256().digest_size)
if data is None:
return None
full_name = os.path.join(self._dir.name, fn)
append = 0
# While the filename already exists, check if we have a hash match
while os.path.exists(full_name):
hash = hashlib.sha256()
hash.update(open(full_name, "rb").read())
# Found a matching file. Do not receive the file.
if data == hash.digest():
conn.send(b'\x00')
conn.close()
return full_name, mime
append += 1
hash = hashlib.sha256()
name_parts = fn.split('.')
if len(name_parts) > 1:
full_name = os.path.join(self._dir.name, "%s-%d.%s" % ('.'.join(name_parts[:-1]), append, name_parts[-1]))
else:
full_name = os.path.join(self._dir.name, "%s-%d" % (fn, append, name_parts[-1]))
conn.send(b'\x01')
# Receive the file length
data = conn.recv_exact(4) data = conn.recv_exact(4)
if data is None: if data is None:
return None return None
file_len = struct.unpack("!I", data)[0] file_len = struct.unpack("!I", data)[0]
print(fn, mime) # Receive the file data
fn = os.path.join(self._dir.name, fn) fn = os.path.join(self._dir.name, fn)
with open(fn, "wb+") as f: with open(fn, "wb+") as f:
while file_len > 0: while file_len > 0:
@ -98,12 +141,26 @@ class Server(object):
os.remove(fn) os.remove(fn)
return None return None
def run_server(args):
sv = Server(path=args.socket, dir=args.dir)
caps = mailcap.getcaps()
open(args.pid, "w+").write(str(os.getpid()))
while True:
rv = sv.next_file()
if rv:
f, mime = rv
print(f, mime)
match = mailcap.findmatch(caps, mime.split(';')[0], filename=f)
if match and match[0]:
os.system(match[0])
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('--pid', '-p', default=os.path.join(os.environ['HOME'], '.mailcap.pid')) parser.add_argument('--pid', '-p', default=os.path.join(os.environ['HOME'], '.mailcap.pid'))
parser.add_argument('--action', '-a', default='run', choices=['run', 'kill']) parser.add_argument('--action', '-a', default='run', choices=['run', 'kill'])
parser.add_argument('--no-daemonize', '-n', action='store_true') parser.add_argument('--no-daemonize', '-n', action='store_true')
parser.add_argument('--socket', '-s', default=os.path.join(os.environ['HOME'], '.mailcap.sock')) parser.add_argument('--socket', '-s', default=os.path.join(os.environ['HOME'], '.mailcap.sock'))
parser.add_argument('--dir', '-d', default=None)
args = parser.parse_args() args = parser.parse_args()
if args.action == 'kill': if args.action == 'kill':
@ -125,17 +182,8 @@ if __name__ == "__main__":
if os.path.exists(args.socket): if os.path.exists(args.socket):
os.remove(args.socket) os.remove(args.socket)
with daemon.DaemonContext(detach_process=not args.no_daemonize, files_preserve=[1,2]): if args.no_daemonize:
sv = Server(args.socket) run_server(args)
caps = mailcap.getcaps() else:
open(args.pid, "w+").write(str(os.getpid())) with daemon.DaemonContext():
while True: run_server()
rv = sv.next_file()
if rv:
f, mime = rv
print(f, mime)
match = mailcap.findmatch(caps, mime.split(';')[0], filename=f)
if match and match[0]:
os.system(match[0])
if 'nodelete' not in match[1]:
os.remove(f)