mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
add landing page with mounting instructions
This commit is contained in:
parent
e53531a9fb
commit
f0e78a6826
|
@ -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")
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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("."):
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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:
|
||||||
|
try:
|
||||||
bound = self.create_servers()
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
try:
|
||||||
bound = self.create_servers()
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
1
copyparty/web/a/up2k.py
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../bin/up2k.py
|
1
copyparty/web/a/webdav-cfg.bat
Symbolic link
1
copyparty/web/a/webdav-cfg.bat
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../contrib/webdav-cfg.bat
|
|
@ -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;
|
||||||
|
|
|
@ -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 <small>(you're not logged in)</small></p>
|
<p id="b">howdy stranger <small>(you're not logged in)</small></p>
|
||||||
|
|
|
@ -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
199
copyparty/web/svcs.html
Normal 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
34
copyparty/web/svcs.js
Normal 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' : '');
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue