From 9e850fc3ab62abc3f6af329bb5dc4d353fa1f5b2 Mon Sep 17 00:00:00 2001 From: ed Date: Sat, 27 Mar 2021 15:48:52 +0100 Subject: [PATCH] zip selection --- copyparty/authsrv.py | 62 ++++++++++++++++-------------------- copyparty/httpcli.py | 25 +++++++++++++-- copyparty/httpconn.py | 4 ++- copyparty/web/browser.css | 24 ++++++++++++++ copyparty/web/browser.html | 9 +++++- copyparty/web/browser.js | 65 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 150 insertions(+), 39 deletions(-) diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index ea8b0658..8cf30503 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -161,47 +161,39 @@ class VFS(object): for x in vfs.walk(wrel, "", uname, scandir, lstat): yield x - def zipgen(self, vrem, rems, uname, dots, scandir): - vtops = [["", [self, vrem]]] - if rems: - # list of subfolders to zip was provided, - # add all the ones uname is allowed to access - vtops = [] - for rem in rems: - try: - d = rem if not vrem else vrem + "/" + rem - vn = self.get(d, uname, True, False) - vtops.append([rem, vn]) - except: - pass + def zipgen(self, vrem, flt, uname, dots, scandir): + if flt: + flt = {k: True for k in flt} - for rel, (vn, rem) in vtops: - for vpath, apath, files, rd, vd in vn.walk(rel, rem, uname, dots, scandir): - # print(repr([vpath, apath, [x[0] for x in files]])) - fnames = [n[0] for n in files] - vpaths = [vpath + "/" + n for n in fnames] if vpath else fnames - apaths = [os.path.join(apath, n) for n in fnames] - files = list(zip(vpaths, apaths, files)) + for vpath, apath, files, rd, vd in self.walk("", vrem, uname, dots, scandir): + if flt: + files = [x for x in files if x[0] in flt] + rd = [x for x in rd if x[0] in flt] + vd = {x: y for x, y in vd.items() if x in flt} - if not dots: - # dotfile filtering based on vpath (intended visibility) - files = [x for x in files if "/." not in "/" + x[0]] + # print(repr([vpath, apath, [x[0] for x in files]])) + fnames = [n[0] for n in files] + vpaths = [vpath + "/" + n for n in fnames] if vpath else fnames + apaths = [os.path.join(apath, n) for n in fnames] + files = list(zip(vpaths, apaths, files)) - rm = [x for x in rd if x[0].startswith(".")] - for x in rm: - rd.remove(x) + if not dots: + # dotfile filtering based on vpath (intended visibility) + files = [x for x in files if "/." not in "/" + x[0]] - rm = [k for k in vd.keys() if k.startswith(".")] - for x in rm: - del vd[x] + rm = [x for x in rd if x[0].startswith(".")] + for x in rm: + rd.remove(x) - # up2k filetring based on actual abspath - files = [ - x for x in files if "{0}.hist{0}up2k.".format(os.sep) not in x[1] - ] + rm = [k for k in vd.keys() if k.startswith(".")] + for x in rm: + del vd[x] - for f in [{"vp": v, "ap": a, "st": n[1]} for v, a, n in files]: - yield f + # up2k filetring based on actual abspath + files = [x for x in files if "{0}.hist{0}up2k.".format(os.sep) not in x[1]] + + for f in [{"vp": v, "ap": a, "st": n[1]} for v, a, n in files]: + yield f def user_tree(self, uname, readable=False, writable=False): ret = [] diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index b20b98a1..52bb9061 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -161,8 +161,8 @@ class HttpCli(object): try: # self.log("pebkac at httpcli.run #2: " + repr(ex)) self.keepalive = self._check_nonfatal(ex) - self.log("{}\033[0m: {}".format(str(ex), self.vpath), 3) - msg = "
{}: {}\r\n".format(str(ex), self.vpath)
+                self.log("{}\033[0m, {}".format(str(ex), self.vpath), 3)
+                msg = "
{}\r\nURL: {}\r\n".format(str(ex), self.vpath)
                 self.reply(msg.encode("utf-8", "replace"), status=ex.code)
                 return self.keepalive
             except Pebkac:
@@ -397,8 +397,29 @@ class HttpCli(object):
         if act == "tput":
             return self.handle_text_upload()
 
+        if act == "zip":
+            return self.handle_zip_post()
+
         raise Pebkac(422, 'invalid action "{}"'.format(act))
 
+    def handle_zip_post(self):
+        for k in ["zip", "tar"]:
+            v = self.uparam.get(k)
+            if v is not None:
+                break
+
+        if not v:
+            raise Pebkac(422, "need zip or tar keyword")
+
+        vn, rem = self.auth.vfs.get(self.vpath, self.uname, True, False)
+        items = self.parser.require("files", 1024 * 1024)
+        if not items:
+            raise Pebkac(422, "need files list")
+
+        items = items.replace("\r", "").split("\n")
+        items = [x for x in items if items]
+        return self.tx_zip(k, v, vn, rem, items, self.args.ed)
+
     def handle_post_json(self):
         try:
             remains = int(self.headers["content-length"])
