Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b97a1088fa | ||
|
|
b77bed3324 | ||
|
|
a2b7c85a1f | ||
|
|
b28533f850 | ||
|
|
bd8c7e538a | ||
|
|
89e48cff24 | ||
|
|
ae90a7b7b6 | ||
|
|
6fc1be04da | ||
|
|
0061d29534 | ||
|
|
a891f34a93 | ||
|
|
d6a1e62a95 | ||
|
|
cda36ea8b4 | ||
|
|
909a76434a | ||
|
|
39348ef659 |
10
README.md
10
README.md
@@ -68,12 +68,16 @@ you may also want these, especially on servers:
|
|||||||
|
|
||||||
## notes
|
## notes
|
||||||
|
|
||||||
|
general:
|
||||||
|
* paper-printing is affected by dark/light-mode! use lightmode for color, darkmode for grayscale
|
||||||
|
* because no browsers currently implement the media-query to do this properly orz
|
||||||
|
|
||||||
|
browser-specific:
|
||||||
* iPhone/iPad: use Firefox to download files
|
* iPhone/iPad: use Firefox to download files
|
||||||
* Android-Chrome: increase "parallel uploads" for higher speed (android bug)
|
* Android-Chrome: increase "parallel uploads" for higher speed (android bug)
|
||||||
* Android-Firefox: takes a while to select files (their fix for ☝️)
|
* Android-Firefox: takes a while to select files (their fix for ☝️)
|
||||||
* Desktop-Firefox: ~~may use gigabytes of RAM if your files are massive~~ *seems to be OK now*
|
* Desktop-Firefox: ~~may use gigabytes of RAM if your files are massive~~ *seems to be OK now*
|
||||||
* paper-printing is affected by dark/light-mode! use lightmode for color, darkmode for grayscale
|
* Desktop-Firefox: may stop you from deleting folders you've uploaded until you visit `about:memory` and click `Minimize memory usage`
|
||||||
* because no browsers currently implement the media-query to do this properly orz
|
|
||||||
|
|
||||||
|
|
||||||
## status
|
## status
|
||||||
@@ -397,6 +401,8 @@ quick summary of more eccentric web-browsers trying to view a directory index:
|
|||||||
* cross-platform python client available in [./bin/](bin/)
|
* cross-platform python client available in [./bin/](bin/)
|
||||||
* [rclone](https://rclone.org/) as client can give ~5x performance, see [./docs/rclone.md](docs/rclone.md)
|
* [rclone](https://rclone.org/) as client can give ~5x performance, see [./docs/rclone.md](docs/rclone.md)
|
||||||
|
|
||||||
|
* sharex (screenshot utility): see [./contrib/sharex.sxcu](contrib/#sharexsxcu)
|
||||||
|
|
||||||
copyparty returns a truncated sha512sum of your PUT/POST as base64; you can generate the same checksum locally to verify uplaods:
|
copyparty returns a truncated sha512sum of your PUT/POST as base64; you can generate the same checksum locally to verify uplaods:
|
||||||
|
|
||||||
b512(){ printf "$((sha512sum||shasum -a512)|sed -E 's/ .*//;s/(..)/\\x\1/g')"|base64|head -c43;}
|
b512(){ printf "$((sha512sum||shasum -a512)|sed -E 's/ .*//;s/(..)/\\x\1/g')"|base64|head -c43;}
|
||||||
|
|||||||
@@ -9,6 +9,16 @@
|
|||||||
* assumes the webserver and copyparty is running on the same server/IP
|
* assumes the webserver and copyparty is running on the same server/IP
|
||||||
* modify `10.13.1.1` as necessary if you wish to support browsers without javascript
|
* modify `10.13.1.1` as necessary if you wish to support browsers without javascript
|
||||||
|
|
||||||
|
### [`sharex.sxcu`](sharex.sxcu)
|
||||||
|
* sharex config file to upload screenshots and grab the URL
|
||||||
|
* `RequestURL`: full URL to the target folder
|
||||||
|
* `pw`: password (remove the `pw` line if anon-write)
|
||||||
|
|
||||||
|
however if your copyparty is behind a reverse-proxy, you may want to use [`sharex-html.sxcu`](sharex-html.sxcu) instead:
|
||||||
|
* `RequestURL`: full URL to the target folder
|
||||||
|
* `URL`: full URL to the root folder (with trailing slash) followed by `$regex:1|1$`
|
||||||
|
* `pw`: password (remove `Parameters` if anon-write)
|
||||||
|
|
||||||
### [`explorer-nothumbs-nofoldertypes.reg`](explorer-nothumbs-nofoldertypes.reg)
|
### [`explorer-nothumbs-nofoldertypes.reg`](explorer-nothumbs-nofoldertypes.reg)
|
||||||
* disables thumbnails and folder-type detection in windows explorer
|
* disables thumbnails and folder-type detection in windows explorer
|
||||||
* makes it way faster (especially for slow/networked locations (such as copyparty-fuse))
|
* makes it way faster (especially for slow/networked locations (such as copyparty-fuse))
|
||||||
|
|||||||
19
contrib/sharex-html.sxcu
Normal file
19
contrib/sharex-html.sxcu
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"Version": "13.5.0",
|
||||||
|
"Name": "copyparty-html",
|
||||||
|
"DestinationType": "ImageUploader",
|
||||||
|
"RequestMethod": "POST",
|
||||||
|
"RequestURL": "http://127.0.0.1:3923/sharex",
|
||||||
|
"Parameters": {
|
||||||
|
"pw": "wark"
|
||||||
|
},
|
||||||
|
"Body": "MultipartFormData",
|
||||||
|
"Arguments": {
|
||||||
|
"act": "bput"
|
||||||
|
},
|
||||||
|
"FileFormName": "f",
|
||||||
|
"RegexList": [
|
||||||
|
"bytes // <a href=\"/([^\"]+)\""
|
||||||
|
],
|
||||||
|
"URL": "http://127.0.0.1:3923/$regex:1|1$"
|
||||||
|
}
|
||||||
17
contrib/sharex.sxcu
Normal file
17
contrib/sharex.sxcu
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"Version": "13.5.0",
|
||||||
|
"Name": "copyparty",
|
||||||
|
"DestinationType": "ImageUploader",
|
||||||
|
"RequestMethod": "POST",
|
||||||
|
"RequestURL": "http://127.0.0.1:3923/sharex",
|
||||||
|
"Parameters": {
|
||||||
|
"pw": "wark",
|
||||||
|
"j": null
|
||||||
|
},
|
||||||
|
"Body": "MultipartFormData",
|
||||||
|
"Arguments": {
|
||||||
|
"act": "bput"
|
||||||
|
},
|
||||||
|
"FileFormName": "f",
|
||||||
|
"URL": "$json:files[0].url$"
|
||||||
|
}
|
||||||
@@ -238,6 +238,7 @@ def run_argparse(argv, formatter):
|
|||||||
--ls '**' # list all files which are possible to read
|
--ls '**' # list all files which are possible to read
|
||||||
--ls '**,*,ln' # check for dangerous symlinks
|
--ls '**,*,ln' # check for dangerous symlinks
|
||||||
--ls '**,*,ln,p,r' # check, then start normally if safe
|
--ls '**,*,ln,p,r' # check, then start normally if safe
|
||||||
|
\033[0m
|
||||||
"""
|
"""
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -377,6 +378,9 @@ def main(argv=None):
|
|||||||
+ " (if you crash with codec errors then that is why)"
|
+ " (if you crash with codec errors then that is why)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if WINDOWS and sys.version_info < (3, 6):
|
||||||
|
al.no_scandir = True
|
||||||
|
|
||||||
# signal.signal(signal.SIGINT, sighandler)
|
# signal.signal(signal.SIGINT, sighandler)
|
||||||
|
|
||||||
SvcHub(al).run()
|
SvcHub(al).run()
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
VERSION = (0, 11, 7)
|
VERSION = (0, 11, 10)
|
||||||
CODENAME = "the grid"
|
CODENAME = "the grid"
|
||||||
BUILD_DT = (2021, 6, 5)
|
BUILD_DT = (2021, 6, 8)
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import sys
|
|||||||
import stat
|
import stat
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
from .__init__ import PY2, WINDOWS
|
from .__init__ import WINDOWS
|
||||||
from .util import IMPLICATIONS, undot, Pebkac, fsdec, fsenc, statdir, nuprint
|
from .util import IMPLICATIONS, undot, Pebkac, fsdec, fsenc, statdir, nuprint
|
||||||
|
|
||||||
|
|
||||||
@@ -96,6 +96,7 @@ class VFS(object):
|
|||||||
]
|
]
|
||||||
|
|
||||||
def get(self, vpath, uname, will_read, will_write):
|
def get(self, vpath, uname, will_read, will_write):
|
||||||
|
# type: (str, str, bool, bool) -> tuple[VFS, str]
|
||||||
"""returns [vfsnode,fs_remainder] if user has the requested permissions"""
|
"""returns [vfsnode,fs_remainder] if user has the requested permissions"""
|
||||||
vn, rem = self._find(vpath)
|
vn, rem = self._find(vpath)
|
||||||
|
|
||||||
@@ -136,6 +137,7 @@ class VFS(object):
|
|||||||
return os.path.realpath(rp)
|
return os.path.realpath(rp)
|
||||||
|
|
||||||
def ls(self, rem, uname, scandir, incl_wo=False, lstat=False):
|
def ls(self, rem, uname, scandir, incl_wo=False, lstat=False):
|
||||||
|
# type: (str, str, bool, bool, bool) -> tuple[str, str, dict[str, VFS]]
|
||||||
"""return user-readable [fsdir,real,virt] items at vpath"""
|
"""return user-readable [fsdir,real,virt] items at vpath"""
|
||||||
virt_vis = {} # nodes readable by user
|
virt_vis = {} # nodes readable by user
|
||||||
abspath = self.canonical(rem)
|
abspath = self.canonical(rem)
|
||||||
@@ -156,13 +158,21 @@ class VFS(object):
|
|||||||
|
|
||||||
return [abspath, real, virt_vis]
|
return [abspath, real, virt_vis]
|
||||||
|
|
||||||
def walk(self, rel, rem, uname, dots, scandir, lstat=False):
|
def walk(self, rel, rem, seen, uname, dots, scandir, lstat):
|
||||||
"""
|
"""
|
||||||
recursively yields from ./rem;
|
recursively yields from ./rem;
|
||||||
rel is a unix-style user-defined vpath (not vfs-related)
|
rel is a unix-style user-defined vpath (not vfs-related)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
fsroot, vfs_ls, vfs_virt = self.ls(rem, uname, scandir, False, lstat)
|
fsroot, vfs_ls, vfs_virt = self.ls(
|
||||||
|
rem, uname, scandir, incl_wo=False, lstat=lstat
|
||||||
|
)
|
||||||
|
|
||||||
|
if seen and not fsroot.startswith(seen[-1]) and fsroot in seen:
|
||||||
|
print("bailing from symlink loop,\n {}\n {}".format(seen[-1], fsroot))
|
||||||
|
return
|
||||||
|
|
||||||
|
seen = seen[:] + [fsroot]
|
||||||
rfiles = [x for x in vfs_ls if not stat.S_ISDIR(x[1].st_mode)]
|
rfiles = [x for x in vfs_ls if not stat.S_ISDIR(x[1].st_mode)]
|
||||||
rdirs = [x for x in vfs_ls if stat.S_ISDIR(x[1].st_mode)]
|
rdirs = [x for x in vfs_ls if stat.S_ISDIR(x[1].st_mode)]
|
||||||
|
|
||||||
@@ -177,7 +187,7 @@ class VFS(object):
|
|||||||
|
|
||||||
wrel = (rel + "/" + rdir).lstrip("/")
|
wrel = (rel + "/" + rdir).lstrip("/")
|
||||||
wrem = (rem + "/" + rdir).lstrip("/")
|
wrem = (rem + "/" + rdir).lstrip("/")
|
||||||
for x in self.walk(wrel, wrem, uname, scandir, lstat):
|
for x in self.walk(wrel, wrem, seen, uname, dots, scandir, lstat):
|
||||||
yield x
|
yield x
|
||||||
|
|
||||||
for n, vfs in sorted(vfs_virt.items()):
|
for n, vfs in sorted(vfs_virt.items()):
|
||||||
@@ -185,14 +195,16 @@ class VFS(object):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
wrel = (rel + "/" + n).lstrip("/")
|
wrel = (rel + "/" + n).lstrip("/")
|
||||||
for x in vfs.walk(wrel, "", uname, scandir, lstat):
|
for x in vfs.walk(wrel, "", seen, uname, dots, scandir, lstat):
|
||||||
yield x
|
yield x
|
||||||
|
|
||||||
def zipgen(self, vrem, flt, uname, dots, scandir):
|
def zipgen(self, vrem, flt, uname, dots, scandir):
|
||||||
if flt:
|
if flt:
|
||||||
flt = {k: True for k in flt}
|
flt = {k: True for k in flt}
|
||||||
|
|
||||||
for vpath, apath, files, rd, vd in self.walk("", vrem, uname, dots, scandir):
|
for vpath, apath, files, rd, vd in self.walk(
|
||||||
|
"", vrem, [], uname, dots, scandir, False
|
||||||
|
):
|
||||||
if flt:
|
if flt:
|
||||||
files = [x for x in files if x[0] in flt]
|
files = [x for x in files if x[0] in flt]
|
||||||
|
|
||||||
@@ -616,13 +628,13 @@ class AuthSrv(object):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
atop = vn.realpath
|
atop = vn.realpath
|
||||||
g = vn.walk("", "", u, True, not self.args.no_scandir, lstat=False)
|
g = vn.walk("", "", [], u, True, not self.args.no_scandir, False)
|
||||||
for vpath, apath, files, _, _ in g:
|
for vpath, apath, files, _, _ in g:
|
||||||
fnames = [n[0] for n in files]
|
fnames = [n[0] for n in files]
|
||||||
vpaths = [vpath + "/" + n for n in fnames] if vpath else fnames
|
vpaths = [vpath + "/" + n for n in fnames] if vpath else fnames
|
||||||
vpaths = [vtop + x for x in vpaths]
|
vpaths = [vtop + x for x in vpaths]
|
||||||
apaths = [os.path.join(apath, n) for n in fnames]
|
apaths = [os.path.join(apath, n) for n in fnames]
|
||||||
files = list(zip(vpaths, apaths))
|
files = [[vpath + "/", apath + os.sep]] + list(zip(vpaths, apaths))
|
||||||
|
|
||||||
if flag_ln:
|
if flag_ln:
|
||||||
files = [x for x in files if not x[1].startswith(atop + os.sep)]
|
files = [x for x in files if not x[1].startswith(atop + os.sep)]
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import calendar
|
|||||||
|
|
||||||
from .__init__ import E, PY2, WINDOWS, ANYWIN
|
from .__init__ import E, PY2, WINDOWS, ANYWIN
|
||||||
from .util import * # noqa # pylint: disable=unused-wildcard-import
|
from .util import * # noqa # pylint: disable=unused-wildcard-import
|
||||||
|
from .authsrv import AuthSrv
|
||||||
from .szip import StreamZip
|
from .szip import StreamZip
|
||||||
from .star import StreamTar
|
from .star import StreamTar
|
||||||
|
|
||||||
@@ -35,12 +36,12 @@ class HttpCli(object):
|
|||||||
def __init__(self, conn):
|
def __init__(self, conn):
|
||||||
self.t0 = time.time()
|
self.t0 = time.time()
|
||||||
self.conn = conn
|
self.conn = conn
|
||||||
self.s = conn.s
|
self.s = conn.s # type: socket
|
||||||
self.sr = conn.sr
|
self.sr = conn.sr # type: Unrecv
|
||||||
self.ip = conn.addr[0]
|
self.ip = conn.addr[0]
|
||||||
self.addr = conn.addr
|
self.addr = conn.addr # type: tuple[str, int]
|
||||||
self.args = conn.args
|
self.args = conn.args
|
||||||
self.auth = conn.auth
|
self.auth = conn.auth # type: AuthSrv
|
||||||
self.ico = conn.ico
|
self.ico = conn.ico
|
||||||
self.thumbcli = conn.thumbcli
|
self.thumbcli = conn.thumbcli
|
||||||
self.log_func = conn.log_func
|
self.log_func = conn.log_func
|
||||||
@@ -506,6 +507,7 @@ class HttpCli(object):
|
|||||||
items = items.replace("\r", "").split("\n")
|
items = items.replace("\r", "").split("\n")
|
||||||
items = [unquotep(x) for x in items if items]
|
items = [unquotep(x) for x in items if items]
|
||||||
|
|
||||||
|
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):
|
def handle_post_json(self):
|
||||||
@@ -849,14 +851,28 @@ class HttpCli(object):
|
|||||||
status = "ERROR"
|
status = "ERROR"
|
||||||
|
|
||||||
msg = "{} // {} bytes // {:.3f} MiB/s\n".format(status, sz_total, spd)
|
msg = "{} // {} bytes // {:.3f} MiB/s\n".format(status, sz_total, spd)
|
||||||
|
jmsg = {"status": status, "sz": sz_total, "mbps": round(spd, 3), "files": []}
|
||||||
|
|
||||||
for sz, sha512, ofn, lfn in files:
|
for sz, sha512, ofn, lfn in files:
|
||||||
vpath = self.vpath + "/" + lfn
|
vpath = (self.vpath + "/" if self.vpath else "") + lfn
|
||||||
msg += 'sha512: {} // {} bytes // <a href="/{}">{}</a>\n'.format(
|
msg += 'sha512: {} // {} bytes // <a href="/{}">{}</a>\n'.format(
|
||||||
sha512[:56], sz, quotep(vpath), html_escape(ofn, crlf=True)
|
sha512[:56], sz, quotep(vpath), html_escape(ofn, crlf=True)
|
||||||
)
|
)
|
||||||
# truncated SHA-512 prevents length extension attacks;
|
# truncated SHA-512 prevents length extension attacks;
|
||||||
# using SHA-512/224, optionally SHA-512/256 = :64
|
# using SHA-512/224, optionally SHA-512/256 = :64
|
||||||
|
jpart = {
|
||||||
|
"url": "{}://{}/{}".format(
|
||||||
|
"https" if self.tls else "http",
|
||||||
|
self.headers.get("host", "copyparty"),
|
||||||
|
vpath,
|
||||||
|
),
|
||||||
|
"sha512": sha512[:56],
|
||||||
|
"sz": sz,
|
||||||
|
"fn": lfn,
|
||||||
|
"fn_orig": ofn,
|
||||||
|
"path": vpath,
|
||||||
|
}
|
||||||
|
jmsg["files"].append(jpart)
|
||||||
|
|
||||||
vspd = self._spd(sz_total, False)
|
vspd = self._spd(sz_total, False)
|
||||||
self.log("{} {}".format(vspd, msg))
|
self.log("{} {}".format(vspd, msg))
|
||||||
@@ -868,7 +884,12 @@ class HttpCli(object):
|
|||||||
ft = "{}\n{}\n{}\n".format(ft, msg.rstrip(), errmsg)
|
ft = "{}\n{}\n{}\n".format(ft, msg.rstrip(), errmsg)
|
||||||
f.write(ft.encode("utf-8"))
|
f.write(ft.encode("utf-8"))
|
||||||
|
|
||||||
|
if "j" in self.uparam:
|
||||||
|
jtxt = json.dumps(jmsg, indent=2, sort_keys=True)
|
||||||
|
self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
|
||||||
|
else:
|
||||||
self.redirect(self.vpath, msg=msg, flavor="return to", click=False)
|
self.redirect(self.vpath, msg=msg, flavor="return to", click=False)
|
||||||
|
|
||||||
self.parser.drop()
|
self.parser.drop()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -1399,7 +1420,7 @@ class HttpCli(object):
|
|||||||
try:
|
try:
|
||||||
vn, rem = self.auth.vfs.get(top, self.uname, True, False)
|
vn, rem = self.auth.vfs.get(top, self.uname, True, False)
|
||||||
fsroot, vfs_ls, vfs_virt = vn.ls(
|
fsroot, vfs_ls, vfs_virt = vn.ls(
|
||||||
rem, self.uname, not self.args.no_scandir, True
|
rem, self.uname, not self.args.no_scandir, incl_wo=True
|
||||||
)
|
)
|
||||||
except:
|
except:
|
||||||
vfs_ls = []
|
vfs_ls = []
|
||||||
@@ -1566,7 +1587,7 @@ class HttpCli(object):
|
|||||||
return self.tx_zip(k, v, vn, rem, [], self.args.ed)
|
return self.tx_zip(k, v, 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
|
rem, self.uname, not self.args.no_scandir, incl_wo=True
|
||||||
)
|
)
|
||||||
stats = {k: v for k, v in vfs_ls}
|
stats = {k: v for k, v in vfs_ls}
|
||||||
vfs_ls = [x[0] for x in vfs_ls]
|
vfs_ls = [x[0] for x in vfs_ls]
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ except ImportError:
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
from .__init__ import E, MACOS
|
from .__init__ import E, MACOS
|
||||||
from .httpconn import HttpConn
|
|
||||||
from .authsrv import AuthSrv
|
from .authsrv import AuthSrv
|
||||||
|
from .httpconn import HttpConn
|
||||||
|
|
||||||
|
|
||||||
class HttpSrv(object):
|
class HttpSrv(object):
|
||||||
@@ -99,6 +99,7 @@ class HttpSrv(object):
|
|||||||
cli.run()
|
cli.run()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
|
sck = cli.s
|
||||||
if self.args.log_conn:
|
if self.args.log_conn:
|
||||||
self.log("%s %s" % addr, "|%sC-cdone" % ("-" * 7,), c="1;30")
|
self.log("%s %s" % addr, "|%sC-cdone" % ("-" * 7,), c="1;30")
|
||||||
|
|
||||||
|
|||||||
@@ -95,9 +95,11 @@ class SvcHub(object):
|
|||||||
break
|
break
|
||||||
|
|
||||||
if n == 3:
|
if n == 3:
|
||||||
print("waiting for thumbsrv...")
|
print("waiting for thumbsrv (10sec)...")
|
||||||
|
|
||||||
print("nailed it")
|
print("nailed it", end="")
|
||||||
|
finally:
|
||||||
|
print("\033[0m")
|
||||||
|
|
||||||
def _log_disabled(self, src, msg, c=0):
|
def _log_disabled(self, src, msg, c=0):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ class ThumbSrv(object):
|
|||||||
|
|
||||||
msg = "cannot create video thumbnails because some of the required programs are not available: "
|
msg = "cannot create video thumbnails because some of the required programs are not available: "
|
||||||
msg += ", ".join(missing)
|
msg += ", ".join(missing)
|
||||||
self.log(msg, c=1)
|
self.log(msg, c=3)
|
||||||
|
|
||||||
t = threading.Thread(target=self.cleaner)
|
t = threading.Thread(target=self.cleaner)
|
||||||
t.daemon = True
|
t.daemon = True
|
||||||
|
|||||||
@@ -262,6 +262,11 @@ def ren_open(fname, *args, **kwargs):
|
|||||||
yield {"orz": [f, fname]}
|
yield {"orz": [f, fname]}
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if suffix:
|
||||||
|
ext = fname.split(".")[-1]
|
||||||
|
if len(ext) < 7:
|
||||||
|
suffix += "." + ext
|
||||||
|
|
||||||
orig_name = fname
|
orig_name = fname
|
||||||
bname = fname
|
bname = fname
|
||||||
ext = ""
|
ext = ""
|
||||||
|
|||||||
@@ -153,6 +153,9 @@ dbg.asyncStore.pendingBreakpoints = {}
|
|||||||
# fix firefox phantom breakpoints
|
# fix firefox phantom breakpoints
|
||||||
about:config >> devtools.debugger.prefs-schema-version = -1
|
about:config >> devtools.debugger.prefs-schema-version = -1
|
||||||
|
|
||||||
|
# determine server version
|
||||||
|
git reset --hard origin/HEAD && git log --format=format:"%H %ai %d" --decorate=full > /dev/shm/revs && cat /dev/shm/revs | while read -r rev extra; do (git reset --hard $rev >/dev/null 2>/dev/null && dsz=$(cat copyparty/web/{util,browser,up2k}.js 2>/dev/null | diff -wNarU0 - <(cat /mnt/Users/ed/Downloads/ref/{util,browser,up2k}.js) | wc -c) && printf '%s %6s %s\n' "$rev" $dsz "$extra") </dev/null; done
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
## http 206
|
## http 206
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ for d in copyparty dep-j2; do find $d -type f; done |
|
|||||||
sed -r 's/(.*)\.(.*)/\2 \1/' | LC_ALL=C sort |
|
sed -r 's/(.*)\.(.*)/\2 \1/' | LC_ALL=C sort |
|
||||||
sed -r 's/([^ ]*) (.*)/\2.\1/' | grep -vE '/list1?$' > list1
|
sed -r 's/([^ ]*) (.*)/\2.\1/' | grep -vE '/list1?$' > list1
|
||||||
|
|
||||||
(grep -vE 'gz$' list1; grep -E 'gz$' list1) >list
|
(grep -vE '\.(gz|br)$' list1; grep -E '\.(gz|br)$' list1) >list || true
|
||||||
|
|
||||||
echo creating tar
|
echo creating tar
|
||||||
args=(--owner=1000 --group=1000)
|
args=(--owner=1000 --group=1000)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ grep -E '/(python|pypy)[0-9\.-]*$' >$dir/pys || true
|
|||||||
printf '\033[1;30mlooking for jinja2 in [%s]\033[0m\n' "$_py" >&2
|
printf '\033[1;30mlooking for jinja2 in [%s]\033[0m\n' "$_py" >&2
|
||||||
$_py -c 'import jinja2' 2>/dev/null || continue
|
$_py -c 'import jinja2' 2>/dev/null || continue
|
||||||
printf '%s\n' "$_py"
|
printf '%s\n' "$_py"
|
||||||
mv $dir/{,x.}jinja2
|
mv $dir/{,x.}dep-j2
|
||||||
break
|
break
|
||||||
done)"
|
done)"
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from textwrap import dedent
|
|||||||
from argparse import Namespace
|
from argparse import Namespace
|
||||||
|
|
||||||
from tests import util as tu
|
from tests import util as tu
|
||||||
from copyparty.authsrv import AuthSrv
|
from copyparty.authsrv import AuthSrv, VFS
|
||||||
from copyparty import util
|
from copyparty import util
|
||||||
|
|
||||||
|
|
||||||
@@ -47,6 +47,7 @@ class TestVFS(unittest.TestCase):
|
|||||||
self.assertEqual(util.undot(query), response)
|
self.assertEqual(util.undot(query), response)
|
||||||
|
|
||||||
def ls(self, vfs, vpath, uname):
|
def ls(self, vfs, vpath, uname):
|
||||||
|
# type: (VFS, str, str) -> tuple[str, str, str]
|
||||||
"""helper for resolving and listing a folder"""
|
"""helper for resolving and listing a folder"""
|
||||||
vn, rem = vfs.get(vpath, uname, True, False)
|
vn, rem = vfs.get(vpath, uname, True, False)
|
||||||
r1 = vn.ls(rem, uname, False)
|
r1 = vn.ls(rem, uname, False)
|
||||||
|
|||||||
Reference in New Issue
Block a user