add landing page with mounting instructions

This commit is contained in:
ed 2022-11-26 19:47:27 +00:00
parent e53531a9fb
commit f0e78a6826
22 changed files with 439 additions and 18 deletions

View file

@ -847,6 +847,7 @@ def run_argparse(
ap2.add_argument("--no-scandir", action="store_true", help="disable scandir; instead using listdir + stat on each file") ap2.add_argument("--no-scandir", action="store_true", help="disable scandir; instead using listdir + stat on each file")
ap2.add_argument("--no-fastboot", action="store_true", help="wait for up2k indexing before starting the httpd") ap2.add_argument("--no-fastboot", action="store_true", help="wait for up2k indexing before starting the httpd")
ap2.add_argument("--no-htp", action="store_true", help="disable httpserver threadpool, create threads as-needed instead") ap2.add_argument("--no-htp", action="store_true", help="disable httpserver threadpool, create threads as-needed instead")
ap2.add_argument("--rclone-mdns", action="store_true", help="use mdns-domain instead of server-ip on /?hc")
ap2.add_argument("--stackmon", metavar="P,S", type=u, help="write stacktrace to Path every S second, for example --stackmon=\033[32m./st/%%Y-%%m/%%d/%%H%%M.xz,60") ap2.add_argument("--stackmon", metavar="P,S", type=u, help="write stacktrace to Path every S second, for example --stackmon=\033[32m./st/%%Y-%%m/%%d/%%H%%M.xz,60")
ap2.add_argument("--log-thrs", metavar="SEC", type=float, help="list active threads every SEC") ap2.add_argument("--log-thrs", metavar="SEC", type=float, help="list active threads every SEC")
ap2.add_argument("--log-fk", metavar="REGEX", type=u, default="", help="log filekey params for files where path matches REGEX; [\033[32m.\033[0m] (a single dot) = all files") ap2.add_argument("--log-fk", metavar="REGEX", type=u, default="", help="log filekey params for files where path matches REGEX; [\033[32m.\033[0m] (a single dot) = all files")

View file

@ -113,6 +113,10 @@ class BrokerMp(object):
for p in self.procs: for p in self.procs:
p.q_pend.put((0, dest, [args[0], len(self.procs)])) p.q_pend.put((0, dest, [args[0], len(self.procs)]))
elif dest == "set_netdevs":
for p in self.procs:
p.q_pend.put((0, dest, list(args)))
elif dest == "cb_httpsrv_up": elif dest == "cb_httpsrv_up":
self.hub.cb_httpsrv_up() self.hub.cb_httpsrv_up()

View file

@ -97,6 +97,9 @@ class MpWorker(BrokerCli):
elif dest == "listen": elif dest == "listen":
self.httpsrv.listen(args[0], args[1]) self.httpsrv.listen(args[0], args[1])
elif dest == "set_netdevs":
self.httpsrv.set_netdevs(args[0])
elif dest == "retq": elif dest == "retq":
# response from previous ipc call # response from previous ipc call
with self.retpend_mutex: with self.retpend_mutex:

View file

@ -61,6 +61,10 @@ class BrokerThr(BrokerCli):
self.httpsrv.listen(args[0], 1) self.httpsrv.listen(args[0], 1)
return return
if dest == "set_netdevs":
self.httpsrv.set_netdevs(args[0])
return
# new ipc invoking managed service in hub # new ipc invoking managed service in hub
obj = self.hub obj = self.hub
for node in dest.split("."): for node in dest.split("."):

View file

@ -691,6 +691,9 @@ class HttpCli(object):
if "reset" in self.uparam: if "reset" in self.uparam:
return self.set_cfg_reset() return self.set_cfg_reset()
if "hc" in self.uparam:
return self.tx_svcs()
if "h" in self.uparam: if "h" in self.uparam:
return self.tx_mounts() return self.tx_mounts()
@ -2562,6 +2565,32 @@ class HttpCli(object):
return True return True
def tx_svcs(self) -> bool:
aname = re.sub("[^0-9a-zA-Z]+", "", self.args.name) or "a"
ep = self.headers["host"]
host = ep.split(":")[0]
hport = ep[ep.find(":") :] if ":" in ep else ""
rip = (
host
if self.args.rclone_mdns or not self.args.zm
else self.conn.hsrv.nm.map(self.ip) or host
)
html = self.j2s(
"svcs",
args=self.args,
accs=bool(self.asrv.acct),
s="s" if self.is_https else "",
rip=rip,
ep=ep,
vp=(self.uparam["hc"] or "").lstrip("/"),
host=host,
hport=hport,
aname=aname,
pw=self.pw or "pw",
)
self.reply(html.encode("utf-8"))
return True
def tx_mounts(self) -> bool: def tx_mounts(self) -> bool:
suf = self.urlq({}, ["h"]) suf = self.urlq({}, ["h"])
avol = [x for x in self.wvol if x in self.rvol] avol = [x for x in self.wvol if x in self.rvol]

View file

@ -37,6 +37,7 @@ from .util import (
Daemon, Daemon,
Garda, Garda,
Magician, Magician,
NetMap,
ipnorm, ipnorm,
min_ex, min_ex,
shut_socket, shut_socket,
@ -72,6 +73,7 @@ class HttpSrv(object):
nsuf = "-n{}-i{:x}".format(nid, os.getpid()) if nid else "" nsuf = "-n{}-i{:x}".format(nid, os.getpid()) if nid else ""
self.magician = Magician() self.magician = Magician()
self.nm = NetMap([], {})
self.ssdp: Optional["SSDPr"] = None self.ssdp: Optional["SSDPr"] = None
self.gpwd = Garda(self.args.ban_pw) self.gpwd = Garda(self.args.ban_pw)
self.g404 = Garda(self.args.ban_404) self.g404 = Garda(self.args.ban_404)
@ -102,10 +104,8 @@ class HttpSrv(object):
env = jinja2.Environment() env = jinja2.Environment()
env.loader = jinja2.FileSystemLoader(os.path.join(self.E.mod, "web")) env.loader = jinja2.FileSystemLoader(os.path.join(self.E.mod, "web"))
self.j2 = { jn = ["splash", "svcs", "browser", "browser2", "msg", "md", "mde", "cf"]
x: env.get_template(x + ".html") self.j2 = {x: env.get_template(x + ".html") for x in jn}
for x in ["splash", "browser", "browser2", "msg", "md", "mde", "cf"]
}
zs = os.path.join(self.E.mod, "web", "deps", "prism.js.gz") zs = os.path.join(self.E.mod, "web", "deps", "prism.js.gz")
self.prism = os.path.exists(zs) self.prism = os.path.exists(zs)
@ -140,6 +140,9 @@ class HttpSrv(object):
except: except:
pass pass
def set_netdevs(self, netdevs: dict[str, str]) -> None:
self.nm = NetMap([self.ip], netdevs)
def start_threads(self, n: int) -> None: def start_threads(self, n: int) -> None:
self.tp_nthr += n self.tp_nthr += n
if self.args.log_htp: if self.args.log_htp:

View file

@ -257,7 +257,12 @@ class MDNS(MCast):
self.log("sendto failed: {} ({})".format(srv.ip, ex), "90") self.log("sendto failed: {} ({})".format(srv.ip, ex), "90")
def run(self) -> None: def run(self) -> None:
bound = self.create_servers() try:
bound = self.create_servers()
except:
self.log("no server IP matches the mdns config", 1)
bound = []
if not bound: if not bound:
self.log("failed to announce copyparty services on the network", 3) self.log("failed to announce copyparty services on the network", 3)
return return

View file

@ -20,6 +20,10 @@ if not hasattr(socket, "IPPROTO_IPV6"):
setattr(socket, "IPPROTO_IPV6", 41) setattr(socket, "IPPROTO_IPV6", 41)
class NoIPs(Exception):
pass
class MC_Sck(object): class MC_Sck(object):
"""there is one socket for each server ip""" """there is one socket for each server ip"""
@ -114,7 +118,7 @@ class MCast(object):
ips = [x for x in ips if ":" not in x or x.startswith("fe80")] ips = [x for x in ips if ":" not in x or x.startswith("fe80")]
if not ips: if not ips:
raise Exception("no server IP matches the mdns config") raise NoIPs()
for ip in ips: for ip in ips:
v6 = ":" in ip v6 = ":" in ip
@ -302,8 +306,8 @@ class MCast(object):
t = "could not map client {} to known subnet; maybe forwarded from another network?" t = "could not map client {} to known subnet; maybe forwarded from another network?"
self.log(t.format(cip), 3) self.log(t.format(cip), 3)
self.cscache[cip] = ret
if len(self.cscache) > 9000: if len(self.cscache) > 9000:
self.cscache = {} self.cscache = {}
self.cscache[cip] = ret
return ret return ret

View file

@ -99,7 +99,12 @@ class SSDPd(MCast):
self.log_func("SSDP", msg, c) self.log_func("SSDP", msg, c)
def run(self) -> None: def run(self) -> None:
bound = self.create_servers() try:
bound = self.create_servers()
except:
self.log("no server IP matches the ssdp config", 1)
bound = []
if not bound: if not bound:
self.log("failed to announce copyparty services on the network", 3) self.log("failed to announce copyparty services on the network", 3)
return return

View file

@ -252,6 +252,7 @@ class TcpSrv(object):
self.srv = srvs self.srv = srvs
self.bound = bound self.bound = bound
self.nsrv = len(srvs) self.nsrv = len(srvs)
self.hub.broker.say("set_netdevs", self.netdevs)
def shutdown(self) -> None: def shutdown(self) -> None:
self.stopping = True self.stopping = True

View file

@ -26,10 +26,10 @@ from collections import Counter
from datetime import datetime from datetime import datetime
from email.utils import formatdate from email.utils import formatdate
from ipaddress import IPv6Address from ipaddress import IPv4Address, IPv4Network, IPv6Address, IPv6Network
from queue import Queue from queue import Queue
from .__init__ import ANYWIN, MACOS, PY2, TYPE_CHECKING, VT100, WINDOWS, unicode from .__init__ import ANYWIN, MACOS, PY2, TYPE_CHECKING, VT100, WINDOWS
from .__version__ import S_BUILD_DT, S_VERSION from .__version__ import S_BUILD_DT, S_VERSION
from .stolen import surrogateescape from .stolen import surrogateescape
@ -95,6 +95,8 @@ if TYPE_CHECKING:
from .authsrv import VFS from .authsrv import VFS
FAKE_MP = False
try: try:
import multiprocessing as mp import multiprocessing as mp
@ -429,6 +431,52 @@ class HLog(logging.Handler):
self.log_func(record.name[-21:], msg, c) self.log_func(record.name[-21:], msg, c)
class NetMap(object):
def __init__(self, ips: list[str], netdevs: dict[str, str]) -> None:
if "::" in ips:
ips = [x for x in ips if x != "::"] + list(
[x.split("/")[0] for x in netdevs if ":" in x]
)
ips.append("0.0.0.0")
if "0.0.0.0" in ips:
ips = [x for x in ips if x != "0.0.0.0"] + list(
[x.split("/")[0] for x in netdevs if ":" not in x]
)
ips = [x for x in ips if x not in ("::1", "127.0.0.1")]
ips = [[x for x in netdevs if x.startswith(y + "/")][0] for y in ips]
self.cache: dict[str, str] = {}
self.b2sip: dict[bytes, str] = {}
self.b2net: dict[bytes, Union[IPv4Network, IPv6Network]] = {}
self.bip: list[bytes] = []
for ip in ips:
v6 = ":" in ip
fam = socket.AF_INET6 if v6 else socket.AF_INET
bip = socket.inet_pton(fam, ip.split("/")[0])
self.bip.append(bip)
self.b2sip[bip] = ip.split('/')[0]
self.b2net[bip] = (IPv6Network if v6 else IPv4Network)(ip, False)
self.bip.sort(reverse=True)
def map(self, ip: str) -> str:
try:
return self.cache[ip]
except:
pass
v6 = ":" in ip
ci = IPv6Address(ip) if v6 else IPv4Address(ip)
bip = next((x for x in self.bip if ci in self.b2net[x]), None)
ret = self.b2sip[bip] if bip else ""
if len(self.cache) > 9000:
self.cache = {}
self.cache[ip] = ret
return ret
class UnrecvEOF(OSError): class UnrecvEOF(OSError):
pass pass

1
copyparty/web/a/up2k.py Symbolic link
View file

@ -0,0 +1 @@
../../../bin/up2k.py

View file

@ -0,0 +1 @@
../../../contrib/webdav-cfg.bat

View file

@ -10,6 +10,9 @@ html {
padding: 0 1em 3em 1em; padding: 0 1em 3em 1em;
line-height: 1.3em; line-height: 1.3em;
} }
#wrap.w {
max-width: 96%;
}
h1 { h1 {
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
margin: 2em 0 .4em 0; margin: 2em 0 .4em 0;
@ -25,15 +28,16 @@ a {
text-decoration: none; text-decoration: none;
border-bottom: 1px solid #8ab; border-bottom: 1px solid #8ab;
border-radius: .2em; border-radius: .2em;
padding: .2em .8em; padding: .2em .6em;
margin: 0 .3em;
} }
a+a { td a {
margin-left: .5em; margin: 0;
} }
.refresh, .af,
.logout { .logout {
float: right; float: right;
margin: -.2em 0 0 .5em; margin: -.2em 0 0 .8em;
} }
.logout, .logout,
a.r { a.r {
@ -64,9 +68,15 @@ table {
.num td:first-child { .num td:first-child {
text-align: right; text-align: right;
} }
.c {
text-align: center;
}
.btns { .btns {
margin: 1em 0; margin: 1em 0;
} }
.btns>a:first-child {
margin-left: 0;
}
#msg { #msg {
margin: 3em 0; margin: 3em 0;
} }
@ -83,6 +93,41 @@ blockquote {
border-left: .3em solid rgba(128,128,128,0.5); border-left: .3em solid rgba(128,128,128,0.5);
border-radius: 0 0 0 .25em; border-radius: 0 0 0 .25em;
} }
pre, code {
color: #480;
background: #fff;
font-family: 'scp', monospace, monospace;
border: 1px solid rgba(128,128,128,0.3);
border-radius: .2em;
padding: .15em .2em;
}
html.z pre,
html.z code {
color: #9e0;
background: rgba(0,16,0,0.2);
}
.ossel a.r {
box-shadow: 0 .3em 1em #c04;
}
.os {
line-height: 1.5em;
}
.sph {
margin-top: 4em;
}
.sph code {
margin-left: .3em;
}
pre b,
code b {
color: #000;
font-weight: normal;
text-shadow: 0 0 .2em #0f0;
}
html.z pre b,
html.z code b {
color: #fff;
}
html.z { html.z {
@ -102,6 +147,9 @@ html.z a.r {
background: #804; background: #804;
border-color: #c28; border-color: #c28;
} }
html.z .ossel a.r {
box-shadow: 0 .3em 1em #704, 0 .3em 1em #704;
}
html.z input { html.z input {
color: #fff; color: #fff;
background: #626; background: #626;

View file

@ -13,7 +13,8 @@
<body> <body>
<div id="wrap"> <div id="wrap">
<a id="a" href="/?h" class="refresh">refresh</a> <a id="a" href="/?h" class="af">refresh</a>
<a id="v" href="/?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>

View file

@ -24,11 +24,14 @@ var Ls = {
".s1": "kartlegg", ".s1": "kartlegg",
"t1": "handling", "t1": "handling",
"u2": "tid siden noen sist skrev til serveren$N( opplastning / navneendring / ... )$N$N17d = 17 dager$N1h23 = 1 time 23 minutter$N4m56 = 4 minuter 56 sekunder", "u2": "tid siden noen sist skrev til serveren$N( opplastning / navneendring / ... )$N$N17d = 17 dager$N1h23 = 1 time 23 minutter$N4m56 = 4 minuter 56 sekunder",
"v1": "koble til",
"v2": "bruk denne serveren som en lokal harddisk$N$NADVARSEL: kommer til å vise passordet ditt!"
}, },
"eng": { "eng": {
"d2": "shows the state of all active threads", "d2": "shows the state of all active threads",
"e2": "reload config files (accounts/volumes/volflags),$Nand rescan all e2ds volumes", "e2": "reload config files (accounts/volumes/volflags),$Nand rescan all e2ds volumes",
"u2": "time since the last server write$N( upload / rename / ... )$N$N17d = 17 days$N1h23 = 1 hour 23 minutes$N4m56 = 4 minutes 56 seconds", "u2": "time since the last server write$N( upload / rename / ... )$N$N17d = 17 days$N1h23 = 1 hour 23 minutes$N4m56 = 4 minutes 56 seconds",
"v2": "use this server as a local HDD$N$NWARNING: this will show your password!",
} }
}, },
d = Ls[sread("lang") || lang]; d = Ls[sread("lang") || lang];

199
copyparty/web/svcs.html Normal file
View file

@ -0,0 +1,199 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{ args.doctitle }} @ {{ args.name }}</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=0.8">
{{ html_head }}
<link rel="stylesheet" media="screen" href="/.cpr/splash.css?_={{ ts }}">
<link rel="stylesheet" media="screen" href="/.cpr/ui.css?_={{ ts }}">
</head>
<body>
<div id="wrap" class="w">
<div class="c">
<p class="btns"><a href="/">browse files</a> // <a href="/?h">control panel</a></p>
<p>or choose your OS for cooler alternatives:</p>
<div class="ossel">
<a id="swin" href="#">Windows</a>
<a id="slin" href="#">Linux</a>
<a id="smac" href="#">macOS</a>
</div>
</div>
<p class="sph">
make this server appear on your computer as a regular HDD!<br />
pick your favorite below (sorted by performance, best first) and lets 🎉<br />
<br />
placeholders:
<span class="os win">
{% if accs %}<code><b>{{ pw }}</b></code>=password, {% endif %}<code><b>W:</b></code>=mountpoint
</span>
<span class="os lin mac">
{% if accs %}<code><b>{{ pw }}</b></code>=password, {% endif %}<code><b>mp</b></code>=mountpoint
</span>
</p>
{% if not args.no_dav %}
<h1>WebDAV</h1>
<div class="os win">
<p><em>note: rclone-FTP is a bit faster, so {% if args.ftp or args.ftps %}try that first{% else %}consider enabling FTP in server settings{% endif %}</em></p>
<p>if you can, install <a href="https://winfsp.dev/rel/">winfsp</a>+<a href="https://downloads.rclone.org/rclone-current-windows-amd64.zip">rclone</a> and then paste this in cmd:</p>
<pre>
rclone config create {{ aname }}-dav webdav url=http{{ s }}://{{ rip }}{{ hport }} vendor=other{% if accs %} user=k pass=<b>{{ pw }}</b>{% endif %}
rclone mount --vfs-cache-mode writes --dir-cache-time 5s {{ aname }}-dav:{{ vp }} <b>W:</b>
</pre>
{% if s %}
<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 %}
<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 password login), then connect:</p>
<pre>
net use <b>w:</b> http{{ s }}://{{ ep }}/{{ vp }}{% if accs %} k /user:<b>{{ pw }}</b>{% endif %}
</pre>
</div>
<div class="os lin">
<pre>
yum install davfs2
{% if accs %}printf '%s\n' <b>{{ pw }}</b> k | {% endif %}mount -t davfs -ouid=1000 http{{ s }}://{{ ep }}/{{ vp }} <b>mp</b>
</pre>
<p>or you can use rclone instead, which is much slower but doesn't require root:</p>
<pre>
rclone config create {{ aname }}-dav webdav url=http{{ s }}://{{ rip }}{{ hport }} vendor=other{% if accs %} user=k pass=<b>{{ pw }}</b>{% endif %}
rclone mount --vfs-cache-mode writes --dir-cache-time 5s {{ aname }}-dav:{{ vp }} <b>mp</b>
</pre>
{% if s %}
<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 %}
<p>or the emergency alternative (gnome/gui-only):</p>
<!-- gnome-bug: ignores vp -->
<pre>
{%- if accs %}
echo <b>{{ pw }}</b> | gio mount dav{{ s }}://k@{{ ep }}/{{ vp }}
{%- else %}
gio mount -a dav{{ s }}://{{ ep }}/{{ vp }}
{%- endif %}
</pre>
</div>
<div class="os mac">
<pre>
osascript -e ' mount volume "http{{ s }}://k:<b>{{ pw }}</b>@{{ ep }}/{{ vp }}" '
</pre>
<p>or you can open up a Finder, press command-K and paste this instead:</p>
<pre>
http{{ s }}://k:<b>{{ pw }}</b>@{{ ep }}/{{ vp }}
</pre>
{% if s %}
<p><em>replace <code>https</code> with <code>http</code> if it doesn't work</em></p>
{% endif %}
</div>
{% endif %}
{% if args.ftp or args.ftps %}
<h1>FTP</h1>
<div class="os win">
<p>if you can, install <a href="https://winfsp.dev/rel/">winfsp</a>+<a href="https://downloads.rclone.org/rclone-current-windows-amd64.zip">rclone</a> and then paste this in cmd:</p>
<pre>
rclone config create {{ aname }}-ftp ftp host={{ rip }} port={{ args.ftp or args.ftps }} pass=k user={% if accs %}<b>{{ pw }}</b>{% else %}anonymous{% endif %} tls={{ "false" if args.ftp else "true" }}
rclone mount --vfs-cache-mode writes --dir-cache-time 5s {{ aname }}-ftp:{{ vp }} <b>W:</b>
</pre>
<p>if you want to use the native FTP client in windows instead (please dont), press <code>win+R</code> and run this command:</p>
<pre>
explorer {{ "ftp" if args.ftp else "ftps" }}://{% if accs %}<b>{{ pw }}</b>:k@{% endif %}{{ host }}:{{ args.ftp or args.ftps }}/{{ vp }}
</pre>
</div>
<div class="os lin">
<pre>
rclone config create {{ aname }}-ftp ftp host={{ rip }} port={{ args.ftp or args.ftps }} pass=k user={% if accs %}<b>{{ pw }}</b>{% else %}anonymous{% endif %} tls={{ "false" if args.ftp else "true" }}
rclone mount --vfs-cache-mode writes --dir-cache-time 5s {{ aname }}-ftp:{{ vp }} <b>mp</b>
</pre>
<p>emergency alternative (gnome/gui-only):</p>
<!-- gnome-bug: ignores vp -->
<pre>
{%- if accs %}
echo <b>{{ pw }}</b> | gio mount ftp{{ "" if args.ftp else "s" }}://k@{{ host }}:{{ args.ftp or args.ftps }}/{{ vp }}
{%- else %}
gio mount -a ftp{{ "" if args.ftp else "s" }}://{{ host }}:{{ args.ftp or args.ftps }}/{{ vp }}
{%- endif %}
</pre>
</div>
<div class="os mac">
<p>note: FTP is read-only on macos; please use WebDAV instead</p>
<pre>
open {{ "ftp" if args.ftp else "ftps" }}://{% if accs %}k:<b>{{ pw }}</b>@{% else %}anonymous:@{% endif %}{{ host }}:{{ args.ftp or args.ftps }}/{{ vp }}
</pre>
</div>
{% endif %}
<h1>partyfuse</h1>
<p>
<a href="/.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 lin">doesn't need root</span>
</p>
<pre>
partyfuse.py{% if accs %} -a <b>{{ pw }}</b>{% endif %} http{{ s }}://{{ ep }}/{{ vp }} <b><span class="os win">W:</span><span class="os lin mac">mp</span></b>
</pre>
{% if s %}
<p><em>note: if you are on LAN (or just dont have valid certificates), add <code>-td</code></p>
{% endif %}
<p>
you can use <a href="/.cpr/a/up2k.py">up2k.py</a> to upload (sometimes faster than web-browsers)
</p>
{% if args.smb %}
<h1>SMB / CIFS</h1>
<em><a href="https://github.com/SecureAuthCorp/impacket/issues/1433">bug:</a> max ~300 files in each folder</em>
<div class="os win">
<pre>
net use <b>w:</b> \\{{ host }}\a{% if accs %} k /user:<b>{{ pw }}</b>{% endif %}
</pre>
<!-- rclone fails due to copyparty-smb bugs -->
</div>
<div class="os lin">
<pre>
mount -t cifs -o{% if accs %}user=<b>{{ pw }}</b>,pass=k,{% endif %}vers={{ 1 if args.smb1 else 2 }}.0,port={{ args.smb_port }},uid=1000 //{{ host }}/a/ <b>mp</b>
</pre>
<!-- p>or the emergency alternative (gnome/gui-only):</p nevermind, only works through mdns -->
</div>
<pre class="os mac">
@@@ net use <b>w:</b> \\{{ host }}\a k /user:<b>{{ pw }}</b>
</pre>
{% endif %}
</div>
<a href="#" id="repl">π</a>
<script>
var lang="{{ lang }}",
dfavico="{{ favico }}";
document.documentElement.className=localStorage.theme||"{{ args.theme }}";
</script>
<script src="/.cpr/util.js?_={{ ts }}"></script>
<script src="/.cpr/svcs.js?_={{ ts }}"></script>
</body>
</html>

