mirror of
https://github.com/9001/copyparty.git
synced 2025-10-23 16:14:10 +00:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
08f4695283 | ||
|
44e76d5eeb | ||
|
cfa36fd279 | ||
|
3d4166e006 | ||
|
07bac1c592 | ||
|
755f2ce1ba | ||
|
cca2844deb | ||
|
24a2f760b7 | ||
|
79bbd8fe38 | ||
|
35dce1e3e4 | ||
|
f886fdf913 |
14
README.md
14
README.md
@@ -127,6 +127,7 @@ summary: all planned features work! now please enjoy the bloatening
|
||||
* ☑ basic: plain multipart, ie6 support
|
||||
* ☑ up2k: js, resumable, multithreaded
|
||||
* ☑ stash: simple PUT filedropper
|
||||
* ☑ unpost: undo/delete accidental uploads
|
||||
* ☑ symlink/discard existing files (content-matching)
|
||||
* download
|
||||
* ☑ single files in browser
|
||||
@@ -215,10 +216,11 @@ example:
|
||||
## tabs
|
||||
|
||||
* `[🔎]` search by size, date, path/name, mp3-tags ... see [searching](#searching)
|
||||
* `[🧯]` unpost: undo/delete accidental uploads
|
||||
* `[🚀]` and `[🎈]` are the uploaders, see [uploading](#uploading)
|
||||
* `[📂]` mkdir, create directories
|
||||
* `[📝]` new-md, create a new markdown document
|
||||
* `[📟]` send-msg, either to server-log or into textfiles if `--urlform save`
|
||||
* `[📂]` mkdir: create directories
|
||||
* `[📝]` new-md: create a new markdown document
|
||||
* `[📟]` send-msg: either to server-log or into textfiles if `--urlform save`
|
||||
* `[🎺]` audio-player config options
|
||||
* `[⚙️]` general client config options
|
||||
|
||||
@@ -312,8 +314,10 @@ you can also zip a selection of files or folders by clicking them in the browser
|
||||
## uploading
|
||||
|
||||
two upload methods are available in the html client:
|
||||
* `🎈 bup`, the basic uploader, supports almost every browser since netscape 4.0
|
||||
* `🚀 up2k`, the fancy one
|
||||
* `[🎈] bup`, the basic uploader, supports almost every browser since netscape 4.0
|
||||
* `[🚀] up2k`, the fancy one
|
||||
|
||||
you can undo/delete uploads using `[🧯] unpost` if the server is running with `-e2d`
|
||||
|
||||
up2k has several advantages:
|
||||
* you can drop folders into the browser (files are added recursively)
|
||||
|
@@ -13,6 +13,10 @@
|
||||
# But note that journalctl will get the timestamps wrong due to
|
||||
# python disabling line-buffering, so messages are out-of-order:
|
||||
# https://user-images.githubusercontent.com/241032/126040249-cb535cc7-c599-4931-a796-a5d9af691bad.png
|
||||
#
|
||||
# enable line-buffering for realtime logging (slight performance cost):
|
||||
# modify ExecStart and prefix it with `/bin/stdbuf -oL` like so:
|
||||
# ExecStart=/bin/stdbuf -oL /usr/bin/python3 [...]
|
||||
|
||||
[Unit]
|
||||
Description=copyparty file server
|
||||
|
@@ -1,8 +1,8 @@
|
||||
# coding: utf-8
|
||||
|
||||
VERSION = (0, 12, 3)
|
||||
VERSION = (0, 12, 6)
|
||||
CODENAME = "fil\033[33med"
|
||||
BUILD_DT = (2021, 7, 30)
|
||||
BUILD_DT = (2021, 7, 31)
|
||||
|
||||
S_VERSION = ".".join(map(str, VERSION))
|
||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||
|
@@ -401,17 +401,18 @@ class AuthSrv(object):
|
||||
if uname == "":
|
||||
uname = "*"
|
||||
|
||||
if "r" in lvl:
|
||||
axs.uread[uname] = 1
|
||||
for un in uname.split(","):
|
||||
if "r" in lvl:
|
||||
axs.uread[un] = 1
|
||||
|
||||
if "w" in lvl:
|
||||
axs.uwrite[uname] = 1
|
||||
if "w" in lvl:
|
||||
axs.uwrite[un] = 1
|
||||
|
||||
if "m" in lvl:
|
||||
axs.umove[uname] = 1
|
||||
if "m" in lvl:
|
||||
axs.umove[un] = 1
|
||||
|
||||
if "d" in lvl:
|
||||
axs.udel[uname] = 1
|
||||
if "d" in lvl:
|
||||
axs.udel[un] = 1
|
||||
|
||||
def _read_volflag(self, flags, name, value, is_list):
|
||||
if name not in ["mtp"]:
|
||||
|
@@ -182,7 +182,7 @@ class HttpCli(object):
|
||||
|
||||
self.uparam = uparam
|
||||
self.cookies = cookies
|
||||
self.vpath = unquotep(vpath)
|
||||
self.vpath = unquotep(vpath) # not query, so + means +
|
||||
|
||||
pwd = uparam.get("pw")
|
||||
self.uname = self.asrv.iacct.get(pwd, "*")
|
||||
@@ -1310,11 +1310,9 @@ class HttpCli(object):
|
||||
else:
|
||||
fn = self.headers.get("host", "hey")
|
||||
|
||||
afn = "".join(
|
||||
[x if x in (string.ascii_letters + string.digits) else "_" for x in fn]
|
||||
)
|
||||
|
||||
bascii = unicode(string.ascii_letters + string.digits).encode("utf-8")
|
||||
safe = (string.ascii_letters + string.digits).replace("%", "")
|
||||
afn = "".join([x if x in safe.replace('"', "") else "_" for x in fn])
|
||||
bascii = unicode(safe).encode("utf-8")
|
||||
ufn = fn.encode("utf-8", "xmlcharrefreplace")
|
||||
if PY2:
|
||||
ufn = [unicode(x) if x in bascii else "%{:02x}".format(ord(x)) for x in ufn]
|
||||
@@ -1329,6 +1327,7 @@ class HttpCli(object):
|
||||
|
||||
cdis = "attachment; filename=\"{}.{}\"; filename*=UTF-8''{}.{}"
|
||||
cdis = cdis.format(afn, fmt, ufn, fmt)
|
||||
self.log(cdis)
|
||||
self.send_headers(None, mime=mime, headers={"Content-Disposition": cdis})
|
||||
|
||||
fgen = vn.zipgen(rem, items, self.uname, dots, not self.args.no_scandir)
|
||||
@@ -1621,6 +1620,9 @@ class HttpCli(object):
|
||||
if not dst:
|
||||
raise Pebkac(400, "need dst vpath")
|
||||
|
||||
# x-www-form-urlencoded (url query part) uses
|
||||
# either + or %20 for 0x20 so handle both
|
||||
dst = unquotep(dst.replace("+", " "))
|
||||
x = self.conn.hsrv.broker.put(
|
||||
True, "up2k.handle_mv", self.uname, self.vpath, dst
|
||||
)
|
||||
|
@@ -26,6 +26,9 @@ class ThumbCli(object):
|
||||
if is_vid and self.args.no_vthumb:
|
||||
return None
|
||||
|
||||
if rem.startswith(".hist/th/") and rem.split(".")[-1] in ["webp", "jpg"]:
|
||||
return os.path.join(ptop, rem)
|
||||
|
||||
if fmt == "j" and self.args.th_no_jpg:
|
||||
fmt = "w"
|
||||
|
||||
|
@@ -49,7 +49,7 @@ pre, code, tt {
|
||||
transition: opacity 0.14s, height 0.14s, padding 0.14s;
|
||||
}
|
||||
#toast {
|
||||
top: 1.4em;
|
||||
bottom: 5em;
|
||||
right: -1em;
|
||||
line-height: 1.5em;
|
||||
padding: 1em 1.3em;
|
||||
|
@@ -133,6 +133,7 @@ ebi('op_cfg').innerHTML = (
|
||||
' <div>\n' +
|
||||
' <a id="tooltips" class="tgl btn" href="#" tt="◔ ◡ ◔">ℹ️ tooltips</a>\n' +
|
||||
' <a id="lightmode" class="tgl btn" href="#">☀️ lightmode</a>\n' +
|
||||
' <a id="dotfiles" class="tgl btn" href="#" tt="show hidden files (if server permits)">dotfiles</a>\n' +
|
||||
' <a id="griden" class="tgl btn" href="#" tt="toggle icons or list-view$NHotkey: G">田 the grid</a>\n' +
|
||||
' <a id="thumbs" class="tgl btn" href="#" tt="in icon view, toggle icons or thumbnails$NHotkey: T">🖼️ thumbs</a>\n' +
|
||||
' </div>\n' +
|
||||
@@ -1468,10 +1469,10 @@ var fileman = (function () {
|
||||
if (r.clip === null)
|
||||
r.clip = jread('fman_clip', []);
|
||||
|
||||
var sel = msel.getsel();
|
||||
clmod(bren, 'en', sel.length == 1);
|
||||
clmod(bdel, 'en', sel.length);
|
||||
clmod(bcut, 'en', sel.length);
|
||||
var nsel = msel.getsel().length;
|
||||
clmod(bren, 'en', nsel == 1);
|
||||
clmod(bdel, 'en', nsel);
|
||||
clmod(bcut, 'en', nsel);
|
||||
clmod(bpst, 'en', r.clip && r.clip.length);
|
||||
bren.style.display = have_mv && has(perms, 'write') && has(perms, 'move') ? '' : 'none';
|
||||
bdel.style.display = have_del && has(perms, 'delete') ? '' : 'none';
|
||||
@@ -1496,13 +1497,13 @@ var fileman = (function () {
|
||||
|
||||
var vsp = vsplit(src),
|
||||
base = vsp[0],
|
||||
ofn = vsp[1];
|
||||
ofn = uricom_dec(vsp[1])[0];
|
||||
|
||||
var fn = prompt('new filename:', ofn);
|
||||
if (!fn || fn == ofn)
|
||||
return toast.warn(1, 'rename aborted');
|
||||
|
||||
var dst = base + fn;
|
||||
var dst = base + uricom_enc(fn, false);
|
||||
|
||||
function rename_cb() {
|
||||
if (this.readyState != XMLHttpRequest.DONE)
|
||||
@@ -1611,7 +1612,7 @@ var fileman = (function () {
|
||||
links = QSA('#files tbody td:nth-child(2) a');
|
||||
|
||||
for (var a = 0, aa = links.length; a < aa; a++)
|
||||
indir.push(links[a].getAttribute('name'));
|
||||
indir.push(vsplit(links[a].getAttribute('href'))[1]);
|
||||
|
||||
for (var a = 0; a < r.clip.length; a++) {
|
||||
var found = false;
|
||||
@@ -2415,6 +2416,7 @@ var treectl = (function () {
|
||||
prev_atop = null,
|
||||
prev_winh = null,
|
||||
dyn = bcfg_get('dyntree', true),
|
||||
dots = bcfg_get('dotfiles', false),
|
||||
treesz = icfg_get('treesz', 16);
|
||||
|
||||
treesz = Math.min(Math.max(treesz, 4), 50);
|
||||
@@ -2533,7 +2535,7 @@ var treectl = (function () {
|
||||
xhr.dst = dst;
|
||||
xhr.rst = rst;
|
||||
xhr.ts = Date.now();
|
||||
xhr.open('GET', dst + '?tree=' + top, true);
|
||||
xhr.open('GET', dst + '?tree=' + top + (dots ? '&dots' : ''), true);
|
||||
xhr.onreadystatechange = recvtree;
|
||||
xhr.send();
|
||||
enspin('#tree');
|
||||
@@ -2637,7 +2639,7 @@ var treectl = (function () {
|
||||
xhr.top = url;
|
||||
xhr.hpush = hpush;
|
||||
xhr.ts = Date.now();
|
||||
xhr.open('GET', xhr.top + '?ls', true);
|
||||
xhr.open('GET', xhr.top + '?ls' + (dots ? '&dots' : ''), true);
|
||||
xhr.onreadystatechange = recvls;
|
||||
xhr.send();
|
||||
if (hpush)
|
||||
@@ -2774,6 +2776,13 @@ var treectl = (function () {
|
||||
return ret;
|
||||
}
|
||||
|
||||
function tdots(e) {
|
||||
ev(e);
|
||||
dots = !dots;
|
||||
bcfg_set('dotfiles', dots);
|
||||
treectl.goto(get_evpath());
|
||||
}
|
||||
|
||||
function dyntree(e) {
|
||||
ev(e);
|
||||
dyn = !dyn;
|
||||
@@ -2793,6 +2802,7 @@ var treectl = (function () {
|
||||
|
||||
ebi('entree').onclick = treectl.entree;
|
||||
ebi('detree').onclick = treectl.detree;
|
||||
ebi('dotfiles').onclick = tdots;
|
||||
ebi('dyntree').onclick = dyntree;
|
||||
ebi('twig').onclick = scaletree;
|
||||
ebi('twobytwo').onclick = scaletree;
|
||||
@@ -2839,7 +2849,7 @@ function apply_perms(newperms) {
|
||||
|
||||
var axs = [],
|
||||
aclass = '>',
|
||||
chk = ['read', 'write', 'rename', 'delete'];
|
||||
chk = ['read', 'write', 'move', 'delete'];
|
||||
|
||||
for (var a = 0; a < chk.length; a++)
|
||||
if (has(perms, chk[a]))
|
||||
@@ -3319,13 +3329,11 @@ var msel = (function () {
|
||||
item.id = links[a].getAttribute('id');
|
||||
item.sel = links[a].closest('tr').classList.contains('sel');
|
||||
item.vp = href.indexOf('/') !== -1 ? href : vbase + href;
|
||||
item.name = href.split('/').slice(-1);
|
||||
|
||||
r.all.push(item);
|
||||
if (item.sel)
|
||||
r.sel.push(item);
|
||||
|
||||
links[a].setAttribute('name', item.name);
|
||||
links[a].closest('tr').setAttribute('tabindex', '0');
|
||||
}
|
||||
};
|
||||
@@ -3365,10 +3373,15 @@ var msel = (function () {
|
||||
};
|
||||
ebi('selzip').onclick = function (e) {
|
||||
ev(e);
|
||||
var names = r.getsel(),
|
||||
var sel = r.getsel(),
|
||||
arg = ebi('selzip').getAttribute('fmt'),
|
||||
txt = names.join('\n'),
|
||||
frm = mknod('form');
|
||||
frm = mknod('form'),
|
||||
txt = [];
|
||||
|
||||
for (var a = 0; a < sel.length; a++)
|
||||
txt.push(vsplit(sel[a].vp)[1]);
|
||||
|
||||
txt = txt.join('\n');
|
||||
|
||||
frm.setAttribute('action', '?' + arg);
|
||||
frm.setAttribute('method', 'post');
|
||||
|
@@ -44,7 +44,7 @@ avg() { awk 'function pr(ncsz) {if (nsmp>0) {printf "%3s %s\n", csz, sum/nsmp} c
|
||||
dirs=("$HOME/vfs/ほげ" "$HOME/vfs/ほげ/ぴよ" "$HOME/vfs/$(printf \\xed\\x91)" "$HOME/vfs/$(printf \\xed\\x91/\\xed\\x92)")
|
||||
mkdir -p "${dirs[@]}"
|
||||
for dir in "${dirs[@]}"; do for fn in ふが "$(printf \\xed\\x93)" 'qwe,rty;asd fgh+jkl%zxc&vbn <qwe>"rty'"'"'uio&asd fgh'; do echo "$dir" > "$dir/$fn.html"; done; done
|
||||
|
||||
# qw er+ty%20ui%%20op<as>df&gh&jk#zx'cv"bn`m=qw*er^ty?ui@op,as.df-gh_jk
|
||||
|
||||
##
|
||||
## upload mojibake
|
||||
|
@@ -34,6 +34,7 @@ gtar=$(command -v gtar || command -v gnutar) || true
|
||||
sed() { gsed "$@"; }
|
||||
find() { gfind "$@"; }
|
||||
sort() { gsort "$@"; }
|
||||
sha1sum() { shasum "$@"; }
|
||||
unexpand() { gunexpand "$@"; }
|
||||
command -v grealpath >/dev/null &&
|
||||
realpath() { grealpath "$@"; }
|
||||
@@ -81,16 +82,23 @@ tmv() {
|
||||
mv t "$1"
|
||||
}
|
||||
|
||||
stamp=$(
|
||||
for d in copyparty scripts; do
|
||||
find $d -type f -printf '%TY-%Tm-%Td %TH:%TM:%TS %p\n'
|
||||
done | sort | tail -n 1 | sha1sum | cut -c-16
|
||||
)
|
||||
|
||||
rm -rf sfx/*
|
||||
mkdir -p sfx build
|
||||
cd sfx
|
||||
|
||||
[ $repack ] && {
|
||||
old="$(
|
||||
printf '%s\n' "$TMPDIR" /tmp |
|
||||
awk '/./ {print; exit}'
|
||||
)/pe-copyparty"
|
||||
tmpdir="$(
|
||||
printf '%s\n' "$TMPDIR" /tmp |
|
||||
awk '/./ {print; exit}'
|
||||
)"
|
||||
|
||||
[ $repack ] && {
|
||||
old="$tmpdir/pe-copyparty"
|
||||
echo "repack of files in $old"
|
||||
cp -pR "$old/"*{dep-j2,copyparty} .
|
||||
}
|
||||
@@ -172,12 +180,12 @@ mkdir -p ../dist
|
||||
sfx_out=../dist/copyparty-sfx
|
||||
|
||||
echo cleanup
|
||||
find .. -name '*.pyc' -delete
|
||||
find .. -name __pycache__ -delete
|
||||
find -name '*.pyc' -delete
|
||||
find -name __pycache__ -delete
|
||||
|
||||
# especially prevent osx from leaking your lan ip (wtf apple)
|
||||
find .. -type f \( -name .DS_Store -or -name ._.DS_Store \) -delete
|
||||
find .. -type f -name ._\* | while IFS= read -r f; do cmp <(printf '\x00\x05\x16') <(head -c 3 -- "$f") && rm -f -- "$f"; done
|
||||
find -type f \( -name .DS_Store -or -name ._.DS_Store \) -delete
|
||||
find -type f -name ._\* | while IFS= read -r f; do cmp <(printf '\x00\x05\x16') <(head -c 3 -- "$f") && rm -f -- "$f"; done
|
||||
|
||||
echo use smol web deps
|
||||
rm -f copyparty/web/deps/*.full.* copyparty/web/dbg-* copyparty/web/Makefile
|
||||
@@ -241,20 +249,42 @@ find | grep -E '\.(js|html)$' | while IFS= read -r f; do
|
||||
tmv "$f"
|
||||
done
|
||||
|
||||
|
||||
gzres() {
|
||||
command -v pigz &&
|
||||
pk='pigz -11 -J 34 -I 100' ||
|
||||
pk='gzip'
|
||||
command -v pigz &&
|
||||
pk='pigz -11 -J 34 -I 256' ||
|
||||
pk='gzip'
|
||||
|
||||
echo "$pk"
|
||||
find | grep -E '\.(js|css)$' | grep -vF /deps/ | while IFS= read -r f; do
|
||||
echo -n .
|
||||
$pk "$f"
|
||||
done
|
||||
echo
|
||||
echo "$pk"
|
||||
find | grep -E '\.(js|css)$' | grep -vF /deps/ | while IFS= read -r f; do
|
||||
echo -n .
|
||||
$pk "$f"
|
||||
done
|
||||
echo
|
||||
}
|
||||
|
||||
|
||||
zdir="$tmpdir/cpp-mksfx"
|
||||
[ -e "$zdir/$stamp" ] || rm -rf "$zdir"
|
||||
mkdir -p "$zdir"
|
||||
echo a > "$zdir/$stamp"
|
||||
nf=$(ls -1 "$zdir"/arc.* | wc -l)
|
||||
[ $nf -ge 10 ] && [ ! $repack ] && use_zdir=1 || use_zdir=
|
||||
|
||||
[ $use_zdir ] || {
|
||||
echo "$nf alts += 1"
|
||||
gzres
|
||||
[ $repack ] ||
|
||||
tar -cf "$zdir/arc.$(date +%s)" copyparty/web/*.gz
|
||||
}
|
||||
[ $use_zdir ] && {
|
||||
arcs=("$zdir"/arc.*)
|
||||
arc="${arcs[$RANDOM % ${#arcs[@]} ] }"
|
||||
echo "using $arc"
|
||||
tar -xf "$arc"
|
||||
for f in copyparty/web/*.gz; do
|
||||
rm "${f%.*}"
|
||||
done
|
||||
}
|
||||
gzres
|
||||
|
||||
|
||||
echo gen tarlist
|
||||
|
@@ -65,9 +65,9 @@ def uncomment(fpath):
|
||||
|
||||
|
||||
def main():
|
||||
print("uncommenting", end="")
|
||||
print("uncommenting", end="", flush=True)
|
||||
for f in sys.argv[1:]:
|
||||
print(".", end="")
|
||||
print(".", end="", flush=True)
|
||||
uncomment(f)
|
||||
|
||||
print("k")
|
||||
|
Reference in New Issue
Block a user