support location-based rproxy

This commit is contained in:
ed 2022-12-10 23:43:31 +00:00
parent 02ad4bfab2
commit db194ab519
21 changed files with 147 additions and 86 deletions

View file

@ -673,6 +673,7 @@ def run_argparse(
ap2.add_argument("-p", metavar="PORT", type=u, default="3923", help="ports to bind (comma/range)") ap2.add_argument("-p", metavar="PORT", type=u, default="3923", help="ports to bind (comma/range)")
ap2.add_argument("--ll", action="store_true", help="include link-local IPv4/IPv6 even if the NIC has routable IPs (breaks some mdns clients)") ap2.add_argument("--ll", action="store_true", help="include link-local IPv4/IPv6 even if the NIC has routable IPs (breaks some mdns clients)")
ap2.add_argument("--rproxy", metavar="DEPTH", type=int, default=1, help="which ip to keep; [\033[32m0\033[0m]=tcp, [\033[32m1\033[0m]=origin (first x-fwd), [\033[32m2\033[0m]=cloudflare, [\033[32m3\033[0m]=nginx, [\033[32m-1\033[0m]=closest proxy") ap2.add_argument("--rproxy", metavar="DEPTH", type=int, default=1, help="which ip to keep; [\033[32m0\033[0m]=tcp, [\033[32m1\033[0m]=origin (first x-fwd), [\033[32m2\033[0m]=cloudflare, [\033[32m3\033[0m]=nginx, [\033[32m-1\033[0m]=closest proxy")
ap2.add_argument("--webroot", metavar="PATH", type=u, default="", help="if reverse-proxying on a location instead of a dedicated subdomain, provide the location here")
if ANYWIN: if ANYWIN:
ap2.add_argument("--reuseaddr", action="store_true", help="set reuseaddr on listening sockets on windows; allows rapid restart of copyparty at the expense of being able to accidentally start multiple instances") ap2.add_argument("--reuseaddr", action="store_true", help="set reuseaddr on listening sockets on windows; allows rapid restart of copyparty at the expense of being able to accidentally start multiple instances")
ap2.add_argument("--s-wr-sz", metavar="B", type=int, default=256*1024, help="socket write size in bytes") ap2.add_argument("--s-wr-sz", metavar="B", type=int, default=256*1024, help="socket write size in bytes")

View file

@ -119,6 +119,8 @@ class HttpCli(object):
# placeholders; assigned by run() # placeholders; assigned by run()
self.keepalive = False self.keepalive = False
self.is_https = False self.is_https = False
self.is_proxied = False
self.is_vproxied = False
self.in_hdr_recv = True self.in_hdr_recv = True
self.headers: dict[str, str] = {} self.headers: dict[str, str] = {}
self.mode = " " self.mode = " "
@ -191,6 +193,7 @@ class HttpCli(object):
def j2s(self, name: str, **ka: Any) -> str: def j2s(self, name: str, **ka: Any) -> str:
tpl = self.conn.hsrv.j2[name] tpl = self.conn.hsrv.j2[name]
ka["r"] = self.args.SR if self.is_vproxied else ""
ka["ts"] = self.conn.hsrv.cachebuster() ka["ts"] = self.conn.hsrv.cachebuster()
ka["lang"] = self.args.lang ka["lang"] = self.args.lang
ka["favico"] = self.args.favico ka["favico"] = self.args.favico
@ -276,6 +279,8 @@ class HttpCli(object):
self.log(t.format(self.args.rproxy, zso), c=3) self.log(t.format(self.args.rproxy, zso), c=3)
self.log_src = self.conn.set_rproxy(self.ip) self.log_src = self.conn.set_rproxy(self.ip)
self.is_vproxied = bool(self.args.R)
self.is_proxied = True
if self.is_banned(): if self.is_banned():
return False return False
@ -320,6 +325,13 @@ class HttpCli(object):
else: else:
uparam[k.lower()] = "" uparam[k.lower()] = ""
if self.is_vproxied:
if vpath.startswith(self.args.R):
vpath = vpath[len(self.args.R) + 1 :]
else:
t = "incorrect --webroot or webserver config; expected vpath starting with [{}] but got [{}]"
self.log(t.format(self.args.R, vpath), 1)
self.ouparam = {k: zs for k, zs in uparam.items()} self.ouparam = {k: zs for k, zs in uparam.items()}
if self.args.rsp_slp: if self.args.rsp_slp:
@ -604,10 +616,11 @@ class HttpCli(object):
status: int = 200, status: int = 200,
use302: bool = False, use302: bool = False,
) -> bool: ) -> bool:
vp = self.args.RS + vpath
html = self.j2s( html = self.j2s(
"msg", "msg",
h2='<a href="/{}">{} /{}</a>'.format( h2='<a href="/{}">{} /{}</a>'.format(
quotep(vpath) + suf, flavor, html_escape(vpath, crlf=True) + suf quotep(vp) + suf, flavor, html_escape(vp, crlf=True) + suf
), ),
pre=msg, pre=msg,
click=click, click=click,
@ -1375,7 +1388,7 @@ class HttpCli(object):
url = "{}://{}/{}".format( url = "{}://{}/{}".format(
"https" if self.is_https else "http", "https" if self.is_https else "http",
self.headers.get("host") or "{}:{}".format(*list(self.s.getsockname()[:2])), self.headers.get("host") or "{}:{}".format(*list(self.s.getsockname()[:2])),
vpath + vsuf, self.args.RS + vpath + vsuf,
) )
return post_sz, sha_hex, sha_b64, remains, path, url return post_sz, sha_hex, sha_b64, remains, path, url
@ -1576,6 +1589,10 @@ class HttpCli(object):
x = self.conn.hsrv.broker.ask("up2k.handle_json", body) x = self.conn.hsrv.broker.ask("up2k.handle_json", body)
ret = x.get() ret = x.get()
if self.is_vproxied:
if "purl" in ret:
ret["purl"] = self.args.SR + ret["purl"]
ret = json.dumps(ret) ret = json.dumps(ret)
self.log(ret) self.log(ret)
self.reply(ret.encode("utf-8"), mime="application/json") self.reply(ret.encode("utf-8"), mime="application/json")
@ -1634,6 +1651,10 @@ class HttpCli(object):
if t not in order: if t not in order:
order.append(t) order.append(t)
if self.is_vproxied:
for hit in hits:
hit["rp"] = self.args.RS + hit["rp"]
r = json.dumps({"hits": hits, "tag_order": order}).encode("utf-8") r = json.dumps({"hits": hits, "tag_order": order}).encode("utf-8")
self.reply(r, mime="application/json") self.reply(r, mime="application/json")
return True return True
@ -2032,7 +2053,7 @@ class HttpCli(object):
)[: vfs.flags["fk"]] )[: vfs.flags["fk"]]
vpath = "{}/{}".format(upload_vpath, lfn).strip("/") vpath = "{}/{}".format(upload_vpath, lfn).strip("/")
rel_url = quotep(vpath) + vsuf rel_url = quotep(self.args.RS + vpath) + vsuf
msg += 'sha512: {} // {} // {} bytes // <a href="/{}">{}</a> {}\n'.format( msg += 'sha512: {} // {} // {} bytes // <a href="/{}">{}</a> {}\n'.format(
sha_hex[:56], sha_hex[:56],
sha_b64, sha_b64,
@ -2537,6 +2558,7 @@ class HttpCli(object):
boundary = "\roll\tide" boundary = "\roll\tide"
targs = { targs = {
"r": self.args.SR if self.is_vproxied else "",
"ts": self.conn.hsrv.cachebuster(), "ts": self.conn.hsrv.cachebuster(),
"svcname": self.args.doctitle, "svcname": self.args.doctitle,
"html_head": self.html_head, "html_head": self.html_head,
@ -2765,6 +2787,11 @@ class HttpCli(object):
dst = dst[len(top) + 1 :] dst = dst[len(top) + 1 :]
ret = self.gen_tree(top, dst) ret = self.gen_tree(top, dst)
if self.is_vproxied:
parents = self.args.R.split("/")
for parent in parents[::-1]:
ret = {"k{}".format(parent): ret, "a": []}
zs = json.dumps(ret) zs = json.dumps(ret)
self.reply(zs.encode("utf-8"), mime="application/json") self.reply(zs.encode("utf-8"), mime="application/json")
return True return True
@ -2875,6 +2902,11 @@ class HttpCli(object):
break break
ret = ret[:2000] ret = ret[:2000]
if self.is_vproxied:
for v in ret:
v["vp"] = self.args.SR + v["vp"]
jtxt = json.dumps(ret, indent=2, sort_keys=True).encode("utf-8", "replace") jtxt = json.dumps(ret, indent=2, sort_keys=True).encode("utf-8", "replace")
self.log("{} #{} {:.2f}sec".format(lm, len(ret), time.time() - t0)) self.log("{} #{} {:.2f}sec".format(lm, len(ret), time.time() - t0))
self.reply(jtxt, mime="application/json") self.reply(jtxt, mime="application/json")
@ -2889,6 +2921,8 @@ class HttpCli(object):
if not req: if not req:
req = [self.vpath] req = [self.vpath]
elif self.is_vproxied:
req = [x[len(self.args.SR) :] for x in req]
nlim = int(self.uparam.get("lim") or 0) nlim = int(self.uparam.get("lim") or 0)
lim = [nlim, nlim] if nlim else [] lim = [nlim, nlim] if nlim else []
@ -2900,6 +2934,10 @@ class HttpCli(object):
def handle_mv(self) -> bool: def handle_mv(self) -> bool:
# full path of new loc (incl filename) # full path of new loc (incl filename)
dst = self.uparam.get("move") dst = self.uparam.get("move")
if self.is_vproxied and dst and dst.startswith(self.args.SR):
dst = dst[len(self.args.RS) :]
if not dst: if not dst:
raise Pebkac(400, "need dst vpath") raise Pebkac(400, "need dst vpath")

View file

@ -178,11 +178,6 @@ class HttpSrv(object):
def listen(self, sck: socket.socket, nlisteners: int) -> None: def listen(self, sck: socket.socket, nlisteners: int) -> None:
if self.args.j != 1: if self.args.j != 1:
# lost in the pickle; redefine # lost in the pickle; redefine
try:
sck.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
except:
pass
if not ANYWIN or self.args.reuseaddr: if not ANYWIN or self.args.reuseaddr:
sck.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sck.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

View file

@ -1,6 +1,7 @@
# coding: utf-8 # coding: utf-8
from __future__ import print_function, unicode_literals from __future__ import print_function, unicode_literals
import stat
import tarfile import tarfile
from queue import Queue from queue import Queue
@ -79,6 +80,9 @@ class StreamTar(StreamArc):
src = f["ap"] src = f["ap"]
fsi = f["st"] fsi = f["st"]
if stat.S_ISDIR(fsi.st_mode):
return
inf = tarfile.TarInfo(name=name) inf = tarfile.TarInfo(name=name)
inf.mode = fsi.st_mode inf.mode = fsi.st_mode
inf.size = fsi.st_size inf.size = fsi.st_size

View file

@ -301,6 +301,17 @@ class SvcHub(object):
vs = [x for x in vs if x] vs = [x for x in vs if x]
setattr(al, n, vs) setattr(al, n, vs)
R = al.webroot
if "//" in R or ":" in R:
t = "found URL in --webroot; it should be just the location, for example /foo/bar"
raise Exception(t)
R = R.strip("/")
if R:
al.R = R
al.SR = "/" + R
al.RS = R + "/"
return True return True
def _setlimits(self) -> None: def _setlimits(self) -> None:

View file

@ -3,6 +3,7 @@ from __future__ import print_function, unicode_literals
import calendar import calendar
import time import time
import stat
import zlib import zlib
from .bos import bos from .bos import bos
@ -238,6 +239,9 @@ class StreamZip(StreamArc):
src = f["ap"] src = f["ap"]
st = f["st"] st = f["st"]
if stat.S_ISDIR(st.st_mode):
return
sz = st.st_size sz = st.st_size
ts = st.st_mtime ts = st.st_mtime

View file

@ -209,10 +209,6 @@ class TcpSrv(object):
def _listen(self, ip: str, port: int) -> None: def _listen(self, ip: str, port: int) -> None:
ipv = socket.AF_INET6 if ":" in ip else socket.AF_INET ipv = socket.AF_INET6 if ":" in ip else socket.AF_INET
srv = socket.socket(ipv, socket.SOCK_STREAM) srv = socket.socket(ipv, socket.SOCK_STREAM)
try:
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
except:
pass
if not ANYWIN or self.args.reuseaddr: if not ANYWIN or self.args.reuseaddr:
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

View file

@ -1075,18 +1075,18 @@ html.y #widget.open {
top: -.12em; top: -.12em;
} }
#wtico { #wtico {
cursor: url(/.cpr/dd/4.png), pointer; cursor: url(dd/4.png), pointer;
animation: cursor 500ms; animation: cursor 500ms;
} }
#wtico:hover { #wtico:hover {
animation: cursor 500ms infinite; animation: cursor 500ms infinite;
} }
@keyframes cursor { @keyframes cursor {
0% {cursor: url(/.cpr/dd/2.png), pointer} 0% {cursor: url(dd/2.png), pointer}
30% {cursor: url(/.cpr/dd/3.png), pointer} 30% {cursor: url(dd/3.png), pointer}
50% {cursor: url(/.cpr/dd/4.png), pointer} 50% {cursor: url(dd/4.png), pointer}
75% {cursor: url(/.cpr/dd/5.png), pointer} 75% {cursor: url(dd/5.png), pointer}
85% {cursor: url(/.cpr/dd/4.png), pointer} 85% {cursor: url(dd/4.png), pointer}
} }
@keyframes spin { @keyframes spin {
100% {transform: rotate(360deg)} 100% {transform: rotate(360deg)}

View file

@ -8,8 +8,8 @@
<meta name="viewport" content="width=device-width, initial-scale=0.8, minimum-scale=0.6"> <meta name="viewport" content="width=device-width, initial-scale=0.8, minimum-scale=0.6">
<meta name="theme-color" content="#333"> <meta name="theme-color" content="#333">
{{ html_head }} {{ html_head }}
<link rel="stylesheet" media="screen" href="/.cpr/ui.css?_={{ ts }}"> <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
<link rel="stylesheet" media="screen" href="/.cpr/browser.css?_={{ ts }}"> <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/browser.css?_={{ ts }}">
{%- if css %} {%- if css %}
<link rel="stylesheet" media="screen" href="{{ css }}?_={{ ts }}"> <link rel="stylesheet" media="screen" href="{{ css }}?_={{ ts }}">
{%- endif %} {%- endif %}
@ -71,7 +71,7 @@
<h1 id="path"> <h1 id="path">
<a href="#" id="entree">🌲</a> <a href="#" id="entree">🌲</a>
{%- for n in vpnodes %} {%- for n in vpnodes %}
<a href="/{{ n[0] }}">{{ n[1] }}</a> <a href="{{ r }}/{{ n[0] }}">{{ n[1] }}</a>
{%- endfor %} {%- endfor %}
</h1> </h1>
@ -121,7 +121,7 @@
<div id="epi" class="logue">{{ logues[1] }}</div> <div id="epi" class="logue">{{ logues[1] }}</div>
<h2><a href="/?h" id="goh">control-panel</a></h2> <h2><a href="{{ r }}/?h" id="goh">control-panel</a></h2>
<a href="#" id="repl">π</a> <a href="#" id="repl">π</a>
@ -134,7 +134,8 @@
<div id="widget"></div> <div id="widget"></div>
<script> <script>
var acct = "{{ acct }}", var SR = {{ r|tojson }},
acct = "{{ acct }}",
perms = {{ perms }}, perms = {{ perms }},
themes = {{ themes }}, themes = {{ themes }},
dtheme = "{{ dtheme }}", dtheme = "{{ dtheme }}",
@ -160,10 +161,10 @@
document.documentElement.className = localStorage.theme || dtheme; document.documentElement.className = localStorage.theme || dtheme;
</script> </script>
<script src="/.cpr/util.js?_={{ ts }}"></script> <script src="{{ r }}/.cpr/util.js?_={{ ts }}"></script>
<script src="/.cpr/baguettebox.js?_={{ ts }}"></script> <script src="{{ r }}/.cpr/baguettebox.js?_={{ ts }}"></script>
<script src="/.cpr/browser.js?_={{ ts }}"></script> <script src="{{ r }}/.cpr/browser.js?_={{ ts }}"></script>
<script src="/.cpr/up2k.js?_={{ ts }}"></script> <script src="{{ r }}/.cpr/up2k.js?_={{ ts }}"></script>
{%- if js %} {%- if js %}
<script src="{{ js }}?_={{ ts }}"></script> <script src="{{ js }}?_={{ ts }}"></script>
{%- endif %} {%- endif %}

View file

@ -3647,7 +3647,7 @@ var showfile = (function () {
qsr('#prism_css'); qsr('#prism_css');
var el = mknod('link', 'prism_css'); var el = mknod('link', 'prism_css');
el.rel = 'stylesheet'; el.rel = 'stylesheet';
el.href = '/.cpr/deps/prism' + (light ? '' : 'd') + '.css'; el.href = SR + '/.cpr/deps/prism' + (light ? '' : 'd') + '.css';
document.head.appendChild(el); document.head.appendChild(el);
}; };
@ -3775,7 +3775,7 @@ var showfile = (function () {
if (!defer) if (!defer)
fun(el.firstChild); fun(el.firstChild);
else else
import_js('/.cpr/deps/prism.js', function () { fun(); }); import_js(SR + '/.cpr/deps/prism.js', function () { fun(); });
} }
} }
@ -4181,10 +4181,10 @@ var thegrid = (function () {
if (r.thumbs) { if (r.thumbs) {
ihref += '?th=' + (have_webp ? 'w' : 'j'); ihref += '?th=' + (have_webp ? 'w' : 'j');
if (href == "#") if (href == "#")
ihref = '/.cpr/ico/⏏️'; ihref = SR + '/.cpr/ico/⏏️';
} }
else if (isdir) { else if (isdir) {
ihref = '/.cpr/ico/folder'; ihref = SR + '/.cpr/ico/folder';
} }
else { else {
var ar = href.split('.'); var ar = href.split('.');
@ -4209,7 +4209,7 @@ var thegrid = (function () {
else else
ext = "unk"; ext = "unk";
} }
ihref = '/.cpr/ico/' + ext; ihref = SR + '/.cpr/ico/' + ext;
} }
ihref += (ihref.indexOf('?') > 0 ? '&' : '?') + 'cache=i'; ihref += (ihref.indexOf('?') > 0 ? '&' : '?') + 'cache=i';
@ -4784,7 +4784,7 @@ document.onkeydown = function (e) {
clearTimeout(search_timeout); clearTimeout(search_timeout);
var xhr = new XHR(); var xhr = new XHR();
xhr.open('POST', '/?srch', true); xhr.open('POST', SR + '/?srch', true);
xhr.setRequestHeader('Content-Type', 'text/plain'); xhr.setRequestHeader('Content-Type', 'text/plain');
xhr.onload = xhr.onerror = xhr_search_results; xhr.onload = xhr.onerror = xhr_search_results;
xhr.ts = Date.now(); xhr.ts = Date.now();
@ -5323,7 +5323,12 @@ var treectl = (function () {
treegrow.call(this.previousSibling, e); treegrow.call(this.previousSibling, e);
return; return;
} }
r.reqls(this.getAttribute('href'), true); var href = this.getAttribute('href');
if (R && !href.startsWith(SR)) {
window.location = href;
return;
}
r.reqls(href, true);
r.dir_cb = tree_scrollto; r.dir_cb = tree_scrollto;
thegrid.setvis(true); thegrid.setvis(true);
} }
@ -5547,7 +5552,7 @@ var treectl = (function () {
qsr('#bbsw'); qsr('#bbsw');
if (ls0 === null) { if (ls0 === null) {
var xhr = new XHR(); var xhr = new XHR();
xhr.open('GET', '/?am_js', true); xhr.open('GET', SR + '/?am_js', true);
xhr.send(); xhr.send();
r.ls_cb = showfile.addlinks; r.ls_cb = showfile.addlinks;
@ -6552,7 +6557,7 @@ function show_md(md, name, div, url, depth) {
if (depth) if (depth)
return toast.warn(10, errmsg + 'failed to load marked.js') return toast.warn(10, errmsg + 'failed to load marked.js')
return import_js('/.cpr/deps/marked.js', function () { return import_js(SR + '/.cpr/deps/marked.js', function () {
show_md(md, name, div, url, 1); show_md(md, name, div, url, 1);
}); });
} }
@ -6705,7 +6710,7 @@ var unpost = (function () {
r.me = me; r.me = me;
} }
var q = '/?ups'; var q = SR + '/?ups';
if (filt.value) if (filt.value)
q += '&filter=' + uricom_enc(filt.value, true); q += '&filter=' + uricom_enc(filt.value, true);
@ -6771,7 +6776,7 @@ var unpost = (function () {
var xhr = new XHR(); var xhr = new XHR();
xhr.n = n; xhr.n = n;
xhr.n2 = n2; xhr.n2 = n2;
xhr.open('POST', '/?delete&lim=' + req.length, true); xhr.open('POST', SR + '/?delete&lim=' + req.length, true);
xhr.onload = xhr.onerror = unpost_delete_cb; xhr.onload = xhr.onerror = unpost_delete_cb;
xhr.send(JSON.stringify(req)); xhr.send(JSON.stringify(req));
}; };

View file

@ -57,7 +57,7 @@
<div>{{ logues[1] }}</div><br /> <div>{{ logues[1] }}</div><br />
{%- endif %} {%- endif %}
<h2><a href="/{{ url_suf }}{{ url_suf and '&amp;' or '?' }}h">control-panel</a></h2> <h2><a href="{{ r }}/{{ url_suf }}{{ url_suf and '&amp;' or '?' }}h">control-panel</a></h2>
</body> </body>
</html> </html>

View file

@ -5,10 +5,10 @@
<meta name="viewport" content="width=device-width, initial-scale=0.7"> <meta name="viewport" content="width=device-width, initial-scale=0.7">
<meta name="theme-color" content="#333"> <meta name="theme-color" content="#333">
{{ html_head }} {{ html_head }}
<link rel="stylesheet" href="/.cpr/ui.css?_={{ ts }}"> <link rel="stylesheet" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
<link rel="stylesheet" href="/.cpr/md.css?_={{ ts }}"> <link rel="stylesheet" href="{{ r }}/.cpr/md.css?_={{ ts }}">
{%- if edit %} {%- if edit %}
<link rel="stylesheet" href="/.cpr/md2.css?_={{ ts }}"> <link rel="stylesheet" href="{{ r }}/.cpr/md2.css?_={{ ts }}">
{%- endif %} {%- endif %}
</head> </head>
<body> <body>
@ -128,7 +128,8 @@ write markdown (most html is 🙆 too)
<script> <script>
var last_modified = {{ lastmod }}, var SR = {{ r|tojson }},
last_modified = {{ lastmod }},
have_emp = {{ have_emp|tojson }}, have_emp = {{ have_emp|tojson }},
dfavico = "{{ favico }}"; dfavico = "{{ favico }}";
@ -153,10 +154,10 @@ l.light = drk? 0:1;
})(); })();
</script> </script>
<script src="/.cpr/util.js?_={{ ts }}"></script> <script src="{{ r }}/.cpr/util.js?_={{ ts }}"></script>
<script src="/.cpr/deps/marked.js?_={{ ts }}"></script> <script src="{{ r }}/.cpr/deps/marked.js?_={{ ts }}"></script>
<script src="/.cpr/md.js?_={{ ts }}"></script> <script src="{{ r }}/.cpr/md.js?_={{ ts }}"></script>
{%- if edit %} {%- if edit %}
<script src="/.cpr/md2.js?_={{ ts }}"></script> <script src="{{ r }}/.cpr/md2.js?_={{ ts }}"></script>
{%- endif %} {%- endif %}
</body></html> </body></html>

