From: Michael Tremer Date: Thu, 22 Feb 2024 18:42:32 +0000 (+0000) Subject: nopaste: Create a service that takes pastes through a TCP connection X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=539d08da070d667659c0dbfbb402fbb169607b2d;p=ipfire.org.git nopaste: Create a service that takes pastes through a TCP connection Signed-off-by: Michael Tremer --- diff --git a/src/backend/nopaste.py b/src/backend/nopaste.py index d6101a26..42b5fab9 100644 --- a/src/backend/nopaste.py +++ b/src/backend/nopaste.py @@ -1,10 +1,21 @@ #!/usr/bin/python3 +import asyncio import datetime +import io +import logging import magic +import tornado.iostream +import tornado.tcpserver +from . import base from .misc import Object +# Setup logging +log = logging.getLogger(__name__) + +CHUNK_SIZE = 1024 ** 2 + class Nopaste(Object): def create(self, content, subject=None, mimetype=None, expires=None, account=None, address=None): self._cleanup_database() @@ -112,3 +123,127 @@ class Nopaste(Object): WHEN time_expires IS NULL \ THEN time_lastseen + INTERVAL '6 months' <= NOW() \ ELSE NOW() >= time_expires END)") + + +class Paste(Object): + def init(self, id, data): + self.id, self.data = id, data + + # UUID + + @property + def uuid(self): + return self.data.uuid + + # Subject + + @property + def subject(self): + return self.data.subject + + # Created At + + @property + def created_at(self): + return self.data.created_at + + time_created = created_at + + # Expires At + + @property + def expires_at(self): + return self.data.expires_at + + time_expires = expires_at + + # Blob + + @lazy_property + def blob(self): + return self.backend.nopaste._fetch_blob(self.data.blob_id) + + content = blob + + # Size + + @property + def size(self): + return self.data.size + + # Address + + @property + def address(self): + return self.data.address + + # Location + + @lazy_property + def location(self): + return self.backend.location.lookup("%s" % self.address) + + # ASN + + @lazy_property + def asn(self): + if self.location and self.location.asn: + return self.backend.location.get_asn(self.location.asn) + + # Country + + @lazy_property + def country(self): + if self.location and self.location.country_code: + return self.backend.location.get_country(self.location.country_code) + + +class Service(tornado.tcpserver.TCPServer): + def __init__(self, config, **kwargs): + # Initialize backend + self.backend = base.Backend(config) + + super().__init__(**kwargs) + + async def handle_stream(self, stream, address): + buffer = io.BytesIO() + + # Read the entire stream + try: + while True: + chunk = await stream.read_bytes(CHUNK_SIZE, partial=True) + + log.debug("Read a chunk of %s byte(s)" % len(chunk)) + + # Write the chunk into the buffer + buffer.write(chunk) + + # If we have read less then we have reached the end + if len(chunk) < CHUNK_SIZE: + break + + # End if the stream closed unexpectedly + except tornado.iostream.StreamClosedError as e: + return + + log.debug("Finished reading payload") + + # Process address + address, port = address + + # Store this into the database + with self.backend.db.transaction(): + uuid = self.backend.nopaste.create( + buffer.getvalue(), + subject="Streamed Upload", + address=address, + ) + + # Format a response message + message = "https://nopaste.ipfire.org/view/%s\n" % uuid + + # Send the message + await stream.write(message.encode("utf-8")) + + # We are done, close the stream + stream.close() diff --git a/src/scripts/ipfire.org-webapp.in b/src/scripts/ipfire.org-webapp.in index 0c8a0e7f..86a7b1d3 100755 --- a/src/scripts/ipfire.org-webapp.in +++ b/src/scripts/ipfire.org-webapp.in @@ -6,6 +6,7 @@ import tornado.options tornado.options.define("debug", type=bool, default=False, help="Enable debug mode") tornado.options.define("port", type=int, default=8001, help="Port to listen on") +import ipfire.nopaste from ipfire.web import Application async def run(): @@ -16,6 +17,10 @@ async def run(): debug=tornado.options.options.debug) app.listen(tornado.options.options.port, xheaders=True) + # Initialize the nopaste service + nopaste = ipfire.nopaste.Service("@configsdir@/@PACKAGE_NAME@.conf") + nopaste.listen(tornado.options.options.port + 1000) + # Wait for forever await asyncio.Event().wait()