コミットを比較

...

10 コミット

作成者 SHA1 メッセージ 日付
Izuru Yakumo 363d4f49a7 Rid of every systemd reference
Signed-off-by: Izuru Yakumo <yakumo.izuru@chaotic.ninja>
2023-05-19 16:21:04 -03:00
Izuru Yakumo 66db42e7b3 It's 0x0a not 0x0
Signed-off-by: Izuru Yakumo <yakumo.izuru@chaotic.ninja>
2023-05-19 16:07:48 -03:00
Izuru Yakumo d230f9461b .rst is horrible, use html 3.2 with almost all templates, ascii art included as a bonus
Signed-off-by: Izuru Yakumo <yakumo.izuru@chaotic.ninja>
2023-05-18 23:51:08 -03:00
Izuru Yakumo b5079c5d82 Added sample service file and sample app.ini, reworked main template.
Signed-off-by: Izuru Yakumo <yakumo.izuru@chaotic.ninja>
2023-04-11 16:18:53 -03:00
Mia Herkt c2b5e95903
ModUI: Handle opening filter panel with NULL user agent 2023-03-29 07:49:56 +02:00
Mia Herkt c189c47306
ModUI: Allow LIKE matching for address filtering 2023-03-29 07:38:36 +02:00
Mia Herkt 3d1facaec3
Store user agent with files
Needed for moderation.
2023-03-29 07:36:49 +02:00
Mia Herkt e00866f5e4
URL: Explicitly set upper-case table name
Looks like recent SQLAlchemy/Alembic chose to lower-case it by
default. Try not to break existing schemas.
2023-03-29 07:19:47 +02:00
jonas-w 3950f6e8eb
fix 500 error when file extension could not be guessed
when a file without an extension was uploaded
and the mimetypes.guess_extension returned None
because there is no official file extension
for that mimetype a NoneType was subscripted
which yielded a 500 http error
2023-01-15 20:36:39 +01:00
Mia Herkt e1e99957b6
ModUI: Fix crash when encountering null NSFW score
Fixes #78
2022-12-29 19:51:04 +01:00
18個のファイルの変更356行の追加295行の削除

ファイルの表示

@ -1,22 +0,0 @@
[Unit]
Description=Prune 0x0 files
After=remote-fs.target
[Service]
Type=oneshot
User=nullptr
WorkingDirectory=/path/to/0x0
BindPaths=/path/to/0x0
Environment=FLASK_APP=fhost
ExecStart=/usr/bin/flask prune
ProtectProc=noaccess
ProtectSystem=strict
ProtectHome=tmpfs
PrivateTmp=true
PrivateUsers=true
ProtectKernelLogs=true
LockPersonality=true
[Install]
WantedBy=multi-user.target

ファイルの表示

@ -1,9 +0,0 @@
[Unit]
Description=Prune 0x0 files
[Timer]
OnCalendar=hourly
Persistent=true
[Install]
WantedBy=timers.target

ファイルの表示

@ -1,22 +0,0 @@
[Unit]
Description=Scan 0x0 files with ClamAV
After=remote-fs.target clamd.service
[Service]
Type=oneshot
User=nullptr
WorkingDirectory=/path/to/0x0
BindPaths=/path/to/0x0
Environment=FLASK_APP=fhost
ExecStart=/usr/bin/flask vscan
ProtectProc=noaccess
ProtectSystem=strict
ProtectHome=tmpfs
PrivateTmp=true
PrivateUsers=true
ProtectKernelLogs=true
LockPersonality=true
[Install]
WantedBy=multi-user.target

ファイルの表示

@ -1,9 +0,0 @@
[Unit]
Description=Scan 0x0 files with ClamAV
[Timer]
OnCalendar=hourly
Persistent=true
[Install]
WantedBy=timers.target

2
0x0a-prune.crontab ノーマルファイル
ファイルの表示

@ -0,0 +1,2 @@
FLASK_APP=fhost
* * 15 * * flask prune

2
0x0a-vscan.crontab ノーマルファイル
ファイルの表示

@ -0,0 +1,2 @@
FLASK_APP=fhost
@hourly flask vscan

125
README.md ノーマルファイル
ファイルの表示