View file

@ -5,10 +5,10 @@
<meta name="viewport" content="width=device-width, initial-scale=0.7"> <meta name="viewport" content="width=device-width, initial-scale=0.7">
<meta name="theme-color" content="#333"> <meta name="theme-color" content="#333">
{{ html_head }} {{ html_head }}
<link rel="stylesheet" href="/.cpr/ui.css?_={{ ts }}"> <link rel="stylesheet" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
<link rel="stylesheet" href="/.cpr/mde.css?_={{ ts }}"> <link rel="stylesheet" href="{{ r }}/.cpr/mde.css?_={{ ts }}">
<link rel="stylesheet" href="/.cpr/deps/mini-fa.css?_={{ ts }}"> <link rel="stylesheet" href="{{ r }}/.cpr/deps/mini-fa.css?_={{ ts }}">
<link rel="stylesheet" href="/.cpr/deps/easymde.css?_={{ ts }}"> <link rel="stylesheet" href="{{ r }}/.cpr/deps/easymde.css?_={{ ts }}">
</head> </head>
<body> <body>
<div id="mw"> <div id="mw">
@ -26,7 +26,8 @@
<a href="#" id="repl">π</a> <a href="#" id="repl">π</a>
<script> <script>
var last_modified = {{ lastmod }}, var SR = {{ r|tojson }},
last_modified = {{ lastmod }},
have_emp = {{ have_emp|tojson }}, have_emp = {{ have_emp|tojson }},
dfavico = "{{ favico }}"; dfavico = "{{ favico }}";
@ -48,8 +49,8 @@ l.light = drk? 0:1;
})(); })();
</script> </script>
<script src="/.cpr/util.js?_={{ ts }}"></script> <script src="{{ r }}/.cpr/util.js?_={{ ts }}"></script>
<script src="/.cpr/deps/marked.js?_={{ ts }}"></script> <script src="{{ r }}/.cpr/deps/marked.js?_={{ ts }}"></script>
<script src="/.cpr/deps/easymde.js?_={{ ts }}"></script> <script src="{{ r }}/.cpr/deps/easymde.js?_={{ ts }}"></script>
<script src="/.cpr/mde.js?_={{ ts }}"></script> <script src="{{ r }}/.cpr/mde.js?_={{ ts }}"></script>
</body></html> </body></html>