diff --git a/copyparty/httpconn.py b/copyparty/httpconn.py
index 71ef3728..5d0c0348 100644
--- a/copyparty/httpconn.py
+++ b/copyparty/httpconn.py
@@ -87,7 +87,9 @@ class HttpConn(object):
                 err = "need at least 4 bytes in the first packet; got {}".format(
                     len(method)
                 )
-                self.log(err)
+                if method:
+                    self.log(err)
+
                 self.s.send(b"HTTP/1.1 400 Bad Request\r\n\r\n" + err.encode("utf-8"))
                 return
 
diff --git a/copyparty/web/browser.css b/copyparty/web/browser.css
index 32bf0d37..da39dfdb 100644
--- a/copyparty/web/browser.css
+++ b/copyparty/web/browser.css
@@ -182,6 +182,11 @@ a, #files tbody div a:last-child {
 	color: #840;
 	text-shadow: 0 0 .3em #b80;
 }
+#files tbody tr.sel td {
+	background: #80b;
+	color: #fff;
+	border-color: #a3d;
+}
 #blocked {
 	position: fixed;
 	top: 0;
@@ -268,6 +273,25 @@ a, #files tbody div a:last-child {
 	padding: .2em 0 0 .07em;
 	color: #fff;
 }
+#wtoggle>span {
+	display: none;
+}
+#wtoggle.sel {
+	width: 4.27em;
+}
+#wtoggle.sel>span {
+	display: inline-block;
+	line-height: 0;
+}
+#wtoggle.sel>span a {
+	font-size: .4em;
+	margin: -.3em 0;
+	position: relative;
+	display: inline-block;
+}
+#wtoggle.sel>span #selzip {
+	top: -.6em;
+}
 #barpos,
 #barbuf {
 	position: absolute;
diff --git a/copyparty/web/browser.html b/copyparty/web/browser.html
index d751532e..442f7ae0 100644
--- a/copyparty/web/browser.html
+++ b/copyparty/web/browser.html
@@ -112,7 +112,14 @@
     {%- endif %}
 
     
-
+
+ + sel.
all
+ sel.
inv.
+ zip +
+ ♫ +
diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index d30af7f2..4d5b1319 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -1595,6 +1595,8 @@ var arcfmt = (function () { o.setAttribute("href", href.slice(0, ofs + 1) + arg); o.textContent = fmt.split('_')[0]; } + ebi('selzip').textContent = fmt.split('_')[0]; + ebi('selzip').setAttribute('fmt', arg); } function try_render() { @@ -1624,6 +1626,69 @@ var arcfmt = (function () { })(); +var msel = (function () { + function getsel() { + var names = []; + var links = document.querySelectorAll('#files tbody tr.sel td:nth-child(2) a'); + for (var a = 0, aa = links.length; a < aa; a++) + names.push(links[a].getAttribute('href')); + + return names; + } + function selui() { + var fun = getsel().length ? "add" : "remove"; + ebi('wtoggle').classList[fun]('sel'); + } + function seltgl(e) { + ev(e); + var tr = this.parentNode; + tr.classList.toggle('sel'); + selui(); + } + var trs = document.querySelectorAll('#files tbody tr'); + ebi('selall').onclick = function (e) { + ev(e); + for (var a = 0, aa = trs.length; a < aa; a++) + trs[a].classList.add('sel'); + selui(); + }; + ebi('selinv').onclick = function (e) { + ev(e); + for (var a = 0, aa = trs.length; a < aa; a++) + trs[a].classList.toggle('sel'); + selui(); + }; + ebi('selzip').onclick = function (e) { + ev(e); + var names = getsel(); + var arg = ebi('selzip').getAttribute('fmt'); + var txt = names.join('\n'); + var frm = document.createElement('form'); + frm.setAttribute('action', '?' + arg); + frm.setAttribute('method', 'post'); + frm.setAttribute('target', '_blank'); + frm.setAttribute('enctype', 'multipart/form-data'); + frm.innerHTML = '' + + ''; + frm.style.display = 'none'; + + var oldform = document.querySelector('#widgeti>form'); + if (oldform) + oldform.parentNode.removeChild(oldform); + + ebi('widgeti').appendChild(frm); + var obj = ebi('ziptxt'); + obj.value = txt; + console.log(txt); + frm.submit(); + }; + var tds = document.querySelectorAll('#files tbody td+td+td'); + for (var a = 0, aa = tds.length; a < aa; a++) { + tds[a].onclick = seltgl; + } +})(); + + function ev_row_tgl(e) { ev(e); filecols.toggle(this.parentElement.parentElement.getElementsByTagName('span')[0].textContent);