Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cacca663b3 | ||
|
|
d5109be559 | ||
|
|
d999f06bb9 | ||
|
|
a1a8a8c7b5 | ||
|
|
fdd6f3b4a6 | ||
|
|
f5191973df | ||
|
|
ddbaebe779 |
@@ -818,6 +818,13 @@ an FTP server can be started using `--ftp 3921`, and/or `--ftps` for explicit T
|
|||||||
* some older software (filezilla on debian-stable) cannot passive-mode with TLS
|
* some older software (filezilla on debian-stable) cannot passive-mode with TLS
|
||||||
* login with any username + your password, or put your password in the username field
|
* login with any username + your password, or put your password in the username field
|
||||||
|
|
||||||
|
some recommended FTP / FTPS clients; `wark` = example password:
|
||||||
|
* https://winscp.net/eng/download.php
|
||||||
|
* https://filezilla-project.org/ struggles a bit with ftps in active-mode, but is fine otherwise
|
||||||
|
* https://rclone.org/ does FTPS with `tls=false explicit_tls=true`
|
||||||
|
* `lftp -u k,wark -p 3921 127.0.0.1 -e ls`
|
||||||
|
* `lftp -u k,wark -p 3990 127.0.0.1 -e 'set ssl:verify-certificate no; ls'`
|
||||||
|
|
||||||
|
|
||||||
## webdav server
|
## webdav server
|
||||||
|
|
||||||
@@ -1421,7 +1428,6 @@ defaults are usually fine - expect `8 GiB/s` download, `1 GiB/s` upload
|
|||||||
below are some tweaks roughly ordered by usefulness:
|
below are some tweaks roughly ordered by usefulness:
|
||||||
|
|
||||||
* `-q` disables logging and can help a bunch, even when combined with `-lo` to redirect logs to file
|
* `-q` disables logging and can help a bunch, even when combined with `-lo` to redirect logs to file
|
||||||
* `--http-only` or `--https-only` (unless you want to support both protocols) will reduce the delay before a new connection is established
|
|
||||||
* `--hist` pointing to a fast location (ssd) will make directory listings and searches faster when `-e2d` or `-e2t` is set
|
* `--hist` pointing to a fast location (ssd) will make directory listings and searches faster when `-e2d` or `-e2t` is set
|
||||||
* `--no-hash .` when indexing a network-disk if you don't care about the actual filehashes and only want the names/tags searchable
|
* `--no-hash .` when indexing a network-disk if you don't care about the actual filehashes and only want the names/tags searchable
|
||||||
* `--no-htp --hash-mt=0 --mtag-mt=1 --th-mt=1` minimizes the number of threads; can help in some eccentric environments (like the vscode debugger)
|
* `--no-htp --hash-mt=0 --mtag-mt=1 --th-mt=1` minimizes the number of threads; can help in some eccentric environments (like the vscode debugger)
|
||||||
@@ -1599,6 +1605,7 @@ can be convenient on machines where installing python is problematic, however is
|
|||||||
* [copyparty.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty.exe) runs on win8 or newer, was compiled on win10, does thumbnails + media tags, and is *currently* safe to use, but any future python/expat/pillow CVEs can only be remedied by downloading a newer version of the exe
|
* [copyparty.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty.exe) runs on win8 or newer, was compiled on win10, does thumbnails + media tags, and is *currently* safe to use, but any future python/expat/pillow CVEs can only be remedied by downloading a newer version of the exe
|
||||||
|
|
||||||
* on win8 it needs [vc redist 2015](https://www.microsoft.com/en-us/download/details.aspx?id=48145), on win10 it just works
|
* on win8 it needs [vc redist 2015](https://www.microsoft.com/en-us/download/details.aspx?id=48145), on win10 it just works
|
||||||
|
* some antivirus may freak out (false-positive), possibly [Avast, AVG, and McAfee](https://www.virustotal.com/gui/file/52391a1e9842cf70ad243ef83844d46d29c0044d101ee0138fcdd3c8de2237d6/detection)
|
||||||
|
|
||||||
* dangerous: [copyparty32.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty32.exe) is compatible with [windows7](https://user-images.githubusercontent.com/241032/221445944-ae85d1f4-d351-4837-b130-82cab57d6cca.png), which means it uses an ancient copy of python (3.7.9) which cannot be upgraded and should never be exposed to the internet (LAN is fine)
|
* dangerous: [copyparty32.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty32.exe) is compatible with [windows7](https://user-images.githubusercontent.com/241032/221445944-ae85d1f4-d351-4837-b130-82cab57d6cca.png), which means it uses an ancient copy of python (3.7.9) which cannot be upgraded and should never be exposed to the internet (LAN is fine)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
# when running copyparty behind a reverse proxy,
|
# when running copyparty behind a reverse proxy,
|
||||||
# the following arguments are recommended:
|
# the following arguments are recommended:
|
||||||
#
|
#
|
||||||
# --http-only lower latency on initial connection
|
|
||||||
# -i 127.0.0.1 only accept connections from nginx
|
# -i 127.0.0.1 only accept connections from nginx
|
||||||
#
|
#
|
||||||
# if you are doing location-based proxying (such as `/stuff` below)
|
# if you are doing location-based proxying (such as `/stuff` below)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
# when running copyparty behind a reverse proxy,
|
# when running copyparty behind a reverse proxy,
|
||||||
# the following arguments are recommended:
|
# the following arguments are recommended:
|
||||||
#
|
#
|
||||||
# --http-only lower latency on initial connection
|
|
||||||
# -i 127.0.0.1 only accept connections from nginx
|
# -i 127.0.0.1 only accept connections from nginx
|
||||||
#
|
#
|
||||||
# -nc must match or exceed the webserver's max number of concurrent clients;
|
# -nc must match or exceed the webserver's max number of concurrent clients;
|
||||||
@@ -9,7 +8,7 @@
|
|||||||
# nginx default is 512 (worker_processes 1, worker_connections 512)
|
# nginx default is 512 (worker_processes 1, worker_connections 512)
|
||||||
#
|
#
|
||||||
# you may also consider adding -j0 for CPU-intensive configurations
|
# you may also consider adding -j0 for CPU-intensive configurations
|
||||||
# (not that i can really think of any good examples)
|
# (5'000 requests per second, or 20gbps upload/download in parallel)
|
||||||
#
|
#
|
||||||
# on fedora/rhel, remember to setsebool -P httpd_can_network_connect 1
|
# on fedora/rhel, remember to setsebool -P httpd_can_network_connect 1
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Maintainer: icxes <dev.null@need.moe>
|
# Maintainer: icxes <dev.null@need.moe>
|
||||||
pkgname=copyparty
|
pkgname=copyparty
|
||||||
pkgver="1.6.11"
|
pkgver="1.6.12"
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="Portable file sharing hub"
|
pkgdesc="Portable file sharing hub"
|
||||||
arch=("any")
|
arch=("any")
|
||||||
@@ -26,7 +26,7 @@ source=("${url}/releases/download/v${pkgver}/${pkgname}-sfx.py"
|
|||||||
"https://raw.githubusercontent.com/9001/${pkgname}/v${pkgver}/LICENSE"
|
"https://raw.githubusercontent.com/9001/${pkgname}/v${pkgver}/LICENSE"
|
||||||
)
|
)
|
||||||
backup=("etc/${pkgname}.d/init" )
|
backup=("etc/${pkgname}.d/init" )
|
||||||
sha256sums=("d096e33ab666ef45213899dd3a10735f62b5441339cb7374f93b232d0b6c8d34"
|
sha256sums=("ce3702634fecf7cd6a5c79709a8e6cb1e9fe0748ec4754a278be7a60b618dd42"
|
||||||
"b8565eba5e64dedba1cf6c7aac7e31c5a731ed7153d6810288a28f00a36c28b2"
|
"b8565eba5e64dedba1cf6c7aac7e31c5a731ed7153d6810288a28f00a36c28b2"
|
||||||
"f65c207e0670f9d78ad2e399bda18d5502ff30d2ac79e0e7fc48e7fbdc39afdc"
|
"f65c207e0670f9d78ad2e399bda18d5502ff30d2ac79e0e7fc48e7fbdc39afdc"
|
||||||
"c4f396b083c9ec02ad50b52412c84d2a82be7f079b2d016e1c9fad22d68285ff"
|
"c4f396b083c9ec02ad50b52412c84d2a82be7f079b2d016e1c9fad22d68285ff"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"url": "https://github.com/9001/copyparty/releases/download/v1.6.11/copyparty-sfx.py",
|
"url": "https://github.com/9001/copyparty/releases/download/v1.6.12/copyparty-sfx.py",
|
||||||
"version": "1.6.11",
|
"version": "1.6.12",
|
||||||
"hash": "sha256-0JbjOrZm70UhOJndOhBzX2K1RBM5y3N0+TsjLQtsjTQ="
|
"hash": "sha256-zjcCY0/s981qXHlwmo5ssen+B0jsR1SieL56YLYY3UI="
|
||||||
}
|
}
|
||||||
@@ -261,7 +261,7 @@ def ensure_locale() -> None:
|
|||||||
warn(t.format(safe))
|
warn(t.format(safe))
|
||||||
|
|
||||||
|
|
||||||
def ensure_cert() -> None:
|
def ensure_cert(al: argparse.Namespace) -> None:
|
||||||
"""
|
"""
|
||||||
the default cert (and the entire TLS support) is only here to enable the
|
the default cert (and the entire TLS support) is only here to enable the
|
||||||
crypto.subtle javascript API, which is necessary due to the webkit guys
|
crypto.subtle javascript API, which is necessary due to the webkit guys
|
||||||
@@ -270,15 +270,30 @@ def ensure_cert() -> None:
|
|||||||
i feel awful about this and so should they
|
i feel awful about this and so should they
|
||||||
"""
|
"""
|
||||||
cert_insec = os.path.join(E.mod, "res/insecure.pem")
|
cert_insec = os.path.join(E.mod, "res/insecure.pem")
|
||||||
cert_cfg = os.path.join(E.cfg, "cert.pem")
|
cert_appdata = os.path.join(E.cfg, "cert.pem")
|
||||||
if not os.path.exists(cert_cfg):
|
if not os.path.isfile(al.cert):
|
||||||
shutil.copy(cert_insec, cert_cfg)
|
if cert_appdata != al.cert:
|
||||||
|
raise Exception("certificate file does not exist: " + al.cert)
|
||||||
|
|
||||||
|
shutil.copy(cert_insec, al.cert)
|
||||||
|
|
||||||
|
with open(al.cert, "rb") as f:
|
||||||
|
buf = f.read()
|
||||||
|
o1 = buf.find(b"-BEGIN PRIVATE KEY-")
|
||||||
|
o2 = buf.find(b"-BEGIN CERTIFICATE-")
|
||||||
|
m = "unsupported certificate format: "
|
||||||
|
if o1 < 0:
|
||||||
|
raise Exception(m + "no private key inside pem")
|
||||||
|
if o2 < 0:
|
||||||
|
raise Exception(m + "no server certificate inside pem")
|
||||||
|
if o1 > o2:
|
||||||
|
raise Exception(m + "private key must appear before server certificate")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if filecmp.cmp(cert_cfg, cert_insec):
|
if filecmp.cmp(al.cert, cert_insec):
|
||||||
lprint(
|
lprint(
|
||||||
"\033[33musing default TLS certificate; https will be insecure."
|
"\033[33musing default TLS certificate; https will be insecure."
|
||||||
+ "\033[36m\ncertificate location: {}\033[0m\n".format(cert_cfg)
|
+ "\033[36m\ncertificate location: {}\033[0m\n".format(al.cert)
|
||||||
)
|
)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
@@ -499,8 +514,12 @@ def get_sects():
|
|||||||
"""
|
"""
|
||||||
volflags are appended to volume definitions, for example,
|
volflags are appended to volume definitions, for example,
|
||||||
to create a write-only volume with the \033[33mnodupe\033[0m and \033[32mnosub\033[0m flags:
|
to create a write-only volume with the \033[33mnodupe\033[0m and \033[32mnosub\033[0m flags:
|
||||||
\033[35m-v /mnt/inc:/inc:w\033[33m:c,nodupe\033[32m:c,nosub"""
|
\033[35m-v /mnt/inc:/inc:w\033[33m:c,nodupe\033[32m:c,nosub\033[0m
|
||||||
)
|
|
||||||
|
if global config defines a volflag for all volumes,
|
||||||
|
you can unset it for a specific volume with -flag
|
||||||
|
"""
|
||||||
|
).rstrip()
|
||||||
+ build_flags_desc(),
|
+ build_flags_desc(),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -697,10 +716,11 @@ def add_network(ap):
|
|||||||
ap2.add_argument("--rsp-jtr", metavar="SEC", type=float, default=0, help="debug: response delay, random duration 0..SEC")
|
ap2.add_argument("--rsp-jtr", metavar="SEC", type=float, default=0, help="debug: response delay, random duration 0..SEC")
|
||||||
|
|
||||||
|
|
||||||
def add_tls(ap):
|
def add_tls(ap, cert_path):
|
||||||
ap2 = ap.add_argument_group('SSL/TLS options')
|
ap2 = ap.add_argument_group('SSL/TLS options')
|
||||||
ap2.add_argument("--http-only", action="store_true", help="disable ssl/tls -- force plaintext")
|
ap2.add_argument("--http-only", action="store_true", help="disable ssl/tls -- force plaintext")
|
||||||
ap2.add_argument("--https-only", action="store_true", help="disable plaintext -- force tls")
|
ap2.add_argument("--https-only", action="store_true", help="disable plaintext -- force tls")
|
||||||
|
ap2.add_argument("--cert", metavar="PATH", type=u, default=cert_path, help="path to TLS certificate")
|
||||||
ap2.add_argument("--ssl-ver", metavar="LIST", type=u, help="set allowed ssl/tls versions; [\033[32mhelp\033[0m] shows available versions; default is what your python version considers safe")
|
ap2.add_argument("--ssl-ver", metavar="LIST", type=u, help="set allowed ssl/tls versions; [\033[32mhelp\033[0m] shows available versions; default is what your python version considers safe")
|
||||||
ap2.add_argument("--ciphers", metavar="LIST", type=u, help="set allowed ssl/tls ciphers; [\033[32mhelp\033[0m] shows available ciphers")
|
ap2.add_argument("--ciphers", metavar="LIST", type=u, help="set allowed ssl/tls ciphers; [\033[32mhelp\033[0m] shows available ciphers")
|
||||||
ap2.add_argument("--ssl-dbg", action="store_true", help="dump some tls info")
|
ap2.add_argument("--ssl-dbg", action="store_true", help="dump some tls info")
|
||||||
@@ -987,8 +1007,10 @@ def run_argparse(
|
|||||||
description="http file sharing hub v{} ({})".format(S_VERSION, S_BUILD_DT),
|
description="http file sharing hub v{} ({})".format(S_VERSION, S_BUILD_DT),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
cert_path = os.path.join(E.cfg, "cert.pem")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
fk_salt = unicode(os.path.getmtime(os.path.join(E.cfg, "cert.pem")))
|
fk_salt = unicode(os.path.getmtime(cert_path))
|
||||||
except:
|
except:
|
||||||
fk_salt = "hunter2"
|
fk_salt = "hunter2"
|
||||||
|
|
||||||
@@ -1000,7 +1022,7 @@ def run_argparse(
|
|||||||
|
|
||||||
add_general(ap, nc, srvname)
|
add_general(ap, nc, srvname)
|
||||||
add_network(ap)
|
add_network(ap)
|
||||||
add_tls(ap)
|
add_tls(ap, cert_path)
|
||||||
add_qr(ap, tty)
|
add_qr(ap, tty)
|
||||||
add_zeroconf(ap)
|
add_zeroconf(ap)
|
||||||
add_zc_mdns(ap)
|
add_zc_mdns(ap)
|
||||||
@@ -1084,8 +1106,6 @@ def main(argv: Optional[list[str]] = None) -> None:
|
|||||||
print("pybin: {}\n".format(pybin), end="")
|
print("pybin: {}\n".format(pybin), end="")
|
||||||
|
|
||||||
ensure_locale()
|
ensure_locale()
|
||||||
if HAVE_SSL:
|
|
||||||
ensure_cert()
|
|
||||||
|
|
||||||
for k, v in zip(argv[1:], argv[2:]):
|
for k, v in zip(argv[1:], argv[2:]):
|
||||||
if k == "-c" and os.path.isfile(v):
|
if k == "-c" and os.path.isfile(v):
|
||||||
@@ -1153,6 +1173,9 @@ def main(argv: Optional[list[str]] = None) -> None:
|
|||||||
except:
|
except:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
if HAVE_SSL:
|
||||||
|
ensure_cert(al)
|
||||||
|
|
||||||
if WINDOWS and not al.keep_qem:
|
if WINDOWS and not al.keep_qem:
|
||||||
try:
|
try:
|
||||||
disable_quickedit()
|
disable_quickedit()
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
VERSION = (1, 6, 12)
|
VERSION = (1, 6, 13)
|
||||||
CODENAME = "cors k"
|
CODENAME = "cors k"
|
||||||
BUILD_DT = (2023, 4, 20)
|
BUILD_DT = (2023, 4, 23)
|
||||||
|
|
||||||
S_VERSION = ".".join(map(str, VERSION))
|
S_VERSION = ".".join(map(str, VERSION))
|
||||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||||
|
|||||||
@@ -578,6 +578,7 @@ class VFS(object):
|
|||||||
|
|
||||||
def zipgen(
|
def zipgen(
|
||||||
self,
|
self,
|
||||||
|
vpath: str,
|
||||||
vrem: str,
|
vrem: str,
|
||||||
flt: set[str],
|
flt: set[str],
|
||||||
uname: str,
|
uname: str,
|
||||||
@@ -589,7 +590,7 @@ class VFS(object):
|
|||||||
|
|
||||||
# if multiselect: add all items to archive root
|
# if multiselect: add all items to archive root
|
||||||
# if single folder: the folder itself is the top-level item
|
# if single folder: the folder itself is the top-level item
|
||||||
folder = "" if flt or not wrap else (vrem.split("/")[-1].lstrip(".") or "top")
|
folder = "" if flt or not wrap else (vpath.split("/")[-1].lstrip(".") or "top")
|
||||||
|
|
||||||
g = self.walk(folder, vrem, [], uname, [[True, False]], dots, scandir, False)
|
g = self.walk(folder, vrem, [], uname, [[True, False]], dots, scandir, False)
|
||||||
for _, _, vpath, apath, files, rd, vd in g:
|
for _, _, vpath, apath, files, rd, vd in g:
|
||||||
@@ -858,7 +859,7 @@ class AuthSrv(object):
|
|||||||
zd = split_cfg_ln(ln)
|
zd = split_cfg_ln(ln)
|
||||||
fstr = ""
|
fstr = ""
|
||||||
for sk, sv in zd.items():
|
for sk, sv in zd.items():
|
||||||
bad = re.sub(r"[a-z0-9_]", "", sk)
|
bad = re.sub(r"[a-z0-9_-]", "", sk).lstrip("-")
|
||||||
if bad:
|
if bad:
|
||||||
err = "bad characters [{}] in volflag name [{}]; "
|
err = "bad characters [{}] in volflag name [{}]; "
|
||||||
err = err.format(bad, sk)
|
err = err.format(bad, sk)
|
||||||
@@ -934,7 +935,14 @@ class AuthSrv(object):
|
|||||||
value: Union[str, bool, list[str]],
|
value: Union[str, bool, list[str]],
|
||||||
is_list: bool,
|
is_list: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
desc = flagdescs.get(name, "?").replace("\n", " ")
|
desc = flagdescs.get(name.lstrip("-"), "?").replace("\n", " ")
|
||||||
|
|
||||||
|
if re.match("^-[^-]+$", name):
|
||||||
|
t = "└─unset volflag [{}] ({})"
|
||||||
|
self._e(t.format(name[1:], desc))
|
||||||
|
flags[name] = True
|
||||||
|
return
|
||||||
|
|
||||||
if name not in "mtp xbu xau xiu xbr xar xbd xad xm".split():
|
if name not in "mtp xbu xau xiu xbr xar xbd xad xm".split():
|
||||||
if value is True:
|
if value is True:
|
||||||
t = "└─add volflag [{}] = {} ({})"
|
t = "└─add volflag [{}] = {} ({})"
|
||||||
@@ -1440,6 +1448,12 @@ class AuthSrv(object):
|
|||||||
self.log(t, 1)
|
self.log(t, 1)
|
||||||
errors = True
|
errors = True
|
||||||
|
|
||||||
|
for vol in vfs.all_vols.values():
|
||||||
|
for k in list(vol.flags.keys()):
|
||||||
|
if re.match("^-[^-]+$", k):
|
||||||
|
vol.flags.pop(k[1:], None)
|
||||||
|
vol.flags.pop(k)
|
||||||
|
|
||||||
if errors:
|
if errors:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ class FtpAuth(DummyAuthorizer):
|
|||||||
self, username: str, password: str, handler: Any
|
self, username: str, password: str, handler: Any
|
||||||
) -> None:
|
) -> None:
|
||||||
handler.username = "{}:{}".format(username, password)
|
handler.username = "{}:{}".format(username, password)
|
||||||
|
handler.uname = "*"
|
||||||
|
|
||||||
ip = handler.addr[0]
|
ip = handler.addr[0]
|
||||||
if ip.startswith("::ffff:"):
|
if ip.startswith("::ffff:"):
|
||||||
@@ -86,14 +87,14 @@ class FtpAuth(DummyAuthorizer):
|
|||||||
|
|
||||||
raise AuthenticationFailed("Authentication failed.")
|
raise AuthenticationFailed("Authentication failed.")
|
||||||
|
|
||||||
handler.username = uname
|
handler.uname = uname
|
||||||
|
|
||||||
def get_home_dir(self, username: str) -> str:
|
def get_home_dir(self, username: str) -> str:
|
||||||
return "/"
|
return "/"
|
||||||
|
|
||||||
def has_user(self, username: str) -> bool:
|
def has_user(self, username: str) -> bool:
|
||||||
asrv = self.hub.asrv
|
asrv = self.hub.asrv
|
||||||
return username in asrv.acct
|
return username in asrv.acct or username in asrv.iacct
|
||||||
|
|
||||||
def has_perm(self, username: str, perm: int, path: Optional[str] = None) -> bool:
|
def has_perm(self, username: str, perm: int, path: Optional[str] = None) -> bool:
|
||||||
return True # handled at filesystem layer
|
return True # handled at filesystem layer
|
||||||
@@ -115,8 +116,7 @@ class FtpFs(AbstractedFS):
|
|||||||
self.h = self.cmd_channel = cmd_channel # type: FTPHandler
|
self.h = self.cmd_channel = cmd_channel # type: FTPHandler
|
||||||
self.hub: "SvcHub" = cmd_channel.hub
|
self.hub: "SvcHub" = cmd_channel.hub
|
||||||
self.args = cmd_channel.args
|
self.args = cmd_channel.args
|
||||||
|
self.uname = cmd_channel.uname
|
||||||
self.uname = self.hub.asrv.iacct.get(cmd_channel.password, "*")
|
|
||||||
|
|
||||||
self.cwd = "/" # pyftpdlib convention of leading slash
|
self.cwd = "/" # pyftpdlib convention of leading slash
|
||||||
self.root = "/var/lib/empty"
|
self.root = "/var/lib/empty"
|
||||||
@@ -215,7 +215,7 @@ class FtpFs(AbstractedFS):
|
|||||||
self.can_delete,
|
self.can_delete,
|
||||||
self.can_get,
|
self.can_get,
|
||||||
self.can_upget,
|
self.can_upget,
|
||||||
) = self.hub.asrv.vfs.can_access(self.cwd.lstrip("/"), self.h.username)
|
) = self.hub.asrv.vfs.can_access(self.cwd.lstrip("/"), self.h.uname)
|
||||||
|
|
||||||
def mkdir(self, path: str) -> None:
|
def mkdir(self, path: str) -> None:
|
||||||
ap = self.rv2a(path, w=True)[0]
|
ap = self.rv2a(path, w=True)[0]
|
||||||
@@ -265,7 +265,7 @@ class FtpFs(AbstractedFS):
|
|||||||
|
|
||||||
def rename(self, src: str, dst: str) -> None:
|
def rename(self, src: str, dst: str) -> None:
|
||||||
if not self.can_move:
|
if not self.can_move:
|
||||||
self.die("Not allowed for user " + self.h.username)
|
self.die("Not allowed for user " + self.h.uname)
|
||||||
|
|
||||||
if self.args.no_mv:
|
if self.args.no_mv:
|
||||||
self.die("The rename/move feature is disabled in server config")
|
self.die("The rename/move feature is disabled in server config")
|
||||||
@@ -344,10 +344,12 @@ class FtpHandler(FTPHandler):
|
|||||||
abstracted_fs = FtpFs
|
abstracted_fs = FtpFs
|
||||||
hub: "SvcHub"
|
hub: "SvcHub"
|
||||||
args: argparse.Namespace
|
args: argparse.Namespace
|
||||||
|
uname: str
|
||||||
|
|
||||||
def __init__(self, conn: Any, server: Any, ioloop: Any = None) -> None:
|
def __init__(self, conn: Any, server: Any, ioloop: Any = None) -> None:
|
||||||
self.hub: "SvcHub" = FtpHandler.hub
|
self.hub: "SvcHub" = FtpHandler.hub
|
||||||
self.args: argparse.Namespace = FtpHandler.args
|
self.args: argparse.Namespace = FtpHandler.args
|
||||||
|
self.uname = "*"
|
||||||
|
|
||||||
if PY2:
|
if PY2:
|
||||||
FTPHandler.__init__(self, conn, server, ioloop)
|
FTPHandler.__init__(self, conn, server, ioloop)
|
||||||
@@ -379,7 +381,7 @@ class FtpHandler(FTPHandler):
|
|||||||
ap,
|
ap,
|
||||||
vfs.canonical(rem),
|
vfs.canonical(rem),
|
||||||
"",
|
"",
|
||||||
self.username,
|
self.uname,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
self.cli_ip,
|
self.cli_ip,
|
||||||
@@ -408,7 +410,7 @@ class FtpHandler(FTPHandler):
|
|||||||
# print("xfer_end: {} => {}".format(ap, vp))
|
# print("xfer_end: {} => {}".format(ap, vp))
|
||||||
if vp:
|
if vp:
|
||||||
vp, fn = os.path.split(vp)
|
vp, fn = os.path.split(vp)
|
||||||
vfs, rem = self.hub.asrv.vfs.get(vp, self.username, False, True)
|
vfs, rem = self.hub.asrv.vfs.get(vp, self.uname, False, True)
|
||||||
vfs, rem = vfs.get_dbv(rem)
|
vfs, rem = vfs.get_dbv(rem)
|
||||||
self.hub.up2k.hash_file(
|
self.hub.up2k.hash_file(
|
||||||
vfs.realpath,
|
vfs.realpath,
|
||||||
@@ -418,7 +420,7 @@ class FtpHandler(FTPHandler):
|
|||||||
fn,
|
fn,
|
||||||
self.cli_ip,
|
self.cli_ip,
|
||||||
time.time(),
|
time.time(),
|
||||||
self.username,
|
self.uname,
|
||||||
)
|
)
|
||||||
|
|
||||||
return FTPHandler.log_transfer(
|
return FTPHandler.log_transfer(
|
||||||
@@ -452,7 +454,7 @@ class Ftpd(object):
|
|||||||
print(t.format(pybin))
|
print(t.format(pybin))
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
h1.certfile = os.path.join(self.args.E.cfg, "cert.pem")
|
h1.certfile = self.args.cert
|
||||||
h1.tls_control_required = True
|
h1.tls_control_required = True
|
||||||
h1.tls_data_required = True
|
h1.tls_data_required = True
|
||||||
|
|
||||||
|
|||||||
@@ -884,6 +884,7 @@ class HttpCli(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
fgen = vn.zipgen(
|
fgen = vn.zipgen(
|
||||||
|
rem,
|
||||||
rem,
|
rem,
|
||||||
set(),
|
set(),
|
||||||
self.uname,
|
self.uname,
|
||||||
@@ -1669,7 +1670,7 @@ class HttpCli(object):
|
|||||||
items = [unquotep(x) for x in items if items]
|
items = [unquotep(x) for x in items if items]
|
||||||
|
|
||||||
self.parser.drop()
|
self.parser.drop()
|
||||||
return self.tx_zip(k, v, vn, rem, items, self.args.ed)
|
return self.tx_zip(k, v, "", vn, rem, items, self.args.ed)
|
||||||
|
|
||||||
def handle_post_json(self) -> bool:
|
def handle_post_json(self) -> bool:
|
||||||
try:
|
try:
|
||||||
@@ -2656,7 +2657,14 @@ class HttpCli(object):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def tx_zip(
|
def tx_zip(
|
||||||
self, fmt: str, uarg: str, vn: VFS, rem: str, items: list[str], dots: bool
|
self,
|
||||||
|
fmt: str,
|
||||||
|
uarg: str,
|
||||||
|
vpath: str,
|
||||||
|
vn: VFS,
|
||||||
|
rem: str,
|
||||||
|
items: list[str],
|
||||||
|
dots: bool,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
if self.args.no_zip:
|
if self.args.no_zip:
|
||||||
raise Pebkac(400, "not enabled")
|
raise Pebkac(400, "not enabled")
|
||||||
@@ -2699,7 +2707,7 @@ class HttpCli(object):
|
|||||||
self.send_headers(None, mime=mime, headers={"Content-Disposition": cdis})
|
self.send_headers(None, mime=mime, headers={"Content-Disposition": cdis})
|
||||||
|
|
||||||
fgen = vn.zipgen(
|
fgen = vn.zipgen(
|
||||||
rem, set(items), self.uname, False, dots, not self.args.no_scandir
|
vpath, rem, set(items), self.uname, dots, False, not self.args.no_scandir
|
||||||
)
|
)
|
||||||
# for f in fgen: print(repr({k: f[k] for k in ["vp", "ap"]}))
|
# for f in fgen: print(repr({k: f[k] for k in ["vp", "ap"]}))
|
||||||
bgen = packer(self.log, fgen, utf8="utf" in uarg, pre_crc="crc" in uarg)
|
bgen = packer(self.log, fgen, utf8="utf" in uarg, pre_crc="crc" in uarg)
|
||||||
@@ -3503,7 +3511,7 @@ class HttpCli(object):
|
|||||||
for k in ["zip", "tar"]:
|
for k in ["zip", "tar"]:
|
||||||
v = self.uparam.get(k)
|
v = self.uparam.get(k)
|
||||||
if v is not None:
|
if v is not None:
|
||||||
return self.tx_zip(k, v, vn, rem, [], self.args.ed)
|
return self.tx_zip(k, v, self.vpath, vn, rem, [], self.args.ed)
|
||||||
|
|
||||||
fsroot, vfs_ls, vfs_virt = vn.ls(
|
fsroot, vfs_ls, vfs_virt = vn.ls(
|
||||||
rem, self.uname, not self.args.no_scandir, [[True, False], [False, True]]
|
rem, self.uname, not self.args.no_scandir, [[True, False], [False, True]]
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ class HttpSrv(object):
|
|||||||
|
|
||||||
self.ssdp = SSDPr(broker)
|
self.ssdp = SSDPr(broker)
|
||||||
|
|
||||||
cert_path = os.path.join(self.E.cfg, "cert.pem")
|
cert_path = self.args.cert
|
||||||
if bos.path.exists(cert_path):
|
if bos.path.exists(cert_path):
|
||||||
self.cert_path = cert_path
|
self.cert_path = cert_path
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -110,10 +110,21 @@
|
|||||||
|
|
||||||
<div class="os win">
|
<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>
|
<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>
|
||||||
|
{% if args.ftp %}
|
||||||
|
<p>connect with plaintext FTP:</p>
|
||||||
<pre>
|
<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 config create {{ aname }}-ftp ftp host={{ rip }} port={{ args.ftp }} pass=k user={% if accs %}<b>{{ pw }}</b>{% else %}anonymous{% endif %} tls=false
|
||||||
rclone mount --vfs-cache-mode writes --dir-cache-time 5s {{ aname }}-ftp:{{ rvp }} <b>W:</b>
|
rclone mount --vfs-cache-mode writes --dir-cache-time 5s {{ aname }}-ftp:{{ rvp }} <b>W:</b>
|
||||||
</pre>
|
</pre>
|
||||||
|
{% endif %}
|
||||||
|
{% if args.ftps %}
|
||||||
|
<p>connect with TLS-encrypted FTPS:</p>
|
||||||
|
<pre>
|
||||||
|
rclone config create {{ aname }}-ftps ftp host={{ rip }} port={{ args.ftps }} pass=k user={% if accs %}<b>{{ pw }}</b>{% else %}anonymous{% endif %} tls=false explicit_tls=true
|
||||||
|
rclone mount --vfs-cache-mode writes --dir-cache-time 5s {{ aname }}-ftps:{{ rvp }} <b>W:</b>
|
||||||
|
</pre>
|
||||||
|
<p><em>note: if you are on LAN (or just dont have valid certificates), add <code>no_check_certificate=true</code> to the config command</em><br />---</p>
|
||||||
|
{% endif %}
|
||||||
<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>
|
<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>
|
<pre>
|
||||||
explorer {{ "ftp" if args.ftp else "ftps" }}://{% if accs %}<b>{{ pw }}</b>:k@{% endif %}{{ host }}:{{ args.ftp or args.ftps }}/{{ rvp }}
|
explorer {{ "ftp" if args.ftp else "ftps" }}://{% if accs %}<b>{{ pw }}</b>:k@{% endif %}{{ host }}:{{ args.ftp or args.ftps }}/{{ rvp }}
|
||||||
@@ -121,10 +132,21 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="os lin">
|
<div class="os lin">
|
||||||
|
{% if args.ftp %}
|
||||||
|
<p>connect with plaintext FTP:</p>
|
||||||
<pre>
|
<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 config create {{ aname }}-ftp ftp host={{ rip }} port={{ args.ftp }} pass=k user={% if accs %}<b>{{ pw }}</b>{% else %}anonymous{% endif %} tls=false
|
||||||
rclone mount --vfs-cache-mode writes --dir-cache-time 5s {{ aname }}-ftp:{{ rvp }} <b>mp</b>
|
rclone mount --vfs-cache-mode writes --dir-cache-time 5s {{ aname }}-ftp:{{ rvp }} <b>mp</b>
|
||||||
</pre>
|
</pre>
|
||||||
|
{% endif %}
|
||||||
|
{% if args.ftps %}
|
||||||
|
<p>connect with TLS-encrypted FTPS:</p>
|
||||||
|
<pre>
|
||||||
|
rclone config create {{ aname }}-ftps ftp host={{ rip }} port={{ args.ftps }} pass=k user={% if accs %}<b>{{ pw }}</b>{% else %}anonymous{% endif %} tls=false explicit_tls=true
|
||||||
|
rclone mount --vfs-cache-mode writes --dir-cache-time 5s {{ aname }}-ftps:{{ rvp }} <b>mp</b>
|
||||||
|
</pre>
|
||||||
|
<p><em>note: if you are on LAN (or just dont have valid certificates), add <code>no_check_certificate=true</code> to the config command</em><br />---</p>
|
||||||
|
{% endif %}
|
||||||
<p>emergency alternative (gnome/gui-only):</p>
|
<p>emergency alternative (gnome/gui-only):</p>
|
||||||
<!-- gnome-bug: ignores vp -->
|
<!-- gnome-bug: ignores vp -->
|
||||||
<pre>
|
<pre>
|
||||||
|
|||||||
@@ -1,3 +1,33 @@
|
|||||||
|
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||||
|
# 2023-0420-2141 `v1.6.12` as seen on nixos
|
||||||
|
|
||||||
|
## new features
|
||||||
|
* @chinponya [made](https://github.com/9001/copyparty/pull/22) a copyparty [Nix package](https://github.com/9001/copyparty#nix-package) and a [NixOS module](https://github.com/9001/copyparty#nixos-module)! nice 🎉
|
||||||
|
* with [systemd-based hardening](https://github.com/9001/copyparty/blob/hovudstraum/contrib/nixos/modules/copyparty.nix#L230-L270) instead of [prisonparty](https://github.com/9001/copyparty/blob/hovudstraum/bin/prisonparty.sh)
|
||||||
|
* complements the [arch package](https://github.com/9001/copyparty/tree/hovudstraum/contrib/package/arch) very well w
|
||||||
|
|
||||||
|
## bugfixes
|
||||||
|
* fix an sqlite fd leak
|
||||||
|
* with enough simultaneous traffic, copyparty could run out of file descriptors since it relied on the gc to close sqlite cursors
|
||||||
|
* now there's a pool of cursors shared between the tcp connections instead, limited to the number of CPU cores
|
||||||
|
* performance mostly unaffected (or slightly improved) compared to before, except for a 20% reduction only during max server load caused by directory-listings or searches
|
||||||
|
* ~~somehow explicitly closing the cursors didn't always work... maybe this was actually a python bug :\\/~~
|
||||||
|
* yes, it does incomplete cleanup if opening a WAL database fails
|
||||||
|
* multirange requests would fail with an error; now they get a 200 as expected (since they're kinda useless and not worth the overhead)
|
||||||
|
* [the only software i've ever seen do that](https://apps.kde.org/discover/) now works as intended
|
||||||
|
* expand `~/` filesystem paths in all remaining args: `-c`, `-lo`, `--hist`, `--ssl-log`, and the `hist` volflag
|
||||||
|
* never use IPv6-format IPv4 (`::ffff:127.0.0.1`) in responses
|
||||||
|
* [u2cli](https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py): don't enter delete stage if some of the uploads failed
|
||||||
|
* audio player in safari on touchbar macbooks
|
||||||
|
* songs would play backwards because the touchbar keeps spamming play/pause
|
||||||
|
* playback would stop when the preloader kicks in because safari sees the new audio object and freaks out
|
||||||
|
|
||||||
|
## other changes
|
||||||
|
* added [windows quickstart / service example](https://github.com/9001/copyparty/blob/hovudstraum/docs/examples/windows.md)
|
||||||
|
* updated pyinstaller (it makes smaller exe files now)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||||
# 2023-0401-2112 `v1.6.11` not joke
|
# 2023-0401-2112 `v1.6.11` not joke
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ open up notepad and save the following as `c:\users\you\documents\party.conf` (f
|
|||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
[global]
|
[global]
|
||||||
lo: c:\users\you\logs\cpp-%Y-%m%d.xz # log to file
|
lo: ~/logs/cpp-%Y-%m%d.xz # log to c:\users\you\logs\
|
||||||
e2dsa, e2ts, no-dedup, z # sets 4 flags; see expl.
|
e2dsa, e2ts, no-dedup, z # sets 4 flags; see expl.
|
||||||
p: 80, 443 # listen on ports 80 and 443, not 3923
|
p: 80, 443 # listen on ports 80 and 443, not 3923
|
||||||
theme: 2 # default theme: protonmail-monokai
|
theme: 2 # default theme: protonmail-monokai
|
||||||
@@ -47,8 +47,7 @@ open up notepad and save the following as `c:\users\you\documents\party.conf` (f
|
|||||||
### config explained: [global]
|
### config explained: [global]
|
||||||
|
|
||||||
the `[global]` section accepts any config parameters you can see when running copyparty (either the exe or the sfx.py) with `--help`, so this is the same as running copyparty with arguments `--lo c:\users\you\logs\copyparty-%Y-%m%d.xz -e2dsa -e2ts --no-dedup -z -p 80,443 --theme 2 --lang nor`
|
the `[global]` section accepts any config parameters you can see when running copyparty (either the exe or the sfx.py) with `--help`, so this is the same as running copyparty with arguments `--lo c:\users\you\logs\copyparty-%Y-%m%d.xz -e2dsa -e2ts --no-dedup -z -p 80,443 --theme 2 --lang nor`
|
||||||
* `lo: c:\users\you\logs\cpp-%Y-%m%d.xz` writes compressed logs (the compression will make them delayed)
|
* `lo: ~/logs/cpp-%Y-%m%d.xz` writes compressed logs (the compression will make them delayed)
|
||||||
* sorry that `~/logs/` doesn't work currently, good oversight
|
|
||||||
* `e2dsa` enables the upload deduplicator and file indexer, which enables searching
|
* `e2dsa` enables the upload deduplicator and file indexer, which enables searching
|
||||||
* `e2ts` enables music metadata indexing, making albums / titles etc. searchable too
|
* `e2ts` enables music metadata indexing, making albums / titles etc. searchable too
|
||||||
* `no-dedup` writes full dupes to disk instead of symlinking, since lots of windows software doesn't handle symlinks well
|
* `no-dedup` writes full dupes to disk instead of symlinking, since lots of windows software doesn't handle symlinks well
|
||||||
|
|||||||
2
docs/protocol-reference.sh
Normal file
2
docs/protocol-reference.sh
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
vsftpd a.conf -olisten=YES -olisten_port=3921 -orun_as_launching_user=YES -obackground=NO -olog_ftp_protocol=YES
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ class TestHttpCli(unittest.TestCase):
|
|||||||
tar = tarfile.open(fileobj=io.BytesIO(b)).getnames()
|
tar = tarfile.open(fileobj=io.BytesIO(b)).getnames()
|
||||||
except:
|
except:
|
||||||
tar = []
|
tar = []
|
||||||
tar = [x[4:] if x.startswith("top/") else x for x in tar]
|
tar = [x.split("/", 1)[1] for x in tar]
|
||||||
tar = ["/".join([y for y in [top, durl, x] if y]) for x in tar]
|
tar = ["/".join([y for y in [top, durl, x] if y]) for x in tar]
|
||||||
tar = [[x] + self.can_rw(x) for x in tar]
|
tar = [[x] + self.can_rw(x) for x in tar]
|
||||||
tar_ok = [x[0] for x in tar if x[1]]
|
tar_ok = [x[0] for x in tar if x[1]]
|
||||||
|
|||||||
Reference in New Issue
Block a user