Compare commits
7 commits
0a59f87ced
...
7d7bcd045e
Author | SHA1 | Date | |
---|---|---|---|
7d7bcd045e | |||
|
8a912e8744 | ||
|
c2b5e95903 | ||
|
c189c47306 | ||
|
3d1facaec3 | ||
|
e00866f5e4 | ||
|
3950f6e8eb |
3 changed files with 53 additions and 10 deletions
27
fhost.py
27
fhost.py
|
@ -107,6 +107,7 @@ db = SQLAlchemy(app)
|
||||||
migrate = Migrate(app, db)
|
migrate = Migrate(app, db)
|
||||||
|
|
||||||
class URL(db.Model):
|
class URL(db.Model):
|
||||||
|
__tablename__ = "URL"
|
||||||
id = db.Column(db.Integer, primary_key = True)
|
id = db.Column(db.Integer, primary_key = True)
|
||||||
url = db.Column(db.UnicodeText, unique = True)
|
url = db.Column(db.UnicodeText, unique = True)
|
||||||
|
|
||||||
|
@ -135,6 +136,7 @@ class File(db.Model):
|
||||||
ext = db.Column(db.UnicodeText)
|
ext = db.Column(db.UnicodeText)
|
||||||
mime = db.Column(db.UnicodeText)
|
mime = db.Column(db.UnicodeText)
|
||||||
addr = db.Column(db.UnicodeText)
|
addr = db.Column(db.UnicodeText)
|
||||||
|
ua = db.Column(db.UnicodeText)
|
||||||
removed = db.Column(db.Boolean, default=False)
|
removed = db.Column(db.Boolean, default=False)
|
||||||
nsfw_score = db.Column(db.Float)
|
nsfw_score = db.Column(db.Float)
|
||||||
expiration = db.Column(db.BigInteger)
|
expiration = db.Column(db.BigInteger)
|
||||||
|
@ -143,11 +145,12 @@ class File(db.Model):
|
||||||
last_vscan = db.Column(db.DateTime)
|
last_vscan = db.Column(db.DateTime)
|
||||||
size = db.Column(db.BigInteger)
|
size = db.Column(db.BigInteger)
|
||||||
|
|
||||||
def __init__(self, sha256, ext, mime, addr, expiration, mgmt_token):
|
def __init__(self, sha256, ext, mime, addr, ua, expiration, mgmt_token):
|
||||||
self.sha256 = sha256
|
self.sha256 = sha256
|
||||||
self.ext = ext
|
self.ext = ext
|
||||||
self.mime = mime
|
self.mime = mime
|
||||||
self.addr = addr
|
self.addr = addr
|
||||||
|
self.ua = ua
|
||||||
self.expiration = expiration
|
self.expiration = expiration
|
||||||
self.mgmt_token = mgmt_token
|
self.mgmt_token = mgmt_token
|
||||||
|
|
||||||
|
@ -212,7 +215,7 @@ class File(db.Model):
|
||||||
Any value greater that the longest allowed file lifespan will be rounded down to that
|
Any value greater that the longest allowed file lifespan will be rounded down to that
|
||||||
value.
|
value.
|
||||||
"""
|
"""
|
||||||
def store(file_, requested_expiration: typing.Optional[int], addr, secret: bool):
|
def store(file_, requested_expiration: typing.Optional[int], addr, ua, secret: bool):
|
||||||
data = file_.read()
|
data = file_.read()
|
||||||
digest = sha256(data).hexdigest()
|
digest = sha256(data).hexdigest()
|
||||||
|
|
||||||
|
@ -248,8 +251,10 @@ class File(db.Model):
|
||||||
if not ext:
|
if not ext:
|
||||||
if gmime in app.config["FHOST_EXT_OVERRIDE"]:
|
if gmime in app.config["FHOST_EXT_OVERRIDE"]:
|
||||||
ext = app.config["FHOST_EXT_OVERRIDE"][gmime]
|
ext = app.config["FHOST_EXT_OVERRIDE"][gmime]
|
||||||
|
elif guess:
|
||||||
|
ext = guess
|
||||||
else:
|
else:
|
||||||
ext = guess_extension(gmime)
|
ext = ""
|
||||||
|
|
||||||
return ext[:app.config["FHOST_MAX_EXT_LENGTH"]] or ".bin"
|
return ext[:app.config["FHOST_MAX_EXT_LENGTH"]] or ".bin"
|
||||||
|
|
||||||
|
@ -276,9 +281,10 @@ class File(db.Model):
|
||||||
mime = get_mime()
|
mime = get_mime()
|
||||||
ext = get_ext(mime)
|
ext = get_ext(mime)
|
||||||
mgmt_token = secrets.token_urlsafe()
|
mgmt_token = secrets.token_urlsafe()
|
||||||
f = File(digest, ext, mime, addr, expiration, mgmt_token)
|
f = File(digest, ext, mime, addr, ua, expiration, mgmt_token)
|
||||||
|
|
||||||
f.addr = addr
|
f.addr = addr
|
||||||
|
f.ua = ua
|
||||||
|
|
||||||
if isnew:
|
if isnew:
|
||||||
f.secret = None
|
f.secret = None
|
||||||
|
@ -366,11 +372,11 @@ requested_expiration can be:
|
||||||
Any value greater that the longest allowed file lifespan will be rounded down to that
|
Any value greater that the longest allowed file lifespan will be rounded down to that
|
||||||
value.
|
value.
|
||||||
"""
|
"""
|
||||||
def store_file(f, requested_expiration: typing.Optional[int], addr, secret: bool):
|
def store_file(f, requested_expiration: typing.Optional[int], addr, ua, secret: bool):
|
||||||
if in_upload_bl(addr):
|
if in_upload_bl(addr):
|
||||||
return "Your host is blocked from uploading files.\n", 451
|
return "Your host is blocked from uploading files.\n", 451
|
||||||
|
|
||||||
sf, isnew = File.store(f, requested_expiration, addr, secret)
|
sf, isnew = File.store(f, requested_expiration, addr, ua, secret)
|
||||||
|
|
||||||
response = make_response(sf.geturl())
|
response = make_response(sf.geturl())
|
||||||
response.headers["X-Expires"] = sf.expiration
|
response.headers["X-Expires"] = sf.expiration
|
||||||
|
@ -380,7 +386,7 @@ def store_file(f, requested_expiration: typing.Optional[int], addr, secret: boo
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def store_url(url, addr, secret: bool):
|
def store_url(url, addr, ua, secret: bool):
|
||||||
if is_fhost_url(url):
|
if is_fhost_url(url):
|
||||||
abort(400)
|
abort(400)
|
||||||
|
|
||||||
|
@ -395,13 +401,13 @@ def store_url(url, addr, secret: bool):
|
||||||
if "content-length" in r.headers:
|
if "content-length" in r.headers:
|
||||||
l = int(r.headers["content-length"])
|
l = int(r.headers["content-length"])
|
||||||
|
|
||||||
if l < app.config["MAX_CONTENT_LENGTH"]:
|
if l <= app.config["MAX_CONTENT_LENGTH"]:
|
||||||
def urlfile(**kwargs):
|
def urlfile(**kwargs):
|
||||||
return type('',(),kwargs)()
|
return type('',(),kwargs)()
|
||||||
|
|
||||||
f = urlfile(read=r.raw.read, content_type=r.headers["content-type"], filename="")
|
f = urlfile(read=r.raw.read, content_type=r.headers["content-type"], filename="")
|
||||||
|
|
||||||
return store_file(f, None, addr, secret)
|
return store_file(f, None, addr, ua, secret)
|
||||||
else:
|
else:
|
||||||
abort(413)
|
abort(413)
|
||||||
else:
|
else:
|
||||||
|
@ -496,6 +502,7 @@ def fhost():
|
||||||
request.files["file"],
|
request.files["file"],
|
||||||
int(request.form["expires"]),
|
int(request.form["expires"]),
|
||||||
request.remote_addr,
|
request.remote_addr,
|
||||||
|
request.user_agent.string,
|
||||||
secret
|
secret
|
||||||
)
|
)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
@ -507,12 +514,14 @@ def fhost():
|
||||||
request.files["file"],
|
request.files["file"],
|
||||||
None,
|
None,
|
||||||
request.remote_addr,
|
request.remote_addr,
|
||||||
|
request.user_agent.string,
|
||||||
secret
|
secret
|
||||||
)
|
)
|
||||||
elif "url" in request.form:
|
elif "url" in request.form:
|
||||||
return store_url(
|
return store_url(
|
||||||
request.form["url"],
|
request.form["url"],
|
||||||
request.remote_addr,
|
request.remote_addr,
|
||||||
|
request.user_agent.string,
|
||||||
secret
|
secret
|
||||||
)
|
)
|
||||||
elif "shorten" in request.form:
|
elif "shorten" in request.form:
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
"""Store user agent string with files
|
||||||
|
|
||||||
|
Revision ID: dd0766afb7d2
|
||||||
|
Revises: 30bfe33aa328
|
||||||
|
Create Date: 2023-03-29 07:18:49.113200
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'dd0766afb7d2'
|
||||||
|
down_revision = '30bfe33aa328'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('file', schema=None) as batch_op:
|
||||||
|
batch_op.add_column(sa.Column('ua', sa.UnicodeText(), nullable=True))
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('file', schema=None) as batch_op:
|
||||||
|
batch_op.drop_column('ua')
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
6
mod.py
6
mod.py
|
@ -26,6 +26,7 @@ class NullptrMod(Screen):
|
||||||
("f4", "filter(4, 'Filter extension:')", "Filter Ext."),
|
("f4", "filter(4, 'Filter extension:')", "Filter Ext."),
|
||||||
("f5", "refresh", "Refresh"),
|
("f5", "refresh", "Refresh"),
|
||||||
("f6", "filter_clear", "Clear filter"),
|
("f6", "filter_clear", "Clear filter"),
|
||||||
|
("f7", "filter(5, 'Filter user agent:')", "Filter UA"),
|
||||||
("r", "remove_file(False)", "Remove file"),
|
("r", "remove_file(False)", "Remove file"),
|
||||||
("ctrl+r", "remove_file(True)", "Ban file"),
|
("ctrl+r", "remove_file(True)", "Ban file"),
|
||||||
("p", "ban_ip(False)", "Ban IP"),
|
("p", "ban_ip(False)", "Ban IP"),
|
||||||
|
@ -60,6 +61,7 @@ class NullptrMod(Screen):
|
||||||
case 2: finput.value = self.current_file.addr
|
case 2: finput.value = self.current_file.addr
|
||||||
case 3: finput.value = self.current_file.mime
|
case 3: finput.value = self.current_file.mime
|
||||||
case 4: finput.value = self.current_file.ext
|
case 4: finput.value = self.current_file.ext
|
||||||
|
case 5: finput.value = self.current_file.ua or ""
|
||||||
|
|
||||||
def on_input_submitted(self, message: Input.Submitted) -> None:
|
def on_input_submitted(self, message: Input.Submitted) -> None:
|
||||||
self.query_one("#filter_container").display = False
|
self.query_one("#filter_container").display = False
|
||||||
|
@ -71,9 +73,10 @@ class NullptrMod(Screen):
|
||||||
case 1:
|
case 1:
|
||||||
try: ftable.query = ftable.base_query.filter(File.id == su.debase(message.value))
|
try: ftable.query = ftable.base_query.filter(File.id == su.debase(message.value))
|
||||||
except ValueError: pass
|
except ValueError: pass
|
||||||
case 2: ftable.query = ftable.base_query.filter(File.addr == message.value)
|
case 2: ftable.query = ftable.base_query.filter(File.addr.like(message.value))
|
||||||
case 3: ftable.query = ftable.base_query.filter(File.mime.like(message.value))
|
case 3: ftable.query = ftable.base_query.filter(File.mime.like(message.value))
|
||||||
case 4: ftable.query = ftable.base_query.filter(File.ext.like(message.value))
|
case 4: ftable.query = ftable.base_query.filter(File.ext.like(message.value))
|
||||||
|
case 5: ftable.query = ftable.base_query.filter(File.ua.like(message.value))
|
||||||
else:
|
else:
|
||||||
ftable.query = ftable.base_query
|
ftable.query = ftable.base_query
|
||||||
|
|
||||||
|
@ -249,6 +252,7 @@ class NullptrMod(Screen):
|
||||||
("MIME type:", f.mime),
|
("MIME type:", f.mime),
|
||||||
("SHA256 checksum:", f.sha256),
|
("SHA256 checksum:", f.sha256),
|
||||||
("Uploaded by:", Text(f.addr)),
|
("Uploaded by:", Text(f.addr)),
|
||||||
|
("User agent:", Text(f.ua or "")),
|
||||||
("Management token:", f.mgmt_token),
|
("Management token:", f.mgmt_token),
|
||||||
("Secret:", f.secret),
|
("Secret:", f.secret),
|
||||||
("Is NSFW:", ("Yes" if f.is_nsfw else "No") + (f" (Score: {f.nsfw_score:0.4f})" if f.nsfw_score else " (Not scanned)")),
|
("Is NSFW:", ("Yes" if f.is_nsfw else "No") + (f" (Score: {f.nsfw_score:0.4f})" if f.nsfw_score else " (Not scanned)")),
|
||||||
|
|
Loading…
Reference in a new issue