diff --git a/copyparty/__main__.py b/copyparty/__main__.py
index ddfdd8a2..86d8cdd5 100644
--- a/copyparty/__main__.py
+++ b/copyparty/__main__.py
@@ -1319,6 +1319,7 @@ def add_optouts(ap):
ap2.add_argument("--no-del", action="store_true", help="disable delete operations")
ap2.add_argument("--no-mv", action="store_true", help="disable move/rename operations")
ap2.add_argument("--no-cp", action="store_true", help="disable copy operations")
+ ap2.add_argument("--no-fs-abrt", action="store_true", help="disable ability to abort ongoing copy/move")
ap2.add_argument("-nth", action="store_true", help="no title hostname; don't show \033[33m--name\033[0m in
")
ap2.add_argument("-nih", action="store_true", help="no info hostname -- don't show in UI")
ap2.add_argument("-nid", action="store_true", help="no info disk-usage -- don't show in UI")
diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py
index 72bff45e..a7ff303d 100644
--- a/copyparty/httpcli.py
+++ b/copyparty/httpcli.py
@@ -1987,6 +1987,9 @@ class HttpCli(object):
if "eshare" in self.uparam:
return self.handle_eshare()
+ if "fs_abrt" in self.uparam:
+ return self.handle_fs_abrt()
+
if "application/octet-stream" in ctype:
return self.handle_post_binary()
@@ -5958,7 +5961,9 @@ class HttpCli(object):
self.asrv.vfs.get(vdst, self.uname, False, True, False, True)
wunlink(self.log, dabs, dvn.flags)
- x = self.conn.hsrv.broker.ask("up2k.handle_mv", self.uname, self.ip, vsrc, vdst)
+ x = self.conn.hsrv.broker.ask(
+ "up2k.handle_mv", self.ouparam.get("akey"), self.uname, self.ip, vsrc, vdst
+ )
self.loud_reply(x.get(), status=201)
return True
@@ -5988,10 +5993,21 @@ class HttpCli(object):
self.asrv.vfs.get(vdst, self.uname, False, True, False, True)
wunlink(self.log, dabs, dvn.flags)
- x = self.conn.hsrv.broker.ask("up2k.handle_cp", self.uname, self.ip, vsrc, vdst)
+ x = self.conn.hsrv.broker.ask(
+ "up2k.handle_cp", self.ouparam.get("akey"), self.uname, self.ip, vsrc, vdst
+ )
self.loud_reply(x.get(), status=201)
return True
+ def handle_fs_abrt(self):
+ if self.args.no_fs_abrt:
+ t = "aborting an ongoing copy/move is disabled in server config"
+ raise Pebkac(403, t)
+
+ self.conn.hsrv.broker.say("up2k.handle_fs_abrt", self.uparam["fs_abrt"])
+ self.loud_reply("aborting", status=200)
+ return True
+
def tx_ls(self, ls: dict[str, Any]) -> bool:
dirs = ls["dirs"]
files = ls["files"]
diff --git a/copyparty/svchub.py b/copyparty/svchub.py
index a6f87a85..950122d0 100644
--- a/copyparty/svchub.py
+++ b/copyparty/svchub.py
@@ -39,9 +39,9 @@ from .th_srv import (
HAVE_FFPROBE,
HAVE_HEIF,
HAVE_PIL,
+ HAVE_RAW,
HAVE_VIPS,
HAVE_WEBP,
- HAVE_RAW,
ThumbSrv,
)
from .up2k import Up2k
diff --git a/copyparty/up2k.py b/copyparty/up2k.py
index b233cecb..993d5b82 100644
--- a/copyparty/up2k.py
+++ b/copyparty/up2k.py
@@ -144,6 +144,7 @@ class Up2k(object):
self.salt = self.args.warksalt
self.r_hash = re.compile("^[0-9a-zA-Z_-]{44}$")
+ self.abrt_key = ""
self.gid = 0
self.gt0 = 0
@@ -3988,6 +3989,9 @@ class Up2k(object):
except:
pass
+ def handle_fs_abrt(self, akey: str) -> None:
+ self.abrt_key = akey
+
def handle_rm(
self,
uname: str,
@@ -4197,7 +4201,7 @@ class Up2k(object):
return n_files, ok + ok2, ng + ng2
- def handle_cp(self, uname: str, ip: str, svp: str, dvp: str) -> str:
+ def handle_cp(self, abrt: str, uname: str, ip: str, svp: str, dvp: str) -> str:
if svp == dvp or dvp.startswith(svp + "/"):
raise Pebkac(400, "cp: cannot copy parent into subfolder")
@@ -4244,6 +4248,8 @@ class Up2k(object):
dvpf = dvp + svpf[len(svp) :]
self._cp_file(uname, ip, svpf, dvpf, curs)
+ if abrt and abrt == self.abrt_key:
+ raise Pebkac(400, "filecopy aborted by http-api")
for v in curs:
v.connection.commit()
@@ -4411,7 +4417,7 @@ class Up2k(object):
return "k"
- def handle_mv(self, uname: str, ip: str, svp: str, dvp: str) -> str:
+ def handle_mv(self, abrt: str, uname: str, ip: str, svp: str, dvp: str) -> str:
if svp == dvp or dvp.startswith(svp + "/"):
raise Pebkac(400, "mv: cannot move parent into subfolder")
@@ -4466,6 +4472,8 @@ class Up2k(object):
dvpf = dvp + svpf[len(svp) :]
self._mv_file(uname, ip, svpf, dvpf, curs)
+ if abrt and abrt == self.abrt_key:
+ raise Pebkac(400, "filemove aborted by http-api")
for v in curs:
v.connection.commit()
diff --git a/copyparty/web/browser.css b/copyparty/web/browser.css
index f2dc8cde..00e42cae 100644
--- a/copyparty/web/browser.css
+++ b/copyparty/web/browser.css
@@ -1930,6 +1930,11 @@ html.y #tree.nowrap .ntree a+a:hover {
padding: 0;
font-size: 1.5em;
}
+#fs_abrt {
+ margin-top: 1em;
+ text-shadow: 0;
+ box-shadow: 1px 1px 0 var(--bg-d3);
+}
#doc {
overflow: visible;
background: #fff;
diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js
index a0a1a21c..112e7da0 100644
--- a/copyparty/web/browser.js
+++ b/copyparty/web/browser.js
@@ -427,6 +427,7 @@ var Ls = {
"fcp_ok": "copy OK",
"fp_busy": "moving {0} items...\n\n{1}",
"fcp_busy": "copying {0} items...\n\n{1}",
+ "fp_abrt": "aborting...",
"fp_err": "move failed:\n",
"fcp_err": "copy failed:\n",
"fp_confirm": "move these {0} items here?",
@@ -1057,6 +1058,7 @@ var Ls = {
"fcp_ok": "kopiering OK",
"fp_busy": "flytter {0} filer...\n\n{1}",
"fcp_busy": "kopierer {0} filer...\n\n{1}",
+ "fp_abrt": "avbryter...",
"fp_err": "flytting feilet:\n",
"fcp_err": "kopiering feilet:\n",
"fp_confirm": "flytt disse {0} filene hit?",
@@ -1686,6 +1688,7 @@ var Ls = {
"fcp_ok": "复制成功", //m
"fp_busy": "正在移动 {0} 项...\n\n{1}",
"fcp_busy": "正在复制 {0} 项...\n\n{1}", //m
+ "fp_abrt": "正在中止...", //m
"fp_err": "移动失败:\n",
"fcp_err": "复制失败:\n", //m
"fp_confirm": "将这些 {0} 项移动到这里?",
@@ -2319,6 +2322,7 @@ var Ls = {
"fcp_ok": "kopírování OK",
"fp_busy": "přesouvám {0} položek...\n\n{1}",
"fcp_busy": "kopíruji {0} položek...\n\n{1}",
+ "fp_abrt": "přerušuji...", //m
"fp_err": "přesun selhal:\n",
"fcp_err": "kopírování selhalo:\n",
"fp_confirm": "přesunout těchto {0} položek sem?",
@@ -2948,6 +2952,7 @@ var Ls = {
"fcp_ok": "Kopieren OK",
"fp_busy": "Verschiebe {0} Elemente...\n\n{1}",
"fcp_busy": "Kopiere {0} Elemente...\n\n{1}",
+ "fp_abrt": "Abbrechen...", //m
"fp_err": "Verschieben fehlgeschlagen:\n",
"fcp_err": "Kopieren fehlgeschlagen:\n",
"fp_confirm": "Diese {0} Elemente hierher verschieben?",
@@ -3577,6 +3582,7 @@ var Ls = {
"fcp_ok": "kopiointi OK",
"fp_busy": "siirretään {0} kohdetta...\n\n{1}",
"fcp_busy": "kopioidaan {0} kohdetta...\n\n{1}",
+ "fp_abrt": "keskeytetään...", //m
"fp_err": "siirto epäonnistui:\n",
"fcp_err": "kopiointi epäonnistui:\n",
"fp_confirm": "siirrä nämä {0} kohdetta tänne?",
@@ -4206,6 +4212,7 @@ var Ls = {
"fcp_ok": "copie OK",
"fp_busy": "déplacement de {0} éléments…\n\n{1}",
"fcp_busy": "copie de {0} éléments…\n\n{1}",
+ "fp_abrt": "abandon en cours...", //m
"fp_err": "deplacement échoué:\n",
"fcp_err": "copie échouée:\n",
"fp_confirm": "déplacer ces {0} éléments ici ?",
@@ -4835,6 +4842,7 @@ var Ls = {
"fcp_ok": "αντιγραφή OK",
"fp_busy": "μετακίνηση {0} αντικειμένων...\n\n{1}",
"fcp_busy": "αντιγραφή {0} αντικειμένων...\n\n{1}",
+ "fp_abrt": "γίνεται ακύρωση...", //m
"fp_err": "αποτυχία μετακίνησης:\n",
"fcp_err": "αποτυχία αντιγραφής:\n",
"fp_confirm": "να μετακινηθούν αυτά τα {0} αντικείμενα εδώ;",
@@ -5464,6 +5472,7 @@ var Ls = {
"fcp_ok": "copia OK",
"fp_busy": "spostando {0} elementi...\n\n{1}",
"fcp_busy": "copiando {0} elementi...\n\n{1}",
+ "fp_abrt": "annullamento in corso...", //m
"fp_err": "spostamento fallito:\n",
"fcp_err": "copia fallita:\n",
"fp_confirm": "spostare questi {0} elementi qui?",
@@ -6093,6 +6102,7 @@ var Ls = {
"fcp_ok": "Kopiëren OK",
"fp_busy": "{0} items verplaatsen...\n\n{1}",
"fcp_busy": "{0} items kopiëren...\n\n{1}",
+ "fp_abrt": "afbreken...", //m
"fp_err": "Verplaatsen mislukt:\n",
"fcp_err": "Kopieëren mislukt:\n",
"fp_confirm": "Verplaats deze {0} items hierheen?",
@@ -6723,6 +6733,7 @@ var Ls = {
"fcp_ok": "kopiering OK",
"fp_busy": "flyttar {0} filer...\n\n{1}",
"fcp_busy": "kopierar {0} filer...\n\n{1}",
+ "fp_abrt": "avbryt...",
"fp_err": "flytting feila:\n",
"fcp_err": "kopiering feila:\n",
"fp_confirm": "flytt disse {0} filene hit?",
@@ -7349,6 +7360,7 @@ var Ls = {
"fcp_ok": "przekopiowano",
"fp_busy": "przenoszenie {0} elementów...\n\n{1}",
"fcp_busy": "kopiowanie {0} elementów...\n\n{1}",
+ "fp_abrt": "przerywanie...", //m
"fp_err": "nie udało się przenieść:\n",
"fcp_err": "nie udało się skopiować:\n",
"fp_confirm": "przenieść tutaj {0} elementy(ów)?",
@@ -7978,6 +7990,7 @@ var Ls = {
"fcp_ok": "успешно скопировано",
"fp_busy": "перемещаю {0} файлов...\n\n{1}",
"fcp_busy": "копирую {0} файлов...\n\n{1}",
+ "fp_abrt": "прерывание...", //m
"fp_err": "ошибка перемещения:\n",
"fcp_err": "ошибка копирования:\n",
"fp_confirm": "переместить эти {0} файлов сюда?",
@@ -8606,6 +8619,7 @@ var Ls = {
"fcp_ok": "copia correcta",
"fp_busy": "moviendo {0} elementos...\n\n{1}",
"fcp_busy": "copiando {0} elementos...\n\n{1}",
+ "fp_abrt": "cancelando...", //m
"fp_err": "fallo al mover:\n",
"fcp_err": "fallo al copiar:\n",
"fp_confirm": "¿mover estos {0} elementos aquí?",
@@ -9235,6 +9249,7 @@ var Ls = {
"fcp_ok": "копіювання OK",
"fp_busy": "переміщення {0} елементів...\n\n{1}",
"fcp_busy": "копіювання {0} елементів...\n\n{1}",
+ "fp_abrt": "переривання...", //m
"fp_err": "переміщення невдале:\n",
"fcp_err": "копіювання невдале:\n",
"fp_confirm": "перемістити ці {0} елементи сюди?",
@@ -9882,6 +9897,7 @@ var ACtx = !IPHONE && (window.AudioContext || window.webkitAudioContext),
hash0 = location.hash,
sloc0 = '' + location,
noih = /[?&]v\b/.exec(sloc0),
+ abrt_key = "",
rtt = null,
ldks = [],
dks = {},
@@ -12414,6 +12430,15 @@ function fmt_ren(re, md, fmt) {
}
+function fs_abrt() {
+ toast.inf(30, L.fp_abrt);
+ fileman.f.length = 0;
+ var xhr = new XHR();
+ xhr.open('POST', '/?fs_abrt=' + abrt_key, true);
+ xhr.send();
+}
+
+
var fileman = (function () {
var bren = ebi('fren'),
bdel = ebi('fdel'),
@@ -12424,6 +12449,7 @@ var fileman = (function () {
t_paste,
r = {};
+ r.f = [];
r.clip = null;
try {
r.bus = new BroadcastChannel("fileman_bus");
@@ -12711,6 +12737,8 @@ var fileman = (function () {
base = vsplit(sel[0].vp)[0],
mkeys;
+ r.f = f;
+
for (var a = 0; a < sel.length; a++) {
var vp = sel[a].vp;
if (vp.endsWith('/'))
@@ -12990,7 +13018,9 @@ var fileman = (function () {
return rn_cancel();
}
- toast.show('inf r', 0, esc(L.fr_busy.format(f.length, f[0].ofn)));
+ var msg = esc(L.fr_busy.format(f.length, f[0].ofn));
+ msg += '\n' + L.fs_abrt + '';
+ toast.show('inf r', 0, msg);
var dst = base + uricom_enc(f[0].inew.value, false);
function rename_cb() {
@@ -13004,8 +13034,10 @@ var fileman = (function () {
return rn_apply_loop();
}
+ abrt_key = randstr(9);
+
var xhr = new XHR();
- xhr.open('POST', f[0].src + '?move=' + dst, true);
+ xhr.open('POST', f[0].src + '?move=' + dst + '&akey=' + abrt_key, true);
xhr.onload = xhr.onerror = rename_cb;
xhr.send();
}
@@ -13267,6 +13299,8 @@ var fileman = (function () {
srcdir = vsplit(r.clip[0])[0],
links = QSA('#files tbody td:nth-child(2) a');
+ r.f = f;
+
for (var a = 0, aa = links.length; a < aa; a++)
indir.push(uricom_dec(vsplit(noq_href(links[a]))[1]));
@@ -13298,13 +13332,17 @@ var fileman = (function () {
if (!t.dst)
return paster();
- toast.show('inf r', 0, esc((r.ccp ? L.fcp_busy : L.fp_busy).format(f.length + 1, uricom_dec(t.src))));
+ var msg = esc((r.ccp ? L.fcp_busy : L.fp_busy).format(f.length + 1, uricom_dec(t.src)));
+ msg += '\n' + L.fs_abrt + '';
+ toast.show('inf r', 0, msg);
var xhr = new XHR(),
act = r.ccp ? '?copy=' : '?move=',
dst = get_evpath() + uricom_enc(t.dst);
- xhr.open('POST', t.src + act + dst, true);
+ abrt_key = randstr(9);
+
+ xhr.open('POST', t.src + act + dst + '&akey=' + abrt_key, true);
xhr.onload = xhr.onerror = paste_cb;
xhr.send();
}
diff --git a/tests/util.py b/tests/util.py
index f02f81ba..bbd0b215 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -143,7 +143,7 @@ class Cfg(Namespace):
def __init__(self, a=None, v=None, c=None, **ka0):
ka = {}
- ex = "allow_flac allow_wav chpw cookie_lax daw dav_auth dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid gsel hardlink hardlink_only ih ihead localtime magic nid nih no_acode no_athumb no_bauth no_clone no_cp no_dav no_db_ip no_del no_dirsz no_dupe no_fnugg no_lifetime no_logues no_mv no_pipe no_poll no_readme no_robots no_sb_md no_sb_lg no_scandir no_tail no_tarcmp no_thumb no_vthumb no_zip nrand nsort nw og og_no_head og_s_title ohead q rand re_dirsz reflink rmagic rss smb srch_dbg srch_excl stats uqe usernames vague_403 vc ver wo_up_readme write_uplog xdev xlink xvol zipmaxu zs"
+ ex = "allow_flac allow_wav chpw cookie_lax daw dav_auth dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid gsel hardlink hardlink_only ih ihead localtime magic nid nih no_acode no_athumb no_bauth no_clone no_cp no_dav no_db_ip no_del no_dirsz no_dupe no_fnugg no_lifetime no_logues no_mv no_pipe no_poll no_readme no_robots no_sb_md no_sb_lg no_scandir no_tail no_tarcmp no_thumb no_vthumb no_u2abrt no_zip nrand nsort nw og og_no_head og_s_title ohead q rand re_dirsz reflink rmagic rss smb srch_dbg srch_excl stats uqe usernames vague_403 vc ver wo_up_readme write_uplog xdev xlink xvol zipmaxu zs"
ka.update(**{k: False for k in ex.split()})
ex = "dav_inf dedup dotpart dotsrch hook_v no_dhash no_fastboot no_fpool no_htp no_rescan no_sendfile no_ses no_snap no_up_list no_voldump re_dhash see_dots plain_ip"