]>
git.ipfire.org Git - ipfire.org.git/blob - src/backend/nopaste.py
8 import tornado
.iostream
9 import tornado
.tcpserver
12 from .misc
import Object
13 from .decorators
import *
16 log
= logging
.getLogger(__name__
)
18 CHUNK_SIZE
= 1024 ** 2
20 class Nopaste(Object
):
21 def _get_paste(self
, query
, *args
, **kwargs
):
22 return self
.db
.fetch_one(Paste
, query
, *args
, **kwargs
)
24 def create(self
, content
, subject
=None, mimetype
=None, expires
=None, account
=None, address
=None):
26 blob_id
= self
._store
_blob
(content
)
28 # Guess the mimetype if none set
30 mimetype
= magic
.from_buffer(content
, mime
=True)
37 expires
= datetime
.datetime
.utcnow() + datetime
.timedelta(seconds
=expires
)
39 # http://blog.00null.net/easily-generating-random-strings-in-postgresql/
40 paste
= self
._get
_paste
("""
56 random_slug(), %s, %s, %s, %s, %s, %s, %s, %s
60 """, subject
, content
, expires
or None, address
, uid
, mimetype
, len(content
), blob_id
,
64 log
.info("Created a new paste (%s) of %s byte(s) from %s (%s - %s)" % (
65 paste
.uuid
, paste
.size
, paste
.address
, paste
.asn
or "N/A", paste
.country
or "N/A",
70 def _fetch_blob(self
, id):
71 blob
= self
.db
.get("""
84 def _store_blob(self
, data
):
86 Stores the blob by sending it to the database and returning its ID
88 blob
= self
.db
.get("""
103 last_uploaded_at = CURRENT_TIMESTAMP
113 paste
= self
._get
_paste
("""
121 expires_at >= CURRENT_TIMESTAMP
130 def get_content(self
, uuid
):
131 res
= self
.db
.get("SELECT content FROM nopaste \
132 WHERE uuid = %s", uuid
)
135 return bytes(res
.content
)
137 def _update_lastseen(self
, uuid
):
138 self
.db
.execute("UPDATE nopaste SET time_lastseen = NOW(), views = views + 1 \
139 WHERE uuid = %s", uuid
)
143 Removes all expired pastes and removes any unneeded blobs
145 # Remove all expired pastes
150 expires_at < CURRENT_TIMESTAMP
153 # Remove unneeded blobs
164 nopaste.blob_id = nopaste_blobs.id
170 def init(self
, id, data
):
171 self
.id, self
.data
= id, data
177 return self
.data
.uuid
183 return self
.data
.subject
188 def created_at(self
):
189 return self
.data
.created_at
191 time_created
= created_at
196 def expires_at(self
):
197 return self
.data
.expires_at
199 time_expires
= expires_at
205 return self
.backend
.nopaste
._fetch
_blob
(self
.data
.blob_id
)
213 return self
.data
.size
219 return self
.data
.mimetype
or "application/octet-stream"
225 return self
.data
.address
231 return self
.backend
.location
.lookup("%s" % self
.address
)
237 if self
.location
and self
.location
.asn
:
238 return self
.backend
.location
.get_asn(self
.location
.asn
)
244 if self
.location
and self
.location
.country_code
:
245 return self
.backend
.location
.get_country(self
.location
.country_code
)
254 class Service(tornado
.tcpserver
.TCPServer
):
255 def __init__(self
, config
, **kwargs
):
257 self
.backend
= base
.Backend(config
)
259 super().__init
__(**kwargs
)
261 async def handle_stream(self
, stream
, address
):
262 buffer = io
.BytesIO()
264 # Read the entire stream
267 chunk
= await stream
.read_bytes(CHUNK_SIZE
, partial
=True)
269 log
.debug("Read a chunk of %s byte(s)" % len(chunk
))
271 # Write the chunk into the buffer
274 # If we have read less then we have reached the end
275 if len(chunk
) < CHUNK_SIZE
:
278 # End if the stream closed unexpectedly
279 except tornado
.iostream
.StreamClosedError
as e
:
282 log
.debug("Finished reading payload")
285 address
, port
= address
287 # Store this into the database
288 with self
.backend
.db
.transaction():
289 uuid
= self
.backend
.nopaste
.create(
291 subject
="Streamed Upload",
295 # Format a response message
296 message
= "https://nopaste.ipfire.org/view/%s\n" % uuid
299 await stream
.write(message
.encode("utf-8"))
301 # We are done, close the stream