diff --git a/README.md b/README.md
index 12c0dd9c..aa7c2956 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py
index 5c47a866..b460d519 100644
--- a/copyparty/httpcli.py
+++ b/copyparty/httpcli.py
@@ -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'
{}
'.format(msg)
- html += ''
+ html = self.conn.tpl_msg.render(h1=msg, h2='ack', 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='return to /{}'.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:
diff --git a/copyparty/httpconn.py b/copyparty/httpconn.py
index f7dec680..ab87db70 100644
--- a/copyparty/httpconn.py
+++ b/copyparty/httpconn.py
@@ -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:
diff --git a/copyparty/web/browser.css b/copyparty/web/browser.css
index 407cf270..140646d4 100644
--- a/copyparty/web/browser.css
+++ b/copyparty/web/browser.css
@@ -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;
}
diff --git a/copyparty/web/browser.html b/copyparty/web/browser.html
index a8687092..8107d019 100644
--- a/copyparty/web/browser.html
+++ b/copyparty/web/browser.html
@@ -10,13 +10,6 @@
-
- {%- for n in vpnodes[:-1] %}
- {{ n[1] }}
- {%- endfor %}
- {{ vpnodes[-1][1] }}
-
-
{%- if can_upload %}
{%- endif %}
+
+ {%- for n in vpnodes[:-1] %}
+ {{ n[1] }}
+ {%- endfor %}
+ {{ vpnodes[-1][1] }}
+
+
diff --git a/copyparty/web/msg.css b/copyparty/web/msg.css
new file mode 100644
index 00000000..47eb5218
--- /dev/null
+++ b/copyparty/web/msg.css
@@ -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;
+}
\ No newline at end of file
diff --git a/copyparty/web/msg.html b/copyparty/web/msg.html
new file mode 100644
index 00000000..3bb8edeb
--- /dev/null
+++ b/copyparty/web/msg.html
@@ -0,0 +1,45 @@
+
+
+
+
+
+ copyparty
+
+
+
+
+
+
+
+
+ {%- if h1 %}
+
{{ h1 }}
+ {%- endif %}
+
+ {%- if h2 %}
+
{{ h2 }}
+ {%- endif %}
+
+ {%- if p %}
+
{{ p }}
+ {%- endif %}
+
+ {%- if pre %}
+
{{ pre }}
+ {%- endif %}
+
+ {%- if html %}
+ {{ html }}
+ {%- endif %}
+
+
+ {%- if redir %}
+
+ {%- endif %}
+
+
+
\ No newline at end of file
diff --git a/docs/notes.sh b/docs/notes.sh
index 1f821af3..94e03b89 100644
--- a/docs/notes.sh
+++ b/docs/notes.sh
@@ -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 "rty'"'"'uio&asd fgh'.html
+
+
##
## vscode