View file

@ -8,7 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=0.8"> <meta name="viewport" content="width=device-width, initial-scale=0.8">
<meta name="theme-color" content="#333"> <meta name="theme-color" content="#333">
{{ html_head }} {{ html_head }}
<link rel="stylesheet" media="screen" href="/.cpr/msg.css?_={{ ts }}"> <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/msg.css?_={{ ts }}">
</head> </head>
<body> <body>

View file

@ -8,19 +8,19 @@
<meta name="viewport" content="width=device-width, initial-scale=0.8"> <meta name="viewport" content="width=device-width, initial-scale=0.8">
<meta name="theme-color" content="#333"> <meta name="theme-color" content="#333">
{{ html_head }} {{ html_head }}
<link rel="stylesheet" media="screen" href="/.cpr/splash.css?_={{ ts }}"> <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/splash.css?_={{ ts }}">
<link rel="stylesheet" media="screen" href="/.cpr/ui.css?_={{ ts }}"> <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
</head> </head>
<body> <body>
<div id="wrap"> <div id="wrap">
<a id="a" href="/?h" class="af">refresh</a> <a id="a" href="{{ r }}/?h" class="af">refresh</a>
<a id="v" href="/?hc" class="af">connect</a> <a id="v" href="{{ r }}/?hc" class="af">connect</a>
{%- if this.uname == '*' %} {%- if this.uname == '*' %}
<p id="b">howdy stranger &nbsp; <small>(you're not logged in)</small></p> <p id="b">howdy stranger &nbsp; <small>(you're not logged in)</small></p>
{%- else %} {%- else %}
<a id="c" href="/?pw=x" class="logout">logout</a> <a id="c" href="{{ r }}/?pw=x" class="logout">logout</a>
<p><span id="m">welcome back,</span> <strong>{{ this.uname }}</strong></p> <p><span id="m">welcome back,</span> <strong>{{ this.uname }}</strong></p>
{%- endif %} {%- endif %}
@ -53,8 +53,8 @@
</table> </table>
</td></tr></table> </td></tr></table>
<div class="btns"> <div class="btns">
<a id="d" href="/?stack">dump stack</a> <a id="d" href="{{ r }}/?stack">dump stack</a>
<a id="e" href="/?reload=cfg">reload cfg</a> <a id="e" href="{{ r }}/?reload=cfg">reload cfg</a>
</div> </div>
{%- endif %} {%- endif %}
@ -79,18 +79,18 @@
<h1 id="cc">client config:</h1> <h1 id="cc">client config:</h1>
<ul> <ul>
{% if k304 %} {% if k304 %}
<li><a id="h" href="/?k304=n">disable k304</a> (currently enabled) <li><a id="h" href="{{ r }}/?k304=n">disable k304</a> (currently enabled)
{%- else %} {%- else %}
<li><a id="i" href="/?k304=y" class="r">enable k304</a> (currently disabled) <li><a id="i" href="{{ r }}/?k304=y" class="r">enable k304</a> (currently disabled)
{% endif %} {% endif %}
<blockquote id="j">enabling this will disconnect your client on every HTTP 304, which can prevent some buggy proxies from getting stuck (suddenly not loading pages), <em>but</em> it will also make things slower in general</blockquote></li> <blockquote id="j">enabling this will disconnect your client on every HTTP 304, which can prevent some buggy proxies from getting stuck (suddenly not loading pages), <em>but</em> it will also make things slower in general</blockquote></li>
<li><a id="k" href="/?reset" class="r" onclick="localStorage.clear();return true">reset client settings</a></li> <li><a id="k" href="{{ r }}/?reset" class="r" onclick="localStorage.clear();return true">reset client settings</a></li>
</ul> </ul>
<h1 id="l">login for more:</h1> <h1 id="l">login for more:</h1>
<ul> <ul>
<form method="post" enctype="multipart/form-data" action="/{{ qvpath }}"> <form method="post" enctype="multipart/form-data" action="{{ r }}/{{ qvpath }}">
<input type="hidden" name="act" value="login" /> <input type="hidden" name="act" value="login" />
<input type="password" name="cppwd" /> <input type="password" name="cppwd" />
<input type="submit" value="Login" /> <input type="submit" value="Login" />
@ -100,13 +100,14 @@
<a href="#" id="repl">π</a> <a href="#" id="repl">π</a>
<script> <script>
var lang="{{ lang }}", var SR = {{ r|tojson }},
lang="{{ lang }}",
dfavico="{{ favico }}"; dfavico="{{ favico }}";
document.documentElement.className=localStorage.theme||"{{ this.args.theme }}"; document.documentElement.className=localStorage.theme||"{{ this.args.theme }}";
</script> </script>
<script src="/.cpr/util.js?_={{ ts }}"></script> <script src="{{ r }}/.cpr/util.js?_={{ ts }}"></script>
<script src="/.cpr/splash.js?_={{ ts }}"></script> <script src="{{ r }}/.cpr/splash.js?_={{ ts }}"></script>
</body> </body>
</html> </html>

