# -*- coding: utf-8 -*- """SuperBeam Handlers The handlers are automatically registered during the creation of the :class:`SuperBeamServer` instance. Each class handler method needs to be a ``staticmethod``. The callee will call the `handle()` with the ``httpd`` instance of the :class:`SuperBeamServer` as required argument. """ import os import json import uuid class BaseTextHandler(object): mimetype = 'text/mime' @staticmethod def handle(httpd): raise Exception(f'Not implemented: Unable to handle {httpd} ' '({BaseTextHandler.mimetype}') class OctetStreamHandler(object): mimetype = 'application/octet-stream' @staticmethod def handle(httpd): raise Exception(f'Not implemented: Unable to handle {httpd} ' '({OctetStreamHandler.mimetype}') class ChunkTemplateHandler(BaseTextHandler): """TODO """ URI = ('/', '/index.htm', 'index') class ThumbHandler(object): """TODO """ mimetype = 'image/png' URI = ('/getthumb',) @staticmethod def handle(httpd): raise Exception(f'Not implemented: Unable to handle {httpd} ' '({ThumbHandler.mimetype}') class LegacyListHandler(BaseTextHandler): """Legacy list handler for SuperBeam - plain text with file metadata URI: ``/superlist`` """ URI = ('/superlist',) @staticmethod def handle(httpd): raise Exception(f'Not implemented: Unable to handle {httpd} ' '({LegacyListHandler.mimetype}') class JsonListHandler(object): """Json List Handler - Metadata of served files as json URI: ``/jsonlist`` """ mimetype = 'application/json' URI = ('/jsonlist',) @staticmethod def _make_jsonlist(files): filestats = [] totalsize = 0 for file_ in files: statinfo = os.stat(file_) filename = os.path.basename(file_) filepath = os.path.dirname(file_) filestats.append({ 'created': str(int(statinfo.st_ctime) * 1000), 'modified': str(int(statinfo.st_mtime) * 1000), 'name': filename, 'path': filepath, 'size': str(statinfo.st_size), 'type': "Image" # TODO dynamic }) totalsize += statinfo.st_size jsonlist = { 'device': 'ANYONMOUS', 'empty_dirs': [], 'files': filestats, 'uuid': str(uuid.uuid4()), 'version': 4130 } return json.dumps(jsonlist), totalsize @staticmethod def handle(httpd): jsonlist, totalsize = JsonListHandler._make_jsonlist(httpd.files) httpd.send_response(200) httpd.send_header('Content-Type', 'application/json') httpd.end_headers() response = bytes(jsonlist, 'utf-8') httpd.wfile.write(response) httpd.total_file_size = totalsize class SingleFileHandler(OctetStreamHandler): """TODO """ URI = ('/get/',) class ZipFileHandler(object): """TODO """ mimetype = 'application/zip' URI = ('/getzip',) @staticmethod def handle(httpd): raise Exception('ZipFileHandler not implemeted.') class SuperStreamHandler(OctetStreamHandler): """Serve SuperBeam data stream Writes all files listed in `SuperBeamServer.files` as single stream. The client needs to request ``/jsonlist`` or ``/superlist`` prior to the download to be able to split the stream into single files. URI: ``/getstream`` """ URI = ('/getstream',) @staticmethod def _get_streamsize(files): totalsize = 0 for file_ in files: statinfo = os.stat(file_) totalsize += statinfo.st_size return totalsize @staticmethod def _stream_file_content(files): for file_ in files: with open(file_, 'rb') as f_handle: yield f_handle.read() @staticmethod def handle(httpd): httpd.send_response(200) httpd.send_header('ylfrettub', '1') httpd.send_header('Content-Type', 'application/octet-stream') httpd.send_header('Content-Length', str(SuperStreamHandler._get_streamsize(httpd.files))) httpd.send_header('Content-Disposition', 'attachment; ' 'filename="RedHotChillyStream"') httpd.end_headers() for chunk in SuperStreamHandler._stream_file_content(httpd.files): httpd.wfile.write(chunk) class AssetHandler(OctetStreamHandler): """TODO """ URI = ('*',) class ApkRequestHandler(object): """TODO """ mimetype = 'application/vnd.android.package-archive' URI = ('/getapk',) @staticmethod def handle(httpd): raise Exception(f'Not implemented: Unable to handle {httpd} ' '({ApkRequestHandler.mimetype}')