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 %}
- ♫
+
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);