View file

@ -8,14 +8,14 @@
<meta name="viewport" content="width=device-width, initial-scale=0.8"> <meta name="viewport" content="width=device-width, initial-scale=0.8">
<meta name="theme-color" content="#333"> <meta name="theme-color" content="#333">
{{ html_head }} {{ html_head }}
<link rel="stylesheet" media="screen" href="/.cpr/splash.css?_={{ ts }}"> <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/splash.css?_={{ ts }}">
<link rel="stylesheet" media="screen" href="/.cpr/ui.css?_={{ ts }}"> <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
</head> </head>
<body> <body>
<div id="wrap" class="w"> <div id="wrap" class="w">
<div class="cn"> <div class="cn">
<p class="btns"><a href="/{{ vp }}">browse files</a> // <a href="/?h">control panel</a></p> <p class="btns"><a href="{{ r }}/{{ vp }}">browse files</a> // <a href="{{ r }}/?h">control panel</a></p>
<p>or choose your OS for cooler alternatives:</p> <p>or choose your OS for cooler alternatives:</p>
<div class="ossel"> <div class="ossel">
<a id="swin" href="#">Windows</a> <a id="swin" href="#">Windows</a>
@ -53,7 +53,7 @@
<p><em>note: if you are on LAN (or just dont have valid certificates), add <code>--no-check-certificate</code> to the mount command</em><br />---</p> <p><em>note: if you are on LAN (or just dont have valid certificates), add <code>--no-check-certificate</code> to the mount command</em><br />---</p>
{% endif %} {% endif %}
<p>if you want to use the native WebDAV client in windows instead (slow and buggy), first run <a href="/.cpr/a/webdav-cfg.bat">webdav-cfg.bat</a> to remove the 47 MiB filesize limit (also fixes latency and password login), then connect:</p> <p>if you want to use the native WebDAV client in windows instead (slow and buggy), first run <a href="{{ r }}/.cpr/a/webdav-cfg.bat">webdav-cfg.bat</a> to remove the 47 MiB filesize limit (also fixes latency and password login), then connect:</p>
<pre> <pre>
net use <b>w:</b> http{{ s }}://{{ ep }}/{{ vp }}{% if accs %} k /user:<b>{{ pw }}</b>{% endif %} net use <b>w:</b> http{{ s }}://{{ ep }}/{{ vp }}{% if accs %} k /user:<b>{{ pw }}</b>{% endif %}
</pre> </pre>
@ -144,7 +144,7 @@
<h1>partyfuse</h1> <h1>partyfuse</h1>
<p> <p>
<a href="/.cpr/a/partyfuse.py">partyfuse.py</a> -- fast, read-only, <a href="{{ r }}/.cpr/a/partyfuse.py">partyfuse.py</a> -- fast, read-only,
<span class="os win">needs <a href="https://winfsp.dev/rel/">winfsp</a></span> <span class="os win">needs <a href="https://winfsp.dev/rel/">winfsp</a></span>
<span class="os lin">doesn't need root</span> <span class="os lin">doesn't need root</span>
</p> </p>
@ -155,7 +155,7 @@
<p><em>note: if you are on LAN (or just dont have valid certificates), add <code>-td</code></em></p> <p><em>note: if you are on LAN (or just dont have valid certificates), add <code>-td</code></em></p>
{% endif %} {% endif %}
<p> <p>
you can use <a href="/.cpr/a/up2k.py">up2k.py</a> to upload (sometimes faster than web-browsers) you can use <a href="{{ r }}/.cpr/a/up2k.py">up2k.py</a> to upload (sometimes faster than web-browsers)
</p> </p>
@ -188,13 +188,14 @@
<a href="#" id="repl">π</a> <a href="#" id="repl">π</a>
<script> <script>
var lang="{{ lang }}", var SR = {{ r|tojson }},
lang="{{ lang }}",
dfavico="{{ favico }}"; dfavico="{{ favico }}";
document.documentElement.className=localStorage.theme||"{{ args.theme }}"; document.documentElement.className=localStorage.theme||"{{ args.theme }}";
</script> </script>
<script src="/.cpr/util.js?_={{ ts }}"></script> <script src="{{ r }}/.cpr/util.js?_={{ ts }}"></script>
<script src="/.cpr/svcs.js?_={{ ts }}"></script> <script src="{{ r }}/.cpr/svcs.js?_={{ ts }}"></script>
</body> </body>
</html> </html>

