mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
fallback uploader works
This commit is contained in:
parent
1ea9b26a89
commit
7b4b38d98b
|
@ -15,7 +15,7 @@ turn your phone or raspi into a portable file server with resumable uploads/down
|
|||
|
||||
* [x] sanic multipart parser
|
||||
* [x] load balancer (multiprocessing)
|
||||
* [ ] upload (plain multipart, ie6 support)
|
||||
* [x] upload (plain multipart, ie6 support)
|
||||
* [ ] upload (js, resumable, multithreaded)
|
||||
* [x] download
|
||||
* [x] browser
|
||||
|
@ -25,6 +25,8 @@ turn your phone or raspi into a portable file server with resumable uploads/down
|
|||
* [x] volumes
|
||||
* [x] accounts
|
||||
|
||||
summary: it works
|
||||
|
||||
|
||||
# dependencies
|
||||
|
||||
|
@ -64,9 +66,10 @@ pip install black bandit pylint flake8 # vscode tooling
|
|||
```
|
||||
|
||||
|
||||
# TODO
|
||||
# immediate todo
|
||||
|
||||
roughly sorted by priority
|
||||
|
||||
* http error handling (conn.status or handler-retval)
|
||||
* last-modified header
|
||||
* support pillow-simd
|
||||
|
|
|
@ -15,8 +15,10 @@ from .util import * # noqa # pylint: disable=unused-wildcard-import
|
|||
if not PY2:
|
||||
unicode = str
|
||||
from urllib.parse import unquote_plus
|
||||
from urllib.parse import quote_plus
|
||||
else:
|
||||
from urllib import unquote_plus # pylint: disable=no-name-in-module
|
||||
from urllib import quote_plus
|
||||
|
||||
|
||||
class HttpCli(object):
|
||||
|
@ -151,7 +153,7 @@ class HttpCli(object):
|
|||
if self.vpath == "" and not self.uparam:
|
||||
nread = len(self.rvol)
|
||||
nwrite = len(self.wvol)
|
||||
if nread + nwrite == 1:
|
||||
if nread + nwrite == 1 or (self.rvol == self.wvol and nread == 1):
|
||||
if nread == 1:
|
||||
self.vpath = self.rvol[0]
|
||||
else:
|
||||
|
@ -212,19 +214,25 @@ class HttpCli(object):
|
|||
pwd = u"x" # nosec
|
||||
|
||||
h = ["Set-Cookie: cppwd={}; Path=/".format(pwd)]
|
||||
html = u'<h1>{}</h1><h2><a href="/">ack</a></h2>'.format(msg)
|
||||
html += '<script>setTimeout(function(){window.location.replace("/");},500);</script>'
|
||||
html = self.conn.tpl_msg.render(h1=msg, h2='<a href="/">ack</a>', redir="/")
|
||||
self.reply(html.encode("utf-8"), headers=h)
|
||||
|
||||
def handle_plain_upload(self):
|
||||
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 = []
|
||||
t0 = time.time()
|
||||
for nfile, (p_field, p_file, p_data) in enumerate(self.parser.gen):
|
||||
fn = os.devnull
|
||||
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
|
||||
# and provides a new filename if taken
|
||||
if os.path.exists(fn):
|
||||
|
@ -253,7 +261,14 @@ class HttpCli(object):
|
|||
# truncated SHA-512 prevents length extension attacks;
|
||||
# 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:
|
||||
# TODO this is bad
|
||||
|
@ -310,10 +325,15 @@ class HttpCli(object):
|
|||
vpnodes = [[u"/", u"/"]]
|
||||
for node in self.vpath.split("/"):
|
||||
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)
|
||||
abspath = vn.canonical(rem)
|
||||
|
||||
if not os.path.exists(abspath):
|
||||
print(abspath)
|
||||
raise Pebkac("404 not found")
|
||||
|
||||
if not os.path.isdir(abspath):
|
||||
return self.tx_file(abspath)
|
||||
|
||||
|
@ -341,7 +361,13 @@ class HttpCli(object):
|
|||
dt = datetime.utcfromtimestamp(inf.st_mtime)
|
||||
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:
|
||||
dirs.append(item)
|
||||
else:
|
||||
|
|
|
@ -31,6 +31,7 @@ class HttpConn(object):
|
|||
|
||||
self.tpl_mounts = self.load_tpl("splash.html")
|
||||
self.tpl_browser = self.load_tpl("browser.html")
|
||||
self.tpl_msg = self.load_tpl("msg.html")
|
||||
|
||||
def load_tpl(self, fn):
|
||||
with open(self.respath(fn), "rb") as f:
|
||||
|
|
|
@ -251,9 +251,8 @@ a.play.act {
|
|||
}
|
||||
#bup {
|
||||
padding: .5em .5em .5em .3em;
|
||||
margin-bottom: 1em;
|
||||
background: #2d2d2d;
|
||||
border-radius: 0 .5em .5em 0;
|
||||
border-radius: 0 0 1em 0;
|
||||
border-right: .3em solid #3a3a3a;
|
||||
max-width: 40em;
|
||||
}
|
||||
|
|
|
@ -10,13 +10,6 @@
|
|||
</head>
|
||||
|
||||
<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 %}
|
||||
<div id="bup">
|
||||
<form method="post" enctype="multipart/form-data" accept-charset="utf-8">
|
||||
|
@ -27,6 +20,13 @@
|
|||
</div>
|
||||
{%- 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">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
31
copyparty/web/msg.css
Normal file
31
copyparty/web/msg.css
Normal 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
45
copyparty/web/msg.html
Normal 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>
|
|
@ -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"; }
|
||||
|
||||
|
||||
##
|
||||
## bad filenames
|
||||
|
||||
echo hi > 'qwe,rty;asd fgh+jkl%zxc&vbn <qwe>"rty'"'"'uio&asd fgh'.html
|
||||
|
||||
|
||||
##
|
||||
## vscode
|
||||
|
||||
|
|
Loading…
Reference in a new issue