@ -0,0 +1,125 @@
# 「null:ポインタ」
This is a no-bullshit file hosting and URL shortening service.
Use with uWSGI.
# Configuration
To configure 0x0a, copy `instance/config.example.py` to
`instance/config.py`, then edit it. Resonable defaults are set, but
there\'s a couple options you\'ll need to change before running 0x0 for
the first time.
By default, the configuration is stored in the Flask instance directory.
Normally, this is in [./instance]{.title-ref}, but it might be different
for your system. For details, see [the Flask
documentation](https://flask.palletsprojects.com/en/2.0.x/config/#instance-folders).
To customize the home and error pages, simply create a `templates`
directory in your instance directory and copy any templates you want to
modify there.
If you are running nginx, you should use the `X-Accel-Redirect` header.
To make it work, include this in your nginx config's `server` block:
location /up {
internal;
}
where `/up` is whatever you've configured as `FHOST_STORAGE_PATH`.
For all other servers, set `FHOST_USE_X_ACCEL_REDIRECT` to `False` and
`USE_X_SENDFILE` to `True`, assuming your server supports this.
Otherwise, Flask will serve the file with chunked encoding, which has
several downsides, one of them being that range requests will not work.
This is a problem for example when streaming media files: It won't be
possible to seek, and some ISOBMFF (MP4) files will not play at all.
To make files expire, simply run `FLASK_APP=fhost flask prune` every now
and then. You can use the provided crontab files for this.
Make sure to edit them to match your system configuration, assuming
you have read `crontab(5)` on a sane OS.
Before running the service for the first time and every time you update
it from this git repository, run `FLASK_APP=fhost flask db upgrade`.
# Moderation UI
![image](modui.webp){height="300px"}
0x0a features a TUI program for file moderation. With it, you can view a
list of uploaded files, as well as extended information on them. It
allows you to take actions like removing files temporarily or
permanently, as well as blocking IP addresses and associated files.
If a sufficiently recent version of python-mpv with libmpv is present
and your terminal supports it, you also get graphical file previews,
including video playback. Upstream mpv currently supports sixels and the
[kitty graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/). For this
to work, set the `MOD_PREVIEW_PROTO` option in `instance/config.py`.
Requirements:
* [Textual](https://textual.textualize.io/)
Optional:
* [python-mpv](https://github.com/jaseg/python-mpv) (graphical
previews)
* [PyAV](https://github.com/PyAV-Org/PyAV) (information on multimedia
files)
* [PyMuPDF](https://github.com/pymupdf/PyMuPDF) (previews and file
information for PDF, XPS, EPUB, MOBI and FB2)
* [libarchive-c](https://github.com/Changaco/python-libarchive-c)
(archive content listing)
** Note: [Mosh](https://mosh.org/) currently does not support sixels or kitty
graphics.
** Hint
You may need to set the `COLORTERM` environment variable to `truecolor`.
** Tip
Using compression with SSH (`-C` option) can significantly reduce the
bandwidth requirements for graphics.
# NSFW Detection
0x0a supports classification of NSFW content via Yahoo's open\_nsfw Caffe
neural network model. This works for images and video files and requires
the following:
- Caffe Python module (built for Python 3)
- [PyAV](https://github.com/PyAV-Org/PyAV)
# Virus Scanning
0x0 can scan its files with ClamAV's daemon. As this can take a long
time for larger files, this does not happen immediately but instead
every time you run the `vscan` command. It is recommended to configure a
systemd timer or cronjob to do this periodically. Examples are included:
0x0a-vscan.crontab
Remember to adjust your size limits in clamd.conf, including
`StreamMaxLength`!
This feature requires the [clamd
module](https://pypi.org/project/clamd/).
# Network Security Considerations
Keep in mind that 0x0a can fetch files from URLs. This includes your
local network! You should take precautions so that this feature cannot
be abused. 0x0a does not (yet) have a way to filter remote URLs, but on
Linux, you can use firewall rules and/or namespaces. This is less
error-prone anyway.
For instance, if you are using the excellent
[FireHOL](https://firehol.org/), it's very easy to create a group on
your system and use it as a condition in your firewall rules. You would
then run the application server under that group.
# Credits
* All the ASCII art used on the templates was taken from https://www.asciiart.eu/

ファイルの表示

@ -1,134 +0,0 @@
The Null Pointer
================
This is a no-bullshit file hosting and URL shortening service that also runs
`0x0.st <https://0x0.st>`_. Use with uWSGI.
Configuration
-------------
To configure 0x0, copy ``instance/config.example.py`` to ``instance/config.py``, then edit
it. Resonable defaults are set, but there's a couple options you'll need to change
before running 0x0 for the first time.
By default, the configuration is stored in the Flask instance directory.
Normally, this is in `./instance`, but it might be different for your system.
For details, see
`the Flask documentation <https://flask.palletsprojects.com/en/2.0.x/config/#instance-folders>`_.
To customize the home and error pages, simply create a ``templates`` directory
in your instance directory and copy any templates you want to modify there.
If you are running nginx, you should use the ``X-Accel-Redirect`` header.
To make it work, include this in your nginx configs ``server`` block::
location /up {
internal;
}
where ``/up`` is whatever youve configured as ``FHOST_STORAGE_PATH``.
For all other servers, set ``FHOST_USE_X_ACCEL_REDIRECT`` to ``False`` and
``USE_X_SENDFILE`` to ``True``, assuming your server supports this.
Otherwise, Flask will serve the file with chunked encoding, which has several
downsides, one of them being that range requests will not work. This is a
problem for example when streaming media files: It wont be possible to seek,
and some ISOBMFF (MP4) files will not play at all.
To make files expire, simply run ``FLASK_APP=fhost flask prune`` every
now and then. You can use the provided systemd unit files for this::
0x0-prune.service
0x0-prune.timer
Make sure to edit them to match your system configuration. In particular,
set the user and paths in ``0x0-prune.service``.
Before running the service for the first time and every time you update it
from this git repository, run ``FLASK_APP=fhost flask db upgrade``.
Moderation UI
-------------
.. image:: modui.webp
:height: 300
0x0 features a TUI program for file moderation. With it, you can view a list
of uploaded files, as well as extended information on them. It allows you to
take actions like removing files temporarily or permanently, as well as
blocking IP addresses and associated files.
If a sufficiently recent version of python-mpv with libmpv is present and
your terminal supports it, you also get graphical file previews, including
video playback. Upstream mpv currently supports sixels and the
`kitty graphics protocol <https://sw.kovidgoyal.net/kitty/graphics-protocol/>`_.
For this to work, set the ``MOD_PREVIEW_PROTO`` option in ``instance/config.py``.
Requirements:
* `Textual <https://textual.textualize.io/>`_
Optional:
* `python-mpv <https://github.com/jaseg/python-mpv>`_
(graphical previews)
* `PyAV <https://github.com/PyAV-Org/PyAV>`_
(information on multimedia files)
* `PyMuPDF <https://github.com/pymupdf/PyMuPDF>`_
(previews and file information for PDF, XPS, EPUB, MOBI and FB2)
* `libarchive-c <https://github.com/Changaco/python-libarchive-c>`_
(archive content listing)
.. note::
`Mosh <https://mosh.org/>`_ currently does not support sixels or kitty graphics.
.. hint::
You may need to set the ``COLORTERM`` environment variable to
``truecolor``.
.. tip::
Using compression with SSH (``-C`` option) can significantly
reduce the bandwidth requirements for graphics.
NSFW Detection
--------------
0x0 supports classification of NSFW content via Yahoos open_nsfw Caffe
neural network model. This works for images and video files and requires
the following:
* Caffe Python module (built for Python 3)
* `PyAV <https://github.com/PyAV-Org/PyAV>`_
Virus Scanning
--------------
0x0 can scan its files with ClamAVs daemon. As this can take a long time
for larger files, this does not happen immediately but instead every time
you run the ``vscan`` command. It is recommended to configure a systemd
timer or cronjob to do this periodically. Examples are included::
0x0-vscan.service
0x0-vscan.timer
Remember to adjust your size limits in clamd.conf, including
``StreamMaxLength``!
This feature requires the `clamd module <https://pypi.org/project/clamd/>`_.
Network Security Considerations
-------------------------------
Keep in mind that 0x0 can fetch files from URLs. This includes your local
network! You should take precautions so that this feature cannot be abused.
0x0 does not (yet) have a way to filter remote URLs, but on Linux, you can
use firewall rules and/or namespaces. This is less error-prone anyway.
For instance, if you are using the excellent `FireHOL <https://firehol.org/>`_,
its very easy to create a group on your system and use it as a condition
in your firewall rules. You would then run the application server under that
group.

13
app.ini ノーマルファイル
ファイルの表示

@ -0,0 +1,13 @@
[uwsgi]
uid = nullptr
master = true
processes = 10
socket = socket
chmod-socket = 664
vacuum = true
file = fhost.py
plugins = python
chdir = /path/to/0x0
callable = app
[nullptr]
timeshift = 0

ファイルの表示

@ -106,6 +106,7 @@ db = SQLAlchemy(app)
migrate = Migrate(app, db)
class URL(db.Model):
__tablename__ = "URL"
id = db.Column(db.Integer, primary_key = True)
url = db.Column(db.UnicodeText, unique = True)
@ -134,6 +135,7 @@ class File(db.Model):
ext = db.Column(db.UnicodeText)
mime = db.Column(db.UnicodeText)
addr = db.Column(db.UnicodeText)
ua = db.Column(db.UnicodeText)
removed = db.Column(db.Boolean, default=False)
nsfw_score = db.Column(db.Float)
expiration = db.Column(db.BigInteger)
@ -142,11 +144,12 @@ class File(db.Model):
last_vscan = db.Column(db.DateTime)
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.ext = ext
self.mime = mime
self.addr = addr
self.ua = ua
self.expiration = expiration
self.mgmt_token = mgmt_token
@ -211,7 +214,7 @@ class File(db.Model):
Any value greater that the longest allowed file lifespan will be rounded down to that
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()
digest = sha256(data).hexdigest()
@ -247,8 +250,10 @@ class File(db.Model):
if not ext:
if gmime in app.config["FHOST_EXT_OVERRIDE"]:
ext = app.config["FHOST_EXT_OVERRIDE"][gmime]
elif guess:
ext = guess
else:
ext = guess_extension(gmime)
ext = ""
return ext[:app.config["FHOST_MAX_EXT_LENGTH"]] or ".bin"
@ -275,9 +280,10 @@ class File(db.Model):
mime = get_mime()
ext = get_ext(mime)
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.ua = ua
if isnew:
f.secret = None
@ -365,11 +371,11 @@ requested_expiration can be:
Any value greater that the longest allowed file lifespan will be rounded down to that
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):
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.headers["X-Expires"] = sf.expiration
@ -379,7 +385,7 @@ def store_file(f, requested_expiration: typing.Optional[int], addr, secret: boo
return response
def store_url(url, addr, secret: bool):
def store_url(url, addr, ua, secret: bool):
if is_fhost_url(url):
abort(400)
@ -400,7 +406,7 @@ def store_url(url, addr, secret: bool):
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:
abort(413)
else:
@ -495,6 +501,7 @@ def fhost():
request.files["file"],
int(request.form["expires"]),
request.remote_addr,
request.user_agent.string,
secret
)
except ValueError:
@ -506,12 +513,14 @@ def fhost():
request.files["file"],
None,
request.remote_addr,
request.user_agent.string,
secret
)
elif "url" in request.form:
return store_url(
request.form["url"],
request.remote_addr,
request.user_agent.string,
secret
)
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 ###

8
mod.py
ファイルの表示

@ -26,6 +26,7 @@ class NullptrMod(Screen):
("f4", "filter(4, 'Filter extension:')", "Filter Ext."),
("f5", "refresh", "Refresh"),
("f6", "filter_clear", "Clear filter"),
("f7", "filter(5, 'Filter user agent:')", "Filter UA"),
("r", "remove_file(False)", "Remove file"),
("ctrl+r", "remove_file(True)", "Ban file"),
("p", "ban_ip(False)", "Ban IP"),
@ -60,6 +61,7 @@ class NullptrMod(Screen):
case 2: finput.value = self.current_file.addr
case 3: finput.value = self.current_file.mime
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:
self.query_one("#filter_container").display = False
@ -71,9 +73,10 @@ class NullptrMod(Screen):
case 1:
try: ftable.query = ftable.base_query.filter(File.id == su.debase(message.value))
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 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:
ftable.query = ftable.base_query
@ -249,9 +252,10 @@ class NullptrMod(Screen):
("MIME type:", f.mime),
("SHA256 checksum:", f.sha256),
("Uploaded by:", Text(f.addr)),
("User agent:", Text(f.ua or "")),
("Management token:", f.mgmt_token),
("Secret:", f.secret),
("Is NSFW:", ("Yes" if f.is_nsfw else "No") + f" (Score: {f.nsfw_score:0.4f})"),
("Is NSFW:", ("Yes" if f.is_nsfw else "No") + (f" (Score: {f.nsfw_score:0.4f})" if f.nsfw_score else " (Not scanned)")),
("Is banned:", "Yes" if f.removed else "No"),
("Expires:", time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(File.get_expiration(f.expiration, f.size)/1000)))
])

ファイルの表示

@ -1,2 +1,21 @@
rm: cannot remove '{{ request.path.split("/")[1] }}': Permission denied
<!DOCTYPE HTML "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Unauthorized (401)</title>
</head>
<body>
<pre>
/\_/\
/\ / o o \
//\\ \~(*)~/
` \/ ^ /
| \|| ||
\ '|| ||
\)()-())
</pre>
<p>
rm: cannot remove '{{ request.path.split("/")[1] }}': Permission denied
</p>
</body>
</html>

ファイルの表示

@ -1,15 +1,23 @@
{% set pid = range(20,100)|random %}
<pre>Process {{ pid }} stopped
* thread #1: tid = {{ pid }}, {{ "{:#018x}".format(id(g)) }}, name = 'fhost'
frame #0:
Process {{ pid }} stopped
* thread #8: tid = {{ pid }}, {{ "{:#018x}".format(id(request)) }} fhost`get(path='{{ request.path }}') + 27 at fhost.c:139, name = 'fhost/responder', stop reason = invalid address (fault address: 0x30)
frame #0: {3:#018x} fhost`get(path='{{ request.path }}') + 27 at fhost.c:139
136 get(SrvContext *ctx, const char *path)
137 {
138 StoredObj *obj = ctx->store->query(shurl_debase(path));
-> 139 switch (obj->type) {
140 case ObjTypeFile:
141 ctx->serve_file_id(obj->id);
142 break;
(lldb) q</pre>
<!DOCTYPE HTML "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>The page you were looking for doesn't exist (404)</title>
</head>
<body bgcolor="#000000" text="#ffffff" link="#ffffff">
<pre>
/\_____/\
/ o o \ ???
( == ^ == )
) (
( )
( ( ) ( ) )
(__(__)___(__)__)
</pre>
<p>
These are not the kittens you're looking for. <br>
Move along.
</p>
<a href="/">Go Back?</a>
</body>
</html>

ファイルの表示

@ -1 +1,26 @@
Could not determine remote file size (no Content-Length in response header; shoot admin).
<!DOCTYPE HTML "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Length Required (411)</title>
</head>
<body bgcolor="#000000" text="#ffffff">
<pre>
,-. _,---._ __ / \
/ ) .-' `./ / \
( ( ,' `/ /|
\ `-" \'\ / |
`. , \ \ / |
/`. ,'-`----Y |
( ; | '
| ,-. ,-' | /
| | ( | hjw | /
) | \ `.___________|/
`--' `--'
</pre>
<p>
Could not determine remote file size (no Content-Length in response header; shoot admin).
</p>
</body>
</html>

ファイルの表示

@ -1 +1,20 @@
Remote file too large ({{ request.headers["content-length"]|filesizeformat(True) }} > {{ config["MAX_CONTENT_LENGTH"]|filesizeformat(True) }}).
<!DOCTYPE HTML "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Payload too large (413)</title>
</head>
<body bgcolor="#000000" text="#ffffff">
<pre>
(:`--..___...-''``-._ |`._
```--...--. . `-..__ .`/ _\
`\ ' ```--`. />
: : : `:`-'
`.:. `.._--...___ ``--...__
``--..,) ```----....__,) fl (tweeked)
</pre>
<p>
Remote file too large ({{ request.headers["content-length"]|filesizeformat(True) }} > {{ config["MAX_CONTENT_LENGTH"]|filesizeformat(True) }}).
</p>
</body>
</html>

ファイルの表示

@ -1 +1,33 @@
451 Unavailable For Legal Reasons
<!DOCTYPE HTML "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>Unavailable for legal reasons (451)</title>
</head>
<body>
<pre>
.___________________________________________________________________________.
| |
|___________________________________________________________________________|
| ! | | | | | |
|_____|__,________|___________|______.____}__,________}___________|_____|
| | \ \ \ \ \ | | / / / / / | |
|_________|___\_,_\___\___\.__|___|___|___|___/___/___/___/___|_________|
| | | | \/ ,".). ', \/. ', | | | |
|_____|___| |. ' ,'|// / (/ ,. ' ,\( / , | |___|_____|
| | ,| '|\/ \| \\,' ,'.' || \\,' .' ,| ' | |
|_._______| " ,|' .| \_ |\/ |#\_/#| /_|\_ |#\_/,) ',.|,' |_________|
| | | #|(,' ) \\\#\ \##/ ||/ \#\ \###/\ \/ \/.', | | |
|_____|__,| || ) | \ |/ /#/ |#( \|\\| |#/ /##( )| \\'/ |___|_____|
| | \ |.\\ |\_/#| /#)|##\ \\_/#| |######\ \\/#| | |
|_________| /\\_/# |#\##/# boba@gagme.wwa.com #@#\ /#/\ |_____.___|
| | | / ##/# ##\/#@####@@###V###\/#@####@@#Y### \ | | |
|___._|___| / :Y##@@##@##@@###@@@###@##@@###@@@###@####: \ |___|_____|
| | / :;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;: \ | |
|_________|/ :::::::::::::::::::::::::::::::::::::::: \|_________|
</pre>
<p>
That's too bad.
</p>
</body>
</html>

ファイルの表示

@ -1,69 +1,38 @@
<pre>
THE NULL POINTER
================
{% set fhost_url = url_for("fhost", _external=True).rstrip("/") %}
HTTP POST files here:
curl -F'file=@yourfile.png' {{ fhost_url }}
You can also POST remote URLs:
curl -F'url=http://example.com/image.jpg' {{ fhost_url }}
If you don't want the resulting URL to be easy to guess:
curl -F'file=@yourfile.png' -Fsecret= {{ fhost_url }}
curl -F'url=http://example.com/image.jpg' -Fsecret= {{ fhost_url }}
Or you can shorten URLs:
curl -F'shorten=http://example.com/some/long/url' {{ fhost_url }}
It is possible to append your own file name to the URL:
{{ fhost_url }}/aaa.jpg/image.jpeg
File URLs are valid for at least 30 days and up to a year (see below).
Shortened URLs do not expire.
Files can be set to expire sooner by adding an "expires" parameter (in hours)
curl -F'file=@yourfile.png' -Fexpires=24 {{ fhost_url }}
OR by setting "expires" to a timestamp in epoch milliseconds
curl -F'file=@yourfile.png' -Fexpires=1681996320000 {{ fhost_url }}
Expired files won't be removed immediately, but will be removed as part of
the next purge.
Whenever a file that does not already exist or has expired is uploaded,
the HTTP response header includes an X-Token field. You can use this
to perform management operations on the file.
To delete the file immediately:
curl -Ftoken=token_here -Fdelete= {{ fhost_url }}/abc.txt
To change the expiration date (see above):
curl -Ftoken=token_here -Fexpires=3 {{ fhost_url }}/abc.txt
{% set max_size = config["MAX_CONTENT_LENGTH"]|filesizeformat(True) %}
Maximum file size: {{ max_size }}
Not allowed: {{ config["FHOST_MIME_BLACKLIST"]|join(", ") }}
FILE RETENTION PERIOD
---------------------
retention = min_age + (-max_age + min_age) * pow((file_size / max_size - 1), 3)
days
{{'{: 6}'.format(config.get("FHOST_MAX_EXPIRATION", 31536000000)//86400000)}} | \
| \
| \
| \
| \
| \
| ..
| \
{{'{: 6.1f}'.format((config.get("FHOST_MIN_EXPIRATION", 2592000000)/2 + config.get("FHOST_MAX_EXPIRATION", 31536000000)/2)/86400000)}} | ----------..-------------------------------------------
| ..
| \
| ..
| ...
| ..
| ...
| ....
| ......
{{'{: 6}'.format(config.get("FHOST_MIN_EXPIRATION", 2592000000)//86400000)}} | ....................
0{{ ((config["MAX_CONTENT_LENGTH"]/2)|filesizeformat(True)).split(" ")[0].rjust(27) }}{{ max_size.split(" ")[0].rjust(27) }}
{{ max_size.split(" ")[1].rjust(54) }}
</pre>
<!DOCTYPE HTML "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>0x0a 「null:ポインタ」</title>
</head>
<body bgcolor="#000000">
<br><br><table border="2" cellpadding="12" cellspacing="0" bgcolor="#ffffff" align="center">
<tr>
<td align="center">
<h1>「null:ポインタ」</h1>
</td>
<td>
<h2>File upload</h2>
<center>
<p>
{% set fhost_url = url_for("fhost", _external=True).rstrip("/") %}
{% set max_size = config["MAX_CONTENT_LENGTH"]|filesizeformat(True) %}
<br>
<form action="{{ fhost_url }}" method="POST" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit" value="Submit">
</form>
Maximum file size: {{ max_size }} <br>
Not allowed: {{ config["FHOST_MIME_BLACKLIST"] }} <br>
<hr>
<h2>URL shortening</h2>
<form action="{{ fhost_url}}" method="POST">
<input type="text" name="shorten">
<input type="submit" value="Submit">
</form>
</p>
</center>
</td>
</tr>
</table>
</body>
</html>