View file

@ -1,7 +1,7 @@
@font-face { @font-face {
font-family: 'scp'; font-family: 'scp';
font-display: swap; font-display: swap;
src: local('Source Code Pro Regular'), local('SourceCodePro-Regular'), url(/.cpr/deps/scp.woff2) format('woff2'); src: local('Source Code Pro Regular'), local('SourceCodePro-Regular'), url(deps/scp.woff2) format('woff2');
} }
html { html {
touch-action: manipulation; touch-action: manipulation;

View file

@ -779,7 +779,7 @@ function up2k_init(subtle) {
setTimeout(function () { setTimeout(function () {
if (window.WebAssembly && !hws.length) if (window.WebAssembly && !hws.length)
fetch('/.cpr/w.hash.js' + CB); fetch(SR + '/.cpr/w.hash.js' + CB);
}, 1000); }, 1000);
function showmodal(msg) { function showmodal(msg) {
@ -809,7 +809,7 @@ function up2k_init(subtle) {
m = L.u_https1 + ' <a href="' + (window.location + '').replace(':', 's:') + '">' + L.u_https2 + '</a> ' + L.u_https3; m = L.u_https1 + ' <a href="' + (window.location + '').replace(':', 's:') + '">' + L.u_https2 + '</a> ' + L.u_https3;
showmodal('<h1>loading ' + fn + '</h1>'); showmodal('<h1>loading ' + fn + '</h1>');
import_js('/.cpr/deps/' + fn, unmodal); import_js(SR + '/.cpr/deps/' + fn, unmodal);
if (HTTPS) { if (HTTPS) {
// chrome<37 firefox<34 edge<12 opera<24 safari<7 // chrome<37 firefox<34 edge<12 opera<24 safari<7
@ -1312,7 +1312,7 @@ function up2k_init(subtle) {
if (window.WebAssembly && !hws.length) { if (window.WebAssembly && !hws.length) {
for (var a = 0; a < Math.min(navigator.hardwareConcurrency || 4, 16); a++) for (var a = 0; a < Math.min(navigator.hardwareConcurrency || 4, 16); a++)
hws.push(new Worker('/.cpr/w.hash.js' + CB)); hws.push(new Worker(SR + '/.cpr/w.hash.js' + CB));
console.log(hws.length + " hashers"); console.log(hws.length + " hashers");
} }

View file

@ -9,6 +9,8 @@ if (!window.console || !console.log)
var wah = '', var wah = '',
L, tt, treectl, thegrid, up2k, asmCrypto, hashwasm, vbar, marked, L, tt, treectl, thegrid, up2k, asmCrypto, hashwasm, vbar, marked,
CB = '?_=' + Date.now(), CB = '?_=' + Date.now(),
R = SR.slice(1),
RS = R ? "/" + R : "",
HALFMAX = 8192 * 8192 * 8192 * 8192, HALFMAX = 8192 * 8192 * 8192 * 8192,
HTTPS = (window.location + '').indexOf('https:') === 0, HTTPS = (window.location + '').indexOf('https:') === 0,
TOUCH = 'ontouchstart' in window, TOUCH = 'ontouchstart' in window,

View file

@ -19,7 +19,7 @@ catch (ex) {
} }
function load_fb() { function load_fb() {
subtle = null; subtle = null;
importScripts('/.cpr/deps/sha512.hw.js'); importScripts('deps/sha512.hw.js');
} }

View file

@ -9,7 +9,7 @@ font-family: 'fa';
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
font-display: block; font-display: block;
src: url("/.cpr/deps/mini-fa.woff") format("woff"); src: url("mini-fa.woff") format("woff");
} }
.fa, .fa,