34
copyparty/web/svcs.js Normal file
View file

@ -0,0 +1,34 @@
var oa = QSA('pre');
for (var a = 0; a < oa.length; a++) {
var html = oa[a].innerHTML,
nd = /^ +/.exec(html)[0].length,
rd = new RegExp('(^|\n) {' + nd + '}', 'g');
oa[a].innerHTML = html.replace(rd, '$1').replace(/[ \r\n]+$/, '');
}
oa = QSA('.ossel a');
for (var a = 0; a < oa.length; a++)
oa[a].onclick = esetos;
function esetos(e) {
ev(e);
setos(e.target.id.slice(1));
}
function setos(os) {
var oa = QSA('.os');
for (var a = 0; a < oa.length; a++)
oa[a].style.display = 'none';
var oa = QSA('.' + os);
for (var a = 0; a < oa.length; a++)
oa[a].style.display = '';
oa = QSA('.ossel a');
for (var a = 0; a < oa.length; a++)
clmod(oa[a], 'r', oa[a].id.slice(1) == os);
}
setos(WINDOWS ? 'win' : LINUX ? 'lin' : MACOS ? 'mac' : '');

View file

@ -17,7 +17,9 @@ var wah = '',
VCHROME = CHROME ? 1 : 0, VCHROME = CHROME ? 1 : 0,
FIREFOX = ('netscape' in window) && / rv:/.test(navigator.userAgent), FIREFOX = ('netscape' in window) && / rv:/.test(navigator.userAgent),
IPHONE = TOUCH && /iPhone|iPad|iPod/i.test(navigator.userAgent), IPHONE = TOUCH && /iPhone|iPad|iPod/i.test(navigator.userAgent),
WINDOWS = navigator.platform ? navigator.platform == 'Win32' : /Windows/.test(navigator.userAgent); LINUX = /Linux/.test(navigator.userAgent),
MACOS = /[^a-z]mac ?os/i.test(navigator.userAgent),
WINDOWS = /Windows/.test(navigator.userAgent);
if (!window.WebAssembly || !WebAssembly.Memory) if (!window.WebAssembly || !WebAssembly.Memory)
window.WebAssembly = false; window.WebAssembly = false;

