fallback uploader works

This commit is contained in:
ed 2019-06-10 19:47:01 +00:00
parent 1ea9b26a89
commit 7b4b38d98b
8 changed files with 129 additions and 18 deletions

View file

@ -15,7 +15,7 @@ turn your phone or raspi into a portable file server with resumable uploads/down
* [x] sanic multipart parser * [x] sanic multipart parser
* [x] load balancer (multiprocessing) * [x] load balancer (multiprocessing)
* [ ] upload (plain multipart, ie6 support) * [x] upload (plain multipart, ie6 support)
* [ ] upload (js, resumable, multithreaded) * [ ] upload (js, resumable, multithreaded)
* [x] download * [x] download
* [x] browser * [x] browser
@ -25,6 +25,8 @@ turn your phone or raspi into a portable file server with resumable uploads/down
* [x] volumes * [x] volumes
* [x] accounts * [x] accounts
summary: it works
# dependencies # dependencies
@ -64,9 +66,10 @@ pip install black bandit pylint flake8 # vscode tooling
``` ```
# TODO # immediate todo
roughly sorted by priority roughly sorted by priority
* http error handling (conn.status or handler-retval)
* last-modified header * last-modified header
* support pillow-simd * support pillow-simd

View file

@ -15,8 +15,10 @@ from .util import * # noqa # pylint: disable=unused-wildcard-import
if not PY2: if not PY2:
unicode = str unicode = str
from urllib.parse import unquote_plus from urllib.parse import unquote_plus
from urllib.parse import quote_plus
else: else:
from urllib import unquote_plus # pylint: disable=no-name-in-module from urllib import unquote_plus # pylint: disable=no-name-in-module
from urllib import quote_plus
class HttpCli(object): class HttpCli(object):
@ -151,7 +153,7 @@ class HttpCli(object):
if self.vpath == "" and not self.uparam: if self.vpath == "" and not self.uparam:
nread = len(self.rvol) nread = len(self.rvol)
nwrite = len(self.wvol) nwrite = len(self.wvol)
if nread + nwrite == 1: if nread + nwrite == 1 or (self.rvol == self.wvol and nread == 1):
if nread == 1: if nread == 1:
self.vpath = self.rvol[0] self.vpath = self.rvol[0]
else: else:
@ -212,19 +214,25 @@ class HttpCli(object):
pwd = u"x" # nosec pwd = u"x" # nosec
h = ["Set-Cookie: cppwd={}; Path=/".format(pwd)] h = ["Set-Cookie: cppwd={}; Path=/".format(pwd)]
html = u'<h1>{}</h1><h2><a href="/">ack</a></h2>'.format(msg) html = self.conn.tpl_msg.render(h1=msg, h2='<a href="/">ack</a>', redir="/")
html += '<script>setTimeout(function(){window.location.replace("/");},500);</script>'
self.reply(html.encode("utf-8"), headers=h) self.reply(html.encode("utf-8"), headers=h)
def handle_plain_upload(self): def handle_plain_upload(self):
nullwrite = self.args.nw nullwrite = self.args.nw
vfs, rem = self.conn.auth.vfs.get(self.vpath, self.uname, False, True)
# rem is escaped at this point,
# this is just a sanity check to prevent any disasters
if rem.startswith("/") or rem.startswith("../") or "/../" in rem:
raise Exception("that was close")
files = [] files = []
t0 = time.time() t0 = time.time()
for nfile, (p_field, p_file, p_data) in enumerate(self.parser.gen): for nfile, (p_field, p_file, p_data) in enumerate(self.parser.gen):
fn = os.devnull fn = os.devnull
if not nullwrite: if not nullwrite:
fn = sanitize_fn(p_file) fn = os.path.join(vfs.realpath, rem, sanitize_fn(p_file))
# TODO broker which avoid this race # TODO broker which avoid this race
# and provides a new filename if taken # and provides a new filename if taken
if os.path.exists(fn): if os.path.exists(fn):
@ -253,7 +261,14 @@ class HttpCli(object):
# truncated SHA-512 prevents length extension attacks; # truncated SHA-512 prevents length extension attacks;
# using SHA-512/224, optionally SHA-512/256 = :64 # using SHA-512/224, optionally SHA-512/256 = :64
self.loud_reply(msg) html = self.conn.tpl_msg.render(
h2='<a href="/{}">return to /{}</a>'.format(
quote_plus(self.vpath, safe="/"), cgi.escape(self.vpath, quote=True)
),
pre=msg,
)
self.log(msg)
self.reply(html.encode("utf-8"))
if not nullwrite: if not nullwrite:
# TODO this is bad # TODO this is bad
@ -310,10 +325,15 @@ class HttpCli(object):
vpnodes = [[u"/", u"/"]] vpnodes = [[u"/", u"/"]]
for node in self.vpath.split("/"): for node in self.vpath.split("/"):
vpath += u"/" + node vpath += u"/" + node
vpnodes.append([cgi.escape(vpath) + "/", cgi.escape(node)]) vpnodes.append([quote_plus(vpath, safe="/") + "/", cgi.escape(node)])
vn, rem = self.auth.vfs.get(self.vpath, self.uname, True, False) vn, rem = self.auth.vfs.get(self.vpath, self.uname, True, False)
abspath = vn.canonical(rem) abspath = vn.canonical(rem)
if not os.path.exists(abspath):
print(abspath)
raise Pebkac("404 not found")
if not os.path.isdir(abspath): if not os.path.isdir(abspath):
return self.tx_file(abspath) return self.tx_file(abspath)
@ -341,7 +361,13 @@ class HttpCli(object):
dt = datetime.utcfromtimestamp(inf.st_mtime) dt = datetime.utcfromtimestamp(inf.st_mtime)
dt = dt.strftime("%Y-%m-%d %H:%M:%S") dt = dt.strftime("%Y-%m-%d %H:%M:%S")
item = [margin, cgi.escape(href), cgi.escape(fn), sz, dt] item = [
margin,
quote_plus(href, safe="/"),
cgi.escape(fn, quote=True),
sz,
dt,
]
if is_dir: if is_dir:
dirs.append(item) dirs.append(item)
else: else:

View file

@ -31,6 +31,7 @@ class HttpConn(object):
self.tpl_mounts = self.load_tpl("splash.html") self.tpl_mounts = self.load_tpl("splash.html")
self.tpl_browser = self.load_tpl("browser.html") self.tpl_browser = self.load_tpl("browser.html")
self.tpl_msg = self.load_tpl("msg.html")
def load_tpl(self, fn): def load_tpl(self, fn):
with open(self.respath(fn), "rb") as f: with open(self.respath(fn), "rb") as f:

View file

@ -251,9 +251,8 @@ a.play.act {
} }
#bup { #bup {
padding: .5em .5em .5em .3em; padding: .5em .5em .5em .3em;
margin-bottom: 1em;
background: #2d2d2d; background: #2d2d2d;
border-radius: 0 .5em .5em 0; border-radius: 0 0 1em 0;
border-right: .3em solid #3a3a3a; border-right: .3em solid #3a3a3a;
max-width: 40em; max-width: 40em;
} }

View file

@ -10,13 +10,6 @@
</head> </head>
<body> <body>
<h1 id="path">
{%- for n in vpnodes[:-1] %}
<a href="{{ n[0] }}">{{ n[1] }}</a>
{%- endfor %}
<span>{{ vpnodes[-1][1] }}</span>
</h1>
{%- if can_upload %} {%- if can_upload %}
<div id="bup"> <div id="bup">
<form method="post" enctype="multipart/form-data" accept-charset="utf-8"> <form method="post" enctype="multipart/form-data" accept-charset="utf-8">
@ -27,6 +20,13 @@
</div> </div>
{%- endif %} {%- endif %}
<h1 id="path">
{%- for n in vpnodes[:-1] %}
<a href="{{ n[0] }}">{{ n[1] }}</a>
{%- endfor %}
<span>{{ vpnodes[-1][1] }}</span>
</h1>
<table id="files"> <table id="files">
<thead> <thead>
<tr> <tr>

31
copyparty/web/msg.css Normal file
View file

@ -0,0 +1,31 @@
html,body,tr,th,td,#files,a {
color: inherit;
background: none;
font-weight: inherit;
font-size: inherit;
padding: none;
border: none;
}
html {
color: #ccc;
background: #333;
font-family: sans-serif;
text-shadow: 1px 1px 0px #000;
}
html, body {
margin: 0;
padding: 0;
}
body {
padding-bottom: 5em;
}
#box {
padding: .5em 1em;
background: #2c2c2c;
}
pre {
font-family: monospace, monospace;
}
a {
color: #fc5;
}

45
copyparty/web/msg.html Normal file
View file

@ -0,0 +1,45 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>copyparty</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=0.8">
<link rel="stylesheet" type="text/css" media="screen" href="/.cpr/msg.css">
</head>
<body>
<div id="box">
{%- if h1 %}
<h1>{{ h1 }}</h1>
{%- endif %}
{%- if h2 %}
<h2>{{ h2 }}</h2>
{%- endif %}
{%- if p %}
<p>{{ p }}</p>
{%- endif %}
{%- if pre %}
<pre>{{ pre }}</pre>
{%- endif %}
{%- if html %}
{{ html }}
{%- endif %}
</div>
{%- if redir %}
<script>
setTimeout(function() {
window.location.replace("{{ redir }}");
}, 500);
</script>
{%- endif %}
</body>
</html>

View file

@ -35,6 +35,12 @@ para() { for s in 1 2 3 4 5 6 7 8 12 16 24 32 48 64; do echo $s; for r in {1..4}
avg() { awk 'function pr(ncsz) {if (nsmp>0) {printf "%3s %s\n", csz, sum/nsmp} csz=$1;sum=0;nsmp=0} {sub(/\r$/,"")} /^[0-9]+$/ {pr($1);next} / MiB/ {sub(/ MiB.*/,"");sub(/.* /,"");sum+=$1;nsmp++} END {pr(0)}' "$1"; } avg() { awk 'function pr(ncsz) {if (nsmp>0) {printf "%3s %s\n", csz, sum/nsmp} csz=$1;sum=0;nsmp=0} {sub(/\r$/,"")} /^[0-9]+$/ {pr($1);next} / MiB/ {sub(/ MiB.*/,"");sub(/.* /,"");sum+=$1;nsmp++} END {pr(0)}' "$1"; }
##
## bad filenames
echo hi > 'qwe,rty;asd fgh+jkl%zxc&vbn <qwe>"rty'"'"'uio&asd&nbsp;fgh'.html
## ##
## vscode ## vscode