View file

@ -92,7 +92,11 @@ rm -rf build/pypi
mkdir -p build/pypi mkdir -p build/pypi
cp -pR setup.py README.md LICENSE copyparty tests bin scripts/strip_hints build/pypi/ cp -pR setup.py README.md LICENSE copyparty tests bin scripts/strip_hints build/pypi/
cd build/pypi cd build/pypi
tar --strip-components=2 -xf ../strip-hints-0.1.10.tar.gz strip-hints-0.1.10/src/strip_hints f=../strip-hints-0.1.10.tar.gz
[ -e $f ] ||
(url=https://files.pythonhosted.org/packages/9c/d4/312ddce71ee10f7e0ab762afc027e07a918f1c0e1be5b0069db5b0e7542d/strip-hints-0.1.10.tar.gz;
wget -O$f "$url" || curl -L "$url" >$f)
tar --strip-components=2 -xf $f strip-hints-0.1.10/src/strip_hints
python3 -c 'from strip_hints.a import uh; uh("copyparty")' python3 -c 'from strip_hints.a import uh; uh("copyparty")'
./setup.py clean2 ./setup.py clean2

View file

@ -245,6 +245,21 @@ tmpdir="$(
} }
rm -f ../tar rm -f ../tar
# resolve symlinks
find -type l |
while IFS= read -r f1; do (
cd "${f1%/*}"
f1="./${f1##*/}"
f2="$(readlink "$f1")"
[ -e "$f2" ] || f2="../$f2"
[ -e "$f2" ] || {
echo could not resolve "$f1"
exit 1
}
rm "$f1"
cp -pv "$f2" "$f1"
); done
# insert asynchat # insert asynchat
mkdir copyparty/vend mkdir copyparty/vend
for n in asyncore.py asynchat.py; do for n in asyncore.py asynchat.py; do

View file

@ -25,6 +25,7 @@ copyparty/res,
copyparty/res/COPYING.txt, copyparty/res/COPYING.txt,
copyparty/res/insecure.pem, copyparty/res/insecure.pem,
copyparty/smbd.py, copyparty/smbd.py,
copyparty/ssdp.py,
copyparty/star.py, copyparty/star.py,
copyparty/stolen, copyparty/stolen,
copyparty/stolen/__init__.py, copyparty/stolen/__init__.py,
@ -57,6 +58,9 @@ copyparty/vend,
copyparty/vend/asynchat.py, copyparty/vend/asynchat.py,
copyparty/vend/asyncore.py, copyparty/vend/asyncore.py,
copyparty/web, copyparty/web,
copyparty/web/a,
copyparty/web/a/up2k.py,
copyparty/web/a/webdav-cfg.bat,
copyparty/web/baguettebox.js, copyparty/web/baguettebox.js,
copyparty/web/browser.css, copyparty/web/browser.css,
copyparty/web/browser.html, copyparty/web/browser.html,
@ -94,6 +98,8 @@ copyparty/web/msg.html,
copyparty/web/splash.css, copyparty/web/splash.css,
copyparty/web/splash.html, copyparty/web/splash.html,
copyparty/web/splash.js, copyparty/web/splash.js,
copyparty/web/svcs.html,
copyparty/web/svcs.js,
copyparty/web/ui.css, copyparty/web/ui.css,
copyparty/web/up2k.js, copyparty/web/up2k.js,
copyparty/web/util.js, copyparty/